diff --git a/OpenRA.Game/Actor.cs b/OpenRA.Game/Actor.cs index 23436144f5..8ac6ec3419 100644 --- a/OpenRA.Game/Actor.cs +++ b/OpenRA.Game/Actor.cs @@ -77,6 +77,8 @@ namespace OpenRA readonly IMouseBounds[] mouseBounds; readonly IVisibilityModifier[] visibilityModifiers; readonly IDefaultVisibility defaultVisibility; + readonly ITargetablePositions[] targetablePositions; + WPos[] staticTargetablePositions; internal Actor(World world, string name, TypeDictionary initDict) { @@ -118,6 +120,15 @@ namespace OpenRA visibilityModifiers = TraitsImplementing().ToArray(); defaultVisibility = Trait(); Targetables = TraitsImplementing().ToArray(); + targetablePositions = TraitsImplementing().ToArray(); + world.AddFrameEndTask(w => + { + // Caching this in a AddFrameEndTask, because trait construction order might cause problems if done directly at creation time. + // All actors that can move should have IMove, if not it's pretty safe to assume the actor is immobile and + // all targetable positions can be cached if all ITargetablePositions have no conditional requirements. + if (!Info.HasTraitInfo() && targetablePositions.All(tp => tp.AlwaysEnabled)) + staticTargetablePositions = targetablePositions.SelectMany(tp => tp.TargetablePositions(this)).ToArray(); + }); SyncHashes = TraitsImplementing().Select(sync => new SyncHash(sync)).ToArray(); } @@ -368,6 +379,18 @@ namespace OpenRA return false; } + public IEnumerable GetTargetablePositions() + { + if (staticTargetablePositions != null) + return staticTargetablePositions; + + var enabledTargetablePositionTraits = targetablePositions.Where(Exts.IsTraitEnabled); + if (enabledTargetablePositionTraits.Any()) + return enabledTargetablePositionTraits.SelectMany(tp => tp.TargetablePositions(this)); + + return new[] { this.CenterPosition }; + } + #region Scripting interface Lazy luaInterface; diff --git a/OpenRA.Game/Traits/Target.cs b/OpenRA.Game/Traits/Target.cs index c8660740ba..b8a2c7c6f9 100644 --- a/OpenRA.Game/Traits/Target.cs +++ b/OpenRA.Game/Traits/Target.cs @@ -157,14 +157,7 @@ namespace OpenRA.Traits if (!actor.Targetables.Any(Exts.IsTraitEnabled)) return new[] { actor.CenterPosition }; - var targetablePositions = actor.TraitsImplementing().Where(Exts.IsTraitEnabled); - if (targetablePositions.Any()) - { - var target = this; - return targetablePositions.SelectMany(tp => tp.TargetablePositions(target.actor)); - } - - return new[] { actor.CenterPosition }; + return actor.GetTargetablePositions(); case TargetType.FrozenActor: return new[] { frozen.CenterPosition }; case TargetType.Terrain: diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index 5cc6623a31..a5e1a197c8 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -387,8 +387,11 @@ namespace OpenRA.Traits public interface ITargetablePositions { IEnumerable TargetablePositions(Actor self); + bool AlwaysEnabled { get; } } + public interface IMoveInfo : ITraitInfoInterface { } + [RequireExplicitImplementation] public interface IGameOver { void GameOver(World world); } diff --git a/OpenRA.Mods.Common/Traits/HitShape.cs b/OpenRA.Mods.Common/Traits/HitShape.cs index 9601bec65d..4d6f0aa405 100644 --- a/OpenRA.Mods.Common/Traits/HitShape.cs +++ b/OpenRA.Mods.Common/Traits/HitShape.cs @@ -73,6 +73,8 @@ namespace OpenRA.Mods.Common.Traits base.Created(self); } + bool ITargetablePositions.AlwaysEnabled { get { return Info.RequiresCondition == null; } } + IEnumerable ITargetablePositions.TargetablePositions(Actor self) { if (IsTraitDisabled) diff --git a/OpenRA.Mods.Common/TraitsInterfaces.cs b/OpenRA.Mods.Common/TraitsInterfaces.cs index 744211fbfe..90a9e10a9a 100644 --- a/OpenRA.Mods.Common/TraitsInterfaces.cs +++ b/OpenRA.Mods.Common/TraitsInterfaces.cs @@ -334,8 +334,6 @@ namespace OpenRA.Mods.Common.Traits IEnumerable ActorPreviewInits(ActorInfo ai, ActorPreviewType type); } - public interface IMoveInfo : ITraitInfoInterface { } - public interface IMove { Activity MoveTo(CPos cell, int nearEnough);