From 6414743f8e5d670c90a14195dd7d608776499ddf Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Tue, 18 Aug 2015 21:25:37 +0100 Subject: [PATCH 1/4] Introduce HasCellCondition to avoid HasFlag overhead. --- OpenRA.Mods.Common/Traits/Mobile.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/OpenRA.Mods.Common/Traits/Mobile.cs b/OpenRA.Mods.Common/Traits/Mobile.cs index 038801cd8a..4a299995fa 100644 --- a/OpenRA.Mods.Common/Traits/Mobile.cs +++ b/OpenRA.Mods.Common/Traits/Mobile.cs @@ -29,6 +29,14 @@ namespace OpenRA.Mods.Common.Traits All = TransientActors | BlockedByMovers } + public static class CellConditionsExts + { + public static bool HasCellCondition(this CellConditions c, CellConditions cellCondition) + { + return (c & cellCondition) == cellCondition; + } + } + [Desc("Unit is able to move.")] public class MobileInfo : IMoveInfo, IOccupySpaceInfo, IFacingInfo, UsesInit, UsesInit, UsesInit { @@ -189,9 +197,9 @@ namespace OpenRA.Mods.Common.Traits if (SharesCell && world.ActorMap.HasFreeSubCell(cell)) return true; - if (check.HasFlag(CellConditions.TransientActors)) + if (check.HasCellCondition(CellConditions.TransientActors)) { - var canIgnoreMovingAllies = self != null && !check.HasFlag(CellConditions.BlockedByMovers); + var canIgnoreMovingAllies = self != null && !check.HasCellCondition(CellConditions.BlockedByMovers); var needsCellExclusively = self == null || Crushes == null || !Crushes.Any(); foreach (var a in world.ActorMap.GetUnitsAt(cell)) { @@ -230,9 +238,9 @@ namespace OpenRA.Mods.Common.Traits if (MovementCostForCell(world, cell) == int.MaxValue) return SubCell.Invalid; - if (check.HasFlag(CellConditions.TransientActors)) + if (check.HasCellCondition(CellConditions.TransientActors)) { - var canIgnoreMovingAllies = self != null && !check.HasFlag(CellConditions.BlockedByMovers); + var canIgnoreMovingAllies = self != null && !check.HasCellCondition(CellConditions.BlockedByMovers); var needsCellExclusively = self == null || Crushes == null || !Crushes.Any(); Func checkTransient = a => From ca75b5af30cc7c42e72fd1934c82ab86b597a83a Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Tue, 18 Aug 2015 21:43:22 +0100 Subject: [PATCH 2/4] Factor logic for determining if an actor blocks movement into IsBlockedBy. --- OpenRA.Mods.Common/Traits/Mobile.cs | 77 ++++++++++++----------------- 1 file changed, 32 insertions(+), 45 deletions(-) diff --git a/OpenRA.Mods.Common/Traits/Mobile.cs b/OpenRA.Mods.Common/Traits/Mobile.cs index 4a299995fa..b2f0030907 100644 --- a/OpenRA.Mods.Common/Traits/Mobile.cs +++ b/OpenRA.Mods.Common/Traits/Mobile.cs @@ -198,32 +198,42 @@ namespace OpenRA.Mods.Common.Traits return true; if (check.HasCellCondition(CellConditions.TransientActors)) - { - var canIgnoreMovingAllies = self != null && !check.HasCellCondition(CellConditions.BlockedByMovers); - var needsCellExclusively = self == null || Crushes == null || !Crushes.Any(); - foreach (var a in world.ActorMap.GetUnitsAt(cell)) - { - if (a == ignoreActor) - continue; - - // Neutral/enemy units are blockers. Allied units that are moving are not blockers. - if (canIgnoreMovingAllies && self.Owner.Stances[a.Owner] == Stance.Ally && IsMovingInMyDirection(self, a)) continue; - - // Non-sharable unit can enter a cell with shareable units only if it can crush all of them. - if (needsCellExclusively) + foreach (var otherActor in world.ActorMap.GetUnitsAt(cell)) + if (IsBlockedBy(self, otherActor, ignoreActor, check)) return false; - var crushables = a.TraitsImplementing(); - if (!crushables.Any()) - return false; - foreach (var crushable in crushables) - if (!crushable.CrushableBy(Crushes, self.Owner)) - return false; - } - } return true; } + bool IsBlockedBy(Actor self, Actor otherActor, Actor ignoreActor, CellConditions check) + { + // We are not blocked by the actor we are ignoring. + if (otherActor == ignoreActor) + return false; + + // If the check allows: we are not blocked by allied units moving in our direction. + if (!check.HasCellCondition(CellConditions.BlockedByMovers) && + self != null && + self.Owner.Stances[otherActor.Owner] == Stance.Ally && + IsMovingInMyDirection(self, otherActor)) + return false; + + // If we cannot crush the other actor in our way, we are blocked. + if (self == null || Crushes == null || Crushes.Length == 0) + return true; + + // If the other actor in our way cannot be crushed, we are blocked. + var crushables = otherActor.TraitsImplementing(); + if (!crushables.Any()) + return true; + foreach (var crushable in crushables) + if (!crushable.CrushableBy(Crushes, self.Owner)) + return true; + + // We are not blocked by the other actor. + return false; + } + public bool CanEnterCell(World world, Actor self, CPos cell, out int movementCost, Actor ignoreActor = null, CellConditions check = CellConditions.All) { if ((movementCost = MovementCostForCell(world, cell)) == int.MaxValue) @@ -240,30 +250,7 @@ namespace OpenRA.Mods.Common.Traits if (check.HasCellCondition(CellConditions.TransientActors)) { - var canIgnoreMovingAllies = self != null && !check.HasCellCondition(CellConditions.BlockedByMovers); - var needsCellExclusively = self == null || Crushes == null || !Crushes.Any(); - - Func checkTransient = a => - { - if (a == ignoreActor) - return false; - - // Neutral/enemy units are blockers. Allied units that are moving are not blockers. - if (canIgnoreMovingAllies && self.Owner.Stances[a.Owner] == Stance.Ally && IsMovingInMyDirection(self, a)) - return false; - - // Non-sharable unit can enter a cell with shareable units only if it can crush all of them. - if (needsCellExclusively) - return true; - var crushables = a.TraitsImplementing(); - if (!crushables.Any()) - return true; - foreach (var crushable in crushables) - if (!crushable.CrushableBy(Crushes, self.Owner)) - return true; - - return false; - }; + Func checkTransient = otherActor => IsBlockedBy(self, otherActor, ignoreActor, check); if (!SharesCell) return world.ActorMap.AnyUnitsAt(cell, SubCell.FullCell, checkTransient) ? SubCell.Invalid : SubCell.FullCell; From 7eab7220ff018b18470d199f3377ad609e177136 Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Tue, 18 Aug 2015 21:54:09 +0100 Subject: [PATCH 3/4] In CanMoveFreelyInto, check if transient actor checks are needed at the start. When transient actors checks are not needed, all control flows in the method return true. Therefore, we can return true directly in this case. Checking this condition is cheaper than checking for a free sub-cell, so this allows us a faster exit when we don't need to check for transient actors. --- OpenRA.Mods.Common/Traits/Mobile.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/OpenRA.Mods.Common/Traits/Mobile.cs b/OpenRA.Mods.Common/Traits/Mobile.cs index b2f0030907..507136e3a8 100644 --- a/OpenRA.Mods.Common/Traits/Mobile.cs +++ b/OpenRA.Mods.Common/Traits/Mobile.cs @@ -194,13 +194,15 @@ namespace OpenRA.Mods.Common.Traits // Determines whether the actor is blocked by other Actors public bool CanMoveFreelyInto(World world, Actor self, CPos cell, Actor ignoreActor, CellConditions check) { + if (!check.HasCellCondition(CellConditions.TransientActors)) + return true; + if (SharesCell && world.ActorMap.HasFreeSubCell(cell)) return true; - if (check.HasCellCondition(CellConditions.TransientActors)) - foreach (var otherActor in world.ActorMap.GetUnitsAt(cell)) - if (IsBlockedBy(self, otherActor, ignoreActor, check)) - return false; + foreach (var otherActor in world.ActorMap.GetUnitsAt(cell)) + if (IsBlockedBy(self, otherActor, ignoreActor, check)) + return false; return true; } From 3d94c4b216933bc741c26e15883a837b73cea906 Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Tue, 18 Aug 2015 21:54:56 +0100 Subject: [PATCH 4/4] Minor formatting fix in Mobile.cs. --- OpenRA.Mods.Common/Traits/Mobile.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/OpenRA.Mods.Common/Traits/Mobile.cs b/OpenRA.Mods.Common/Traits/Mobile.cs index 507136e3a8..91351f3697 100644 --- a/OpenRA.Mods.Common/Traits/Mobile.cs +++ b/OpenRA.Mods.Common/Traits/Mobile.cs @@ -618,9 +618,8 @@ namespace OpenRA.Mods.Common.Traits var p = ToCell + new CVec(i, j); if (CanEnterCell(p)) availCells.Add(p); - else - if (p != nudger.Location && p != ToCell) - notStupidCells.Add(p); + else if (p != nudger.Location && p != ToCell) + notStupidCells.Add(p); } var moveTo = availCells.Any() ? availCells.Random(self.World.SharedRandom) : (CPos?)null;