Store Targetables in Actor.

This can be used to avoid several lookups for these traits, as well as allow Actor to provide specialised methods to deal with target types efficiently. This also reduces some code duplication.
This commit is contained in:
RoosterDragon
2015-10-17 00:45:40 +01:00
parent 8230be6a14
commit dcf375a412
14 changed files with 66 additions and 35 deletions

View File

@@ -44,6 +44,7 @@ namespace OpenRA
public Rectangle VisualBounds { get; private set; }
public IEffectiveOwner EffectiveOwner { get; private set; }
public IOccupySpace OccupiesSpace { get; private set; }
public ITargetable[] Targetables { get; private set; }
public bool IsIdle { get { return currentActivity == null; } }
public bool IsDead { get { return Disposed || (health != null && health.IsDead); } }
@@ -107,6 +108,7 @@ namespace OpenRA
disables = TraitsImplementing<IDisable>().ToArray();
visibilityModifiers = TraitsImplementing<IVisibilityModifier>().ToArray();
defaultVisibility = Trait<IDefaultVisibility>();
Targetables = TraitsImplementing<ITargetable>().ToArray();
}
Rectangle DetermineBounds()
@@ -320,6 +322,30 @@ namespace OpenRA
return defaultVisibility.IsVisible(this, player);
}
public IEnumerable<string> GetAllTargetTypes()
{
foreach (var targetable in Targetables)
foreach (var targetType in targetable.TargetTypes)
yield return targetType;
}
public IEnumerable<string> GetEnabledTargetTypes()
{
foreach (var targetable in Targetables)
if (targetable.IsTraitEnabled())
foreach (var targetType in targetable.TargetTypes)
yield return targetType;
}
public bool IsTargetableBy(Actor byActor)
{
foreach (var targetable in Targetables)
if (targetable.IsTraitEnabled() && targetable.TargetableBy(this, byActor))
return true;
return false;
}
#region Scripting interface
Lazy<ScriptActorInterface> luaInterface;

View File

@@ -127,8 +127,9 @@ namespace OpenRA.GameRules
/// <summary>Checks if the weapon is valid against (can target) the actor.</summary>
public bool IsValidAgainst(Actor victim, Actor firedBy)
{
var targetable = victim.TraitsImplementing<ITargetable>().Where(Exts.IsTraitEnabled);
if (!IsValidTarget(targetable.SelectMany(t => t.TargetTypes)))
var targetTypes = victim.GetEnabledTargetTypes();
if (!IsValidTarget(targetTypes))
return false;
if (!Warheads.Any(w => w.IsValidAgainst(victim, firedBy)))

View File

@@ -59,7 +59,7 @@ namespace OpenRA.Traits
CenterPosition = self.CenterPosition;
Bounds = self.Bounds;
TargetTypes = self.TraitsImplementing<ITargetable>().Where(Exts.IsTraitEnabled).SelectMany(t => t.TargetTypes).ToHashSet();
TargetTypes = self.GetEnabledTargetTypes().ToHashSet();
UpdateVisibility();
}

View File

@@ -22,7 +22,6 @@ namespace OpenRA.Traits
TargetType type;
Actor actor;
IEnumerable<ITargetable> targetable;
FrozenActor frozen;
WPos pos;
int generation;
@@ -48,7 +47,6 @@ namespace OpenRA.Traits
return new Target
{
actor = a,
targetable = a.TraitsImplementing<ITargetable>(),
type = TargetType.Actor,
generation = a.Generation,
};
@@ -83,8 +81,7 @@ namespace OpenRA.Traits
if (targeter == null || Type == TargetType.Invalid)
return false;
var targeted = this.actor;
if (targeted != null && !targetable.Any(t => t.IsTraitEnabled() && t.TargetableBy(targeted, targeter)))
if (actor != null && !actor.IsTargetableBy(targeter))
return false;
return true;
@@ -94,7 +91,24 @@ namespace OpenRA.Traits
// TODO: either replace based on target type or put in singleton trait
public bool RequiresForceFire
{
get { return targetable != null && targetable.Any(Exts.IsTraitEnabled) && targetable.Where(Exts.IsTraitEnabled).All(t => t.RequiresForceFire); }
get
{
if (actor == null)
return false;
var isTargetable = false;
foreach (var targetable in actor.Targetables)
{
if (!targetable.IsTraitEnabled())
continue;
isTargetable = true;
if (!targetable.RequiresForceFire)
return false;
}
return isTargetable;
}
}
// Representative position - see Positions for the full set of targetable positions.
@@ -126,8 +140,7 @@ namespace OpenRA.Traits
switch (Type)
{
case TargetType.Actor:
var targetable = actor.TraitsImplementing<ITargetable>().Where(Exts.IsTraitEnabled);
if (!targetable.Any())
if (!actor.Targetables.Any(Exts.IsTraitEnabled))
return new[] { actor.CenterPosition };
var targetablePositions = actor.TraitOrDefault<ITargetablePositions>();

View File

@@ -63,7 +63,7 @@ namespace OpenRA.Mods.Common.AI
if (!a.Info.HasTraitInfo<AttackBaseInfo>())
return false;
var targetTypes = target.TraitsImplementing<ITargetable>().Where(Exts.IsTraitEnabled).SelectMany(t => t.TargetTypes);
var targetTypes = target.GetEnabledTargetTypes();
if (!targetTypes.Any())
return false;

View File

@@ -125,11 +125,10 @@ namespace OpenRA.Mods.Common.AI
if (a == null)
return 0;
var targetable = a.TraitsImplementing<ITargetable>().Where(Exts.IsTraitEnabled);
if (!targetable.Any(t => t.TargetableBy(a, firedBy.PlayerActor)))
if (!a.IsTargetableBy(firedBy.PlayerActor))
return 0;
if (Types.Overlaps(targetable.SelectMany(t => t.TargetTypes)))
if (Types.Overlaps(a.GetEnabledTargetTypes()))
{
switch (TargetMetric)
{

View File

@@ -25,12 +25,7 @@ namespace OpenRA.Mods.Common.Activities
var attack = self.Trait<AttackBase>();
targets = self.World.ActorsHavingTrait<Huntable>().Where(
a => self != a && !a.IsDead && a.IsInWorld && a.AppearsHostileTo(self)
&& IsTargetable(a, self) && attack.HasAnyValidWeapons(Target.FromActor(a)));
}
bool IsTargetable(Actor self, Actor viewer)
{
return self.TraitsImplementing<ITargetable>().Any(t => t.IsTraitEnabled() && t.TargetableBy(self, viewer));
&& a.IsTargetableBy(self) && attack.HasAnyValidWeapons(Target.FromActor(a)));
}
public override Activity Tick(Actor self)

View File

@@ -67,9 +67,9 @@ namespace OpenRA.Mods.Common.Orders
public class TargetTypeOrderTargeter : UnitOrderTargeter
{
readonly string[] targetTypes;
readonly HashSet<string> targetTypes;
public TargetTypeOrderTargeter(string[] targetTypes, string order, int priority, string cursor, bool targetEnemyUnits, bool targetAllyUnits)
public TargetTypeOrderTargeter(HashSet<string> targetTypes, string order, int priority, string cursor, bool targetEnemyUnits, bool targetAllyUnits)
: base(order, priority, cursor, targetEnemyUnits, targetAllyUnits)
{
this.targetTypes = targetTypes;
@@ -77,7 +77,7 @@ namespace OpenRA.Mods.Common.Orders
public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor)
{
return target.TraitsImplementing<ITargetable>().Any(t => t.IsTraitEnabled() && t.TargetTypes.Overlaps(targetTypes));
return targetTypes.Overlaps(target.GetEnabledTargetTypes());
}
public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor)

View File

@@ -58,8 +58,7 @@ namespace OpenRA.Mods.Common.Traits
if (info.ValidFactions.Any() && !info.ValidFactions.Contains(collector.Owner.Faction.InternalName))
return false;
var targetable = collector.TraitsImplementing<ITargetable>().Where(Exts.IsTraitEnabled);
if (!info.ValidTargets.Overlaps(targetable.SelectMany(t => t.TargetTypes)))
if (!info.ValidTargets.Overlaps(collector.GetEnabledTargetTypes()))
return false;
var positionable = collector.TraitOrDefault<IPositionable>();

View File

@@ -56,8 +56,7 @@ namespace OpenRA.Mods.Common.Warheads
return false;
// A target type is valid if it is in the valid targets list, and not in the invalid targets list.
var targetable = victim.TraitsImplementing<ITargetable>().Where(Exts.IsTraitEnabled);
if (!IsValidTarget(targetable.SelectMany(t => t.TargetTypes)))
if (!IsValidTarget(victim.GetEnabledTargetTypes()))
return false;
return true;

View File

@@ -44,7 +44,7 @@ namespace OpenRA.Mods.RA.Traits
{
get
{
yield return new TargetTypeOrderTargeter(new[] { "DetonateAttack" }, "DetonateAttack", 5, "attack", true, false) { ForceAttack = false };
yield return new TargetTypeOrderTargeter(new HashSet<string> { "DetonateAttack" }, "DetonateAttack", 5, "attack", true, false) { ForceAttack = false };
yield return new DeployOrderTargeter("Detonate", 5);
}
}

View File

@@ -94,7 +94,7 @@ namespace OpenRA.Mods.RA.Traits
{
get
{
yield return new TargetTypeOrderTargeter(new[] { "Disguise" }, "Disguise", 7, "ability", true, true) { ForceAttack = false };
yield return new TargetTypeOrderTargeter(new HashSet<string> { "Disguise" }, "Disguise", 7, "ability", true, true) { ForceAttack = false };
}
}

View File

@@ -77,8 +77,7 @@ namespace OpenRA.Mods.RA.Traits
targetTypes = frozen.TargetTypes;
}
else
targetTypes = order.TargetActor.TraitsImplementing<ITargetable>().Where(Exts.IsTraitEnabled)
.SelectMany(t => t.TargetTypes);
targetTypes = order.TargetActor.GetEnabledTargetTypes();
return Info.Types.Overlaps(targetTypes);
}
@@ -96,7 +95,7 @@ namespace OpenRA.Mods.RA.Traits
var target = self.ResolveFrozenActorOrder(order, Color.Red);
if (target.Type != TargetType.Actor
|| !Info.Types.Overlaps(target.Actor.TraitsImplementing<ITargetable>().SelectMany(t => t.TargetTypes)))
|| !Info.Types.Overlaps(target.Actor.GetAllTargetTypes()))
return;
if (!order.Queued)
@@ -124,7 +123,7 @@ namespace OpenRA.Mods.RA.Traits
if (!info.ValidStances.HasStance(stance))
return false;
return target.TraitsImplementing<ITargetable>().Any(t => t.TargetTypes.Overlaps(info.Types));
return info.Types.Overlaps(target.GetAllTargetTypes());
}
public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor)
@@ -134,7 +133,7 @@ namespace OpenRA.Mods.RA.Traits
if (!info.ValidStances.HasStance(stance))
return false;
return target.Info.TraitInfos<ITargetableInfo>().Any(t => info.Types.Overlaps(t.GetTargetTypes()));
return info.Types.Overlaps(target.Info.TraitInfos<ITargetableInfo>().SelectMany(ti => ti.GetTargetTypes()));
}
}
}

View File

@@ -98,7 +98,7 @@ namespace OpenRA.Mods.RA.Traits
{
get
{
yield return new TargetTypeOrderTargeter(new[] { "DetonateAttack" }, "DetonateAttack", 5, "attack", true, false) { ForceAttack = false };
yield return new TargetTypeOrderTargeter(new HashSet<string> { "DetonateAttack" }, "DetonateAttack", 5, "attack", true, false) { ForceAttack = false };
yield return new DeployOrderTargeter("Detonate", 5);
}
}