diff --git a/OpenRA.Game/Actor.cs b/OpenRA.Game/Actor.cs index 8133bde68d..9da4620d35 100644 --- a/OpenRA.Game/Actor.cs +++ b/OpenRA.Game/Actor.cs @@ -72,6 +72,7 @@ namespace OpenRA public IOccupySpace OccupiesSpace { get; } public ITargetable[] Targetables { get; } public IEnumerable EnabledTargetablePositions { get; } + public ICrushable[] Crushables { get; } public bool IsIdle => CurrentActivity == null; public bool IsDead => Disposed || (health != null && health.IsDead); @@ -155,6 +156,7 @@ namespace OpenRA var targetablesList = new List(); var targetablePositionsList = new List(); var syncHashesList = new List(); + var crushablesList = new List(); foreach (var traitInfo in Info.TraitsInConstructOrder()) { @@ -181,6 +183,7 @@ namespace OpenRA { if (trait is ITargetable t) targetablesList.Add(t); } { if (trait is ITargetablePositions t) targetablePositionsList.Add(t); } { if (trait is ISync t) syncHashesList.Add(new SyncHash(t)); } + { if (trait is ICrushable t) crushablesList.Add(t); } } resolveOrders = resolveOrdersList.ToArray(); @@ -195,6 +198,7 @@ namespace OpenRA EnabledTargetablePositions = targetablePositions.Where(Exts.IsTraitEnabled); enabledTargetableWorldPositions = EnabledTargetablePositions.SelectMany(tp => tp.TargetablePositions(this)); SyncHashes = syncHashesList.ToArray(); + Crushables = crushablesList.ToArray(); } } diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index 841ab36bd1..ffa8fe3c5c 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -649,4 +649,14 @@ namespace OpenRA.Traits { void PlayerDisconnected(Actor self, Player p); } + + // Type tag for crush class bits + public class CrushClass { } + + [RequireExplicitImplementation] + public interface ICrushable + { + bool CrushableBy(Actor self, Actor crusher, BitSet crushClasses); + LongBitSet CrushableBy(Actor self, BitSet crushClasses); + } } diff --git a/OpenRA.Mods.Common/Pathfinder/HierarchicalPathFinder.cs b/OpenRA.Mods.Common/Pathfinder/HierarchicalPathFinder.cs index 7ac12bca5d..e1f7f069eb 100644 --- a/OpenRA.Mods.Common/Pathfinder/HierarchicalPathFinder.cs +++ b/OpenRA.Mods.Common/Pathfinder/HierarchicalPathFinder.cs @@ -657,8 +657,7 @@ namespace OpenRA.Mods.Common.Pathfinder if (isTemporaryBlocker) return false; - var crushables = actor.TraitsImplementing(); - foreach (var crushable in crushables) + foreach (var crushable in actor.Crushables) if (world.NoPlayersMask != crushable.CrushableBy(actor, locomotor.Info.Crushes)) return false; diff --git a/OpenRA.Mods.Common/Traits/Air/Aircraft.cs b/OpenRA.Mods.Common/Traits/Air/Aircraft.cs index afe1af773d..753bbb3fc5 100644 --- a/OpenRA.Mods.Common/Traits/Air/Aircraft.cs +++ b/OpenRA.Mods.Common/Traits/Air/Aircraft.cs @@ -713,8 +713,7 @@ namespace OpenRA.Mods.Common.Traits // If the other actor in our way cannot be crushed, we are blocked. // PERF: Avoid LINQ. - var crushables = otherActor.TraitsImplementing(); - foreach (var crushable in crushables) + foreach (var crushable in otherActor.Crushables) if (crushable.CrushableBy(otherActor, self, Info.Crushes)) return false; @@ -857,7 +856,7 @@ namespace OpenRA.Mods.Common.Traits void CrushAction(Actor self, Func>> action) { var crushables = self.World.ActorMap.GetActorsAt(TopLeft).Where(a => a != self) - .SelectMany(a => a.TraitsImplementing().Select(t => new TraitPair(a, t))); + .SelectMany(a => a.Crushables.Select(t => new TraitPair(a, t))); // Only crush actors that are on the ground level foreach (var crushable in crushables) diff --git a/OpenRA.Mods.Common/Traits/AutoCrusher.cs b/OpenRA.Mods.Common/Traits/AutoCrusher.cs index 3b5de41008..697d8bd22e 100644 --- a/OpenRA.Mods.Common/Traits/AutoCrusher.cs +++ b/OpenRA.Mods.Common/Traits/AutoCrusher.cs @@ -92,7 +92,7 @@ namespace OpenRA.Mods.Common.Traits if (target.TraitsImplementing().Any(c => !c.IsTraitDisabled && !c.IsVisible(target, self.Owner))) return false; - return target.TraitsImplementing().Any(c => c.CrushableBy(target, self, Info.CrushClasses)); + return target.Crushables.Any(c => c.CrushableBy(target, self, Info.CrushClasses)); } protected override void TraitEnabled(Actor self) diff --git a/OpenRA.Mods.Common/Traits/Crushable.cs b/OpenRA.Mods.Common/Traits/Crushable.cs index 579c4b6baa..e5fbc48ab2 100644 --- a/OpenRA.Mods.Common/Traits/Crushable.cs +++ b/OpenRA.Mods.Common/Traits/Crushable.cs @@ -10,6 +10,7 @@ #endregion using OpenRA.Primitives; +using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { diff --git a/OpenRA.Mods.Common/Traits/Husk.cs b/OpenRA.Mods.Common/Traits/Husk.cs index f47773f282..ed80b6da0c 100644 --- a/OpenRA.Mods.Common/Traits/Husk.cs +++ b/OpenRA.Mods.Common/Traits/Husk.cs @@ -219,7 +219,7 @@ namespace OpenRA.Mods.Common.Traits return; var crushables = self.World.ActorMap.GetActorsAt(self.World.Map.CellContaining(position)).Where(a => a != self) - .SelectMany(a => a.TraitsImplementing().Select(t => new TraitPair(a, t))); + .SelectMany(a => a.Crushables.Select(t => new TraitPair(a, t))); // Only crush actors that are on the ground level. foreach (var crushable in crushables) diff --git a/OpenRA.Mods.Common/Traits/Mobile.cs b/OpenRA.Mods.Common/Traits/Mobile.cs index 6d59c7b112..f359383717 100644 --- a/OpenRA.Mods.Common/Traits/Mobile.cs +++ b/OpenRA.Mods.Common/Traits/Mobile.cs @@ -574,7 +574,7 @@ namespace OpenRA.Mods.Common.Traits void CrushAction(Actor self, Func>> action) { var crushables = self.World.ActorMap.GetActorsAt(ToCell, ToSubCell).Where(a => a != self) - .SelectMany(a => a.TraitsImplementing().Select(t => new TraitPair(a, t))); + .SelectMany(a => a.Crushables.Select(t => new TraitPair(a, t))); // Only crush actors that are on the ground level foreach (var crushable in crushables) diff --git a/OpenRA.Mods.Common/Traits/World/Locomotor.cs b/OpenRA.Mods.Common/Traits/World/Locomotor.cs index 8f4d051a49..4f5c06ea0b 100644 --- a/OpenRA.Mods.Common/Traits/World/Locomotor.cs +++ b/OpenRA.Mods.Common/Traits/World/Locomotor.cs @@ -380,7 +380,7 @@ namespace OpenRA.Mods.Common.Traits // If the other actor in our way cannot be crushed, we are blocked. // PERF: Avoid LINQ. - var crushables = otherActor.TraitsImplementing(); + var crushables = otherActor.Crushables; foreach (var crushable in crushables) if (crushable.CrushableBy(otherActor, actor, Info.Crushes)) return false; @@ -493,7 +493,7 @@ namespace OpenRA.Mods.Common.Traits var actorImmovablePlayers = world.AllPlayersMask; var actorCrushablePlayers = world.NoPlayersMask; - var crushables = actor.TraitsImplementing(); + var crushables = actor.Crushables; var mobile = actor.OccupiesSpace as Mobile; var isMovable = mobile != null && !mobile.IsTraitDisabled && !mobile.IsTraitPaused && !mobile.IsImmovable; var isMoving = isMovable && mobile.CurrentMovementTypes.HasMovementType(MovementType.Horizontal); diff --git a/OpenRA.Mods.Common/TraitsInterfaces.cs b/OpenRA.Mods.Common/TraitsInterfaces.cs index 0ebfcabde1..77f7138046 100644 --- a/OpenRA.Mods.Common/TraitsInterfaces.cs +++ b/OpenRA.Mods.Common/TraitsInterfaces.cs @@ -93,16 +93,6 @@ namespace OpenRA.Mods.Common.Traits void Demolish(Actor self, Actor saboteur, int delay, BitSet damageTypes); } - // Type tag for crush class bits - public class CrushClass { } - - [RequireExplicitImplementation] - public interface ICrushable - { - bool CrushableBy(Actor self, Actor crusher, BitSet crushClasses); - LongBitSet CrushableBy(Actor self, BitSet crushClasses); - } - [RequireExplicitImplementation] public interface INotifyCrushed {