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:
@@ -44,6 +44,7 @@ namespace OpenRA
|
|||||||
public Rectangle VisualBounds { get; private set; }
|
public Rectangle VisualBounds { get; private set; }
|
||||||
public IEffectiveOwner EffectiveOwner { get; private set; }
|
public IEffectiveOwner EffectiveOwner { get; private set; }
|
||||||
public IOccupySpace OccupiesSpace { get; private set; }
|
public IOccupySpace OccupiesSpace { get; private set; }
|
||||||
|
public ITargetable[] Targetables { get; private set; }
|
||||||
|
|
||||||
public bool IsIdle { get { return currentActivity == null; } }
|
public bool IsIdle { get { return currentActivity == null; } }
|
||||||
public bool IsDead { get { return Disposed || (health != null && health.IsDead); } }
|
public bool IsDead { get { return Disposed || (health != null && health.IsDead); } }
|
||||||
@@ -107,6 +108,7 @@ namespace OpenRA
|
|||||||
disables = TraitsImplementing<IDisable>().ToArray();
|
disables = TraitsImplementing<IDisable>().ToArray();
|
||||||
visibilityModifiers = TraitsImplementing<IVisibilityModifier>().ToArray();
|
visibilityModifiers = TraitsImplementing<IVisibilityModifier>().ToArray();
|
||||||
defaultVisibility = Trait<IDefaultVisibility>();
|
defaultVisibility = Trait<IDefaultVisibility>();
|
||||||
|
Targetables = TraitsImplementing<ITargetable>().ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle DetermineBounds()
|
Rectangle DetermineBounds()
|
||||||
@@ -320,6 +322,30 @@ namespace OpenRA
|
|||||||
return defaultVisibility.IsVisible(this, player);
|
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
|
#region Scripting interface
|
||||||
|
|
||||||
Lazy<ScriptActorInterface> luaInterface;
|
Lazy<ScriptActorInterface> luaInterface;
|
||||||
|
|||||||
@@ -127,8 +127,9 @@ namespace OpenRA.GameRules
|
|||||||
/// <summary>Checks if the weapon is valid against (can target) the actor.</summary>
|
/// <summary>Checks if the weapon is valid against (can target) the actor.</summary>
|
||||||
public bool IsValidAgainst(Actor victim, Actor firedBy)
|
public bool IsValidAgainst(Actor victim, Actor firedBy)
|
||||||
{
|
{
|
||||||
var targetable = victim.TraitsImplementing<ITargetable>().Where(Exts.IsTraitEnabled);
|
var targetTypes = victim.GetEnabledTargetTypes();
|
||||||
if (!IsValidTarget(targetable.SelectMany(t => t.TargetTypes)))
|
|
||||||
|
if (!IsValidTarget(targetTypes))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!Warheads.Any(w => w.IsValidAgainst(victim, firedBy)))
|
if (!Warheads.Any(w => w.IsValidAgainst(victim, firedBy)))
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ namespace OpenRA.Traits
|
|||||||
|
|
||||||
CenterPosition = self.CenterPosition;
|
CenterPosition = self.CenterPosition;
|
||||||
Bounds = self.Bounds;
|
Bounds = self.Bounds;
|
||||||
TargetTypes = self.TraitsImplementing<ITargetable>().Where(Exts.IsTraitEnabled).SelectMany(t => t.TargetTypes).ToHashSet();
|
TargetTypes = self.GetEnabledTargetTypes().ToHashSet();
|
||||||
|
|
||||||
UpdateVisibility();
|
UpdateVisibility();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ namespace OpenRA.Traits
|
|||||||
|
|
||||||
TargetType type;
|
TargetType type;
|
||||||
Actor actor;
|
Actor actor;
|
||||||
IEnumerable<ITargetable> targetable;
|
|
||||||
FrozenActor frozen;
|
FrozenActor frozen;
|
||||||
WPos pos;
|
WPos pos;
|
||||||
int generation;
|
int generation;
|
||||||
@@ -48,7 +47,6 @@ namespace OpenRA.Traits
|
|||||||
return new Target
|
return new Target
|
||||||
{
|
{
|
||||||
actor = a,
|
actor = a,
|
||||||
targetable = a.TraitsImplementing<ITargetable>(),
|
|
||||||
type = TargetType.Actor,
|
type = TargetType.Actor,
|
||||||
generation = a.Generation,
|
generation = a.Generation,
|
||||||
};
|
};
|
||||||
@@ -83,8 +81,7 @@ namespace OpenRA.Traits
|
|||||||
if (targeter == null || Type == TargetType.Invalid)
|
if (targeter == null || Type == TargetType.Invalid)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var targeted = this.actor;
|
if (actor != null && !actor.IsTargetableBy(targeter))
|
||||||
if (targeted != null && !targetable.Any(t => t.IsTraitEnabled() && t.TargetableBy(targeted, targeter)))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -94,7 +91,24 @@ namespace OpenRA.Traits
|
|||||||
// TODO: either replace based on target type or put in singleton trait
|
// TODO: either replace based on target type or put in singleton trait
|
||||||
public bool RequiresForceFire
|
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.
|
// Representative position - see Positions for the full set of targetable positions.
|
||||||
@@ -126,8 +140,7 @@ namespace OpenRA.Traits
|
|||||||
switch (Type)
|
switch (Type)
|
||||||
{
|
{
|
||||||
case TargetType.Actor:
|
case TargetType.Actor:
|
||||||
var targetable = actor.TraitsImplementing<ITargetable>().Where(Exts.IsTraitEnabled);
|
if (!actor.Targetables.Any(Exts.IsTraitEnabled))
|
||||||
if (!targetable.Any())
|
|
||||||
return new[] { actor.CenterPosition };
|
return new[] { actor.CenterPosition };
|
||||||
|
|
||||||
var targetablePositions = actor.TraitOrDefault<ITargetablePositions>();
|
var targetablePositions = actor.TraitOrDefault<ITargetablePositions>();
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ namespace OpenRA.Mods.Common.AI
|
|||||||
if (!a.Info.HasTraitInfo<AttackBaseInfo>())
|
if (!a.Info.HasTraitInfo<AttackBaseInfo>())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var targetTypes = target.TraitsImplementing<ITargetable>().Where(Exts.IsTraitEnabled).SelectMany(t => t.TargetTypes);
|
var targetTypes = target.GetEnabledTargetTypes();
|
||||||
if (!targetTypes.Any())
|
if (!targetTypes.Any())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|||||||
@@ -125,11 +125,10 @@ namespace OpenRA.Mods.Common.AI
|
|||||||
if (a == null)
|
if (a == null)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
var targetable = a.TraitsImplementing<ITargetable>().Where(Exts.IsTraitEnabled);
|
if (!a.IsTargetableBy(firedBy.PlayerActor))
|
||||||
if (!targetable.Any(t => t.TargetableBy(a, firedBy.PlayerActor)))
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (Types.Overlaps(targetable.SelectMany(t => t.TargetTypes)))
|
if (Types.Overlaps(a.GetEnabledTargetTypes()))
|
||||||
{
|
{
|
||||||
switch (TargetMetric)
|
switch (TargetMetric)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -25,12 +25,7 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
var attack = self.Trait<AttackBase>();
|
var attack = self.Trait<AttackBase>();
|
||||||
targets = self.World.ActorsHavingTrait<Huntable>().Where(
|
targets = self.World.ActorsHavingTrait<Huntable>().Where(
|
||||||
a => self != a && !a.IsDead && a.IsInWorld && a.AppearsHostileTo(self)
|
a => self != a && !a.IsDead && a.IsInWorld && a.AppearsHostileTo(self)
|
||||||
&& IsTargetable(a, self) && attack.HasAnyValidWeapons(Target.FromActor(a)));
|
&& a.IsTargetableBy(self) && attack.HasAnyValidWeapons(Target.FromActor(a)));
|
||||||
}
|
|
||||||
|
|
||||||
bool IsTargetable(Actor self, Actor viewer)
|
|
||||||
{
|
|
||||||
return self.TraitsImplementing<ITargetable>().Any(t => t.IsTraitEnabled() && t.TargetableBy(self, viewer));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Activity Tick(Actor self)
|
public override Activity Tick(Actor self)
|
||||||
|
|||||||
@@ -67,9 +67,9 @@ namespace OpenRA.Mods.Common.Orders
|
|||||||
|
|
||||||
public class TargetTypeOrderTargeter : UnitOrderTargeter
|
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)
|
: base(order, priority, cursor, targetEnemyUnits, targetAllyUnits)
|
||||||
{
|
{
|
||||||
this.targetTypes = targetTypes;
|
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)
|
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)
|
public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor)
|
||||||
|
|||||||
@@ -58,8 +58,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
if (info.ValidFactions.Any() && !info.ValidFactions.Contains(collector.Owner.Faction.InternalName))
|
if (info.ValidFactions.Any() && !info.ValidFactions.Contains(collector.Owner.Faction.InternalName))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var targetable = collector.TraitsImplementing<ITargetable>().Where(Exts.IsTraitEnabled);
|
if (!info.ValidTargets.Overlaps(collector.GetEnabledTargetTypes()))
|
||||||
if (!info.ValidTargets.Overlaps(targetable.SelectMany(t => t.TargetTypes)))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var positionable = collector.TraitOrDefault<IPositionable>();
|
var positionable = collector.TraitOrDefault<IPositionable>();
|
||||||
|
|||||||
@@ -56,8 +56,7 @@ namespace OpenRA.Mods.Common.Warheads
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// A target type is valid if it is in the valid targets list, and not in the invalid targets list.
|
// 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(victim.GetEnabledTargetTypes()))
|
||||||
if (!IsValidTarget(targetable.SelectMany(t => t.TargetTypes)))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ namespace OpenRA.Mods.RA.Traits
|
|||||||
{
|
{
|
||||||
get
|
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);
|
yield return new DeployOrderTargeter("Detonate", 5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ namespace OpenRA.Mods.RA.Traits
|
|||||||
{
|
{
|
||||||
get
|
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 };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,8 +77,7 @@ namespace OpenRA.Mods.RA.Traits
|
|||||||
targetTypes = frozen.TargetTypes;
|
targetTypes = frozen.TargetTypes;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
targetTypes = order.TargetActor.TraitsImplementing<ITargetable>().Where(Exts.IsTraitEnabled)
|
targetTypes = order.TargetActor.GetEnabledTargetTypes();
|
||||||
.SelectMany(t => t.TargetTypes);
|
|
||||||
|
|
||||||
return Info.Types.Overlaps(targetTypes);
|
return Info.Types.Overlaps(targetTypes);
|
||||||
}
|
}
|
||||||
@@ -96,7 +95,7 @@ namespace OpenRA.Mods.RA.Traits
|
|||||||
|
|
||||||
var target = self.ResolveFrozenActorOrder(order, Color.Red);
|
var target = self.ResolveFrozenActorOrder(order, Color.Red);
|
||||||
if (target.Type != TargetType.Actor
|
if (target.Type != TargetType.Actor
|
||||||
|| !Info.Types.Overlaps(target.Actor.TraitsImplementing<ITargetable>().SelectMany(t => t.TargetTypes)))
|
|| !Info.Types.Overlaps(target.Actor.GetAllTargetTypes()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!order.Queued)
|
if (!order.Queued)
|
||||||
@@ -124,7 +123,7 @@ namespace OpenRA.Mods.RA.Traits
|
|||||||
if (!info.ValidStances.HasStance(stance))
|
if (!info.ValidStances.HasStance(stance))
|
||||||
return false;
|
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)
|
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))
|
if (!info.ValidStances.HasStance(stance))
|
||||||
return false;
|
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()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ namespace OpenRA.Mods.RA.Traits
|
|||||||
{
|
{
|
||||||
get
|
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);
|
yield return new DeployOrderTargeter("Detonate", 5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user