diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index b3c4c737b4..8971986b7f 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -299,6 +299,8 @@ namespace OpenRA.Traits void SetVisualPosition(Actor self, WPos pos); } + public interface ITemporaryBlockerInfo : ITraitInfoInterface { } + [RequireExplicitImplementation] public interface ITemporaryBlocker { diff --git a/OpenRA.Game/World.cs b/OpenRA.Game/World.cs index 3165e15e81..da964ae239 100644 --- a/OpenRA.Game/World.cs +++ b/OpenRA.Game/World.cs @@ -149,6 +149,8 @@ namespace OpenRA } } + public bool RulesContainTemporaryBlocker { get; private set; } + internal World(ModData modData, Map map, OrderManager orderManager, WorldType type) { Type = type; @@ -185,6 +187,8 @@ namespace OpenRA MapUid = Map.Uid, MapTitle = Map.Title }; + + RulesContainTemporaryBlocker = map.Rules.Actors.Any(a => a.Value.HasTraitInfo()); } public void AddToMaps(Actor self, IOccupySpace ios) diff --git a/OpenRA.Game/WorldUtils.cs b/OpenRA.Game/WorldUtils.cs index 70d0d5935e..29358088e8 100644 --- a/OpenRA.Game/WorldUtils.cs +++ b/OpenRA.Game/WorldUtils.cs @@ -45,6 +45,9 @@ namespace OpenRA public static bool ContainsTemporaryBlocker(this World world, CPos cell, Actor ignoreActor = null) { + if (!world.RulesContainTemporaryBlocker) + return false; + var temporaryBlockers = world.ActorMap.GetActorsAt(cell); foreach (var temporaryBlocker in temporaryBlockers) { diff --git a/OpenRA.Mods.Cnc/Traits/EnergyWall.cs b/OpenRA.Mods.Cnc/Traits/EnergyWall.cs index a9fc67ae58..8c8db6eb6f 100644 --- a/OpenRA.Mods.Cnc/Traits/EnergyWall.cs +++ b/OpenRA.Mods.Cnc/Traits/EnergyWall.cs @@ -18,7 +18,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.Cnc.Traits { [Desc("Will open and be passable for actors that appear friendly when there are no enemies in range.")] - public class EnergyWallInfo : BuildingInfo, IObservesVariablesInfo, IRulesetLoaded + public class EnergyWallInfo : BuildingInfo, ITemporaryBlockerInfo, IObservesVariablesInfo, IRulesetLoaded { [FieldLoader.Require] [WeaponReference] diff --git a/OpenRA.Mods.Common/Traits/Buildings/Gate.cs b/OpenRA.Mods.Common/Traits/Buildings/Gate.cs index 9455ccb8f7..2c991db1cb 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/Gate.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/Gate.cs @@ -16,7 +16,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { [Desc("Will open and be passable for actors that appear friendly when there are no enemies in range.")] - public class GateInfo : PausableConditionalTraitInfo, IBlocksProjectilesInfo, Requires + public class GateInfo : PausableConditionalTraitInfo, ITemporaryBlockerInfo, IBlocksProjectilesInfo, Requires { public readonly string OpeningSound = null; public readonly string ClosingSound = null; diff --git a/OpenRA.Mods.Common/Traits/World/Locomotor.cs b/OpenRA.Mods.Common/Traits/World/Locomotor.cs index 4ed9a626e5..e6f01f22c0 100644 --- a/OpenRA.Mods.Common/Traits/World/Locomotor.cs +++ b/OpenRA.Mods.Common/Traits/World/Locomotor.cs @@ -268,10 +268,14 @@ namespace OpenRA.Mods.Common.Traits IsMovingInMyDirection(self, otherActor)) return false; - // If there is a temporary blocker in our path, but we can remove it, we are not blocked. - var temporaryBlocker = otherActor.TraitOrDefault(); - if (temporaryBlocker != null && temporaryBlocker.CanRemoveBlockage(otherActor, self)) - return false; + // PERF: Only perform ITemporaryBlocker trait look-up if mod/map rules contain any actors that are temporary blockers + if (self.World.RulesContainTemporaryBlocker) + { + // If there is a temporary blocker in our path, but we can remove it, we are not blocked. + var temporaryBlocker = otherActor.TraitOrDefault(); + if (temporaryBlocker != null && temporaryBlocker.CanRemoveBlockage(otherActor, self)) + return false; + } // If we cannot crush the other actor in our way, we are blocked. if (Crushes == null || Crushes.Count == 0)