diff --git a/OpenRA.Game/Traits/World/UnitInfluence.cs b/OpenRA.Game/Traits/World/UnitInfluence.cs index 7b7e0890d1..9461fa5fb2 100644 --- a/OpenRA.Game/Traits/World/UnitInfluence.cs +++ b/OpenRA.Game/Traits/World/UnitInfluence.cs @@ -51,6 +51,15 @@ namespace OpenRA.Traits yield return i.actor; } + public IEnumerable GetUnitsAt( int2 a, SubCell sub ) + { + if (!map.IsInMap(a)) yield break; + + for( var i = influence[ a.X, a.Y ] ; i != null ; i = i.next ) + if (!i.actor.Destroyed && (i.subCell == sub || i.subCell == SubCell.FullCell)) + yield return i.actor; + } + public bool HasFreeSubCell(int2 a) { if (!AnyUnitsAt(a)) @@ -59,29 +68,6 @@ namespace OpenRA.Traits return new[]{SubCell.TopLeft, SubCell.TopRight, SubCell.Center, SubCell.BottomLeft, SubCell.BottomRight}.Any(b => !AnyUnitsAt(a,b)); } - - // This is only used inside Move, when we *know* that we can enter the cell. - // If the cell contains something crushable, a naive check of 'is it empty' will fail. - // Infantry can currently only crush crates/mines which take a whole cell, so we will ignore - // the SubCell.FullCell case and hope that someone doesn't introduce subcell units - // crushing other subcell units in the future. - public SubCell GetFreeSubcell(int2 a, SubCell preferred) - { - if (preferred == SubCell.FullCell) - return preferred; - - return new[]{ preferred, SubCell.TopLeft, SubCell.TopRight, SubCell.Center, - SubCell.BottomLeft, SubCell.BottomRight}.First(b => - { - var node = influence[ a.X, a.Y ]; - while (node != null) - { - if (node.subCell == b) return false; - node = node.next; - } - return true; - }); - } public bool AnyUnitsAt(int2 a) { @@ -90,12 +76,10 @@ namespace OpenRA.Traits public bool AnyUnitsAt(int2 a, SubCell sub) { - var node = influence[ a.X, a.Y ]; - while (node != null) - { - if (node.subCell == sub || node.subCell == SubCell.FullCell) return true; - node = node.next; - } + for( var i = influence[ a.X, a.Y ] ; i != null ; i = i.next ) + if (i.subCell == sub || i.subCell == SubCell.FullCell) + return true; + return false; } diff --git a/OpenRA.Mods.RA/Move/Mobile.cs b/OpenRA.Mods.RA/Move/Mobile.cs index fb802d450f..01476b22c1 100755 --- a/OpenRA.Mods.RA/Move/Mobile.cs +++ b/OpenRA.Mods.RA/Move/Mobile.cs @@ -307,7 +307,31 @@ namespace OpenRA.Mods.RA.Move yield return Pair.New(toCell, __toSubCell); } } - + + public SubCell GetDesiredSubcell(int2 a) + { + if (!Info.SharesCell) + return SubCell.FullCell; + + // Prioritise the current subcell + return new[]{ __fromSubCell, SubCell.TopLeft, SubCell.TopRight, SubCell.Center, + SubCell.BottomLeft, SubCell.BottomRight}.First(b => + { + var blockingActors = uim.GetUnitsAt(a,b); + if (blockingActors.Count() > 0) + { + // Non-sharable unit can enter a cell with shareable units only if it can crush all of them + if (Info.Crushes == null) + return false; + + if (blockingActors.Any(c => !(c.HasTrait() && + c.TraitsImplementing().Any(d => d.CrushClasses.Intersect(Info.Crushes).Any())))) + return false; + } + return true; + }); + } + public bool CanEnterCell(int2 p) { return CanEnterCell(p, null, true); diff --git a/OpenRA.Mods.RA/Move/Move.cs b/OpenRA.Mods.RA/Move/Move.cs index 4948bbeec5..a201daac76 100755 --- a/OpenRA.Mods.RA/Move/Move.cs +++ b/OpenRA.Mods.RA/Move/Move.cs @@ -229,7 +229,7 @@ namespace OpenRA.Mods.RA.Move hasWaited = false; path.RemoveAt( path.Count - 1 ); - var subCell = self.World.WorldActor.Trait().GetFreeSubcell(nextCell, mobile.__fromSubCell); + var subCell = mobile.GetDesiredSubcell(nextCell); return Pair.New(nextCell, subCell); }