diff --git a/OpenRA.Game/GameRules/WeaponInfo.cs b/OpenRA.Game/GameRules/WeaponInfo.cs
index aacdb0fea9..6af645ce6b 100644
--- a/OpenRA.Game/GameRules/WeaponInfo.cs
+++ b/OpenRA.Game/GameRules/WeaponInfo.cs
@@ -125,8 +125,8 @@ namespace OpenRA.GameRules
/// Checks if the weapon is valid against (can target) the actor.
public bool IsValidAgainst(Actor victim, Actor firedBy)
{
- var targetable = victim.TraitOrDefault();
- if (targetable == null || !IsValidTarget(targetable.TargetTypes))
+ var targetable = victim.TraitsImplementing().Where(Exts.IsTraitEnabled);
+ if (!IsValidTarget(targetable.SelectMany(t => t.TargetTypes)))
return false;
if (!Warheads.Any(w => w.IsValidAgainst(victim, firedBy)))
@@ -138,8 +138,8 @@ namespace OpenRA.GameRules
/// Checks if the weapon is valid against (can target) the frozen actor.
public bool IsValidAgainst(FrozenActor victim, Actor firedBy)
{
- var targetable = victim.Info.Traits.GetOrDefault();
- if (targetable == null || !IsValidTarget(targetable.GetTargetTypes()))
+ var targetable = victim.Info.Traits.WithInterface();
+ if (!IsValidTarget(targetable.SelectMany(t => t.GetTargetTypes())))
return false;
if (!Warheads.Any(w => w.IsValidAgainst(victim, firedBy)))
diff --git a/OpenRA.Game/Traits/Target.cs b/OpenRA.Game/Traits/Target.cs
index d5261e04fa..bd0aa47018 100644
--- a/OpenRA.Game/Traits/Target.cs
+++ b/OpenRA.Game/Traits/Target.cs
@@ -22,7 +22,7 @@ namespace OpenRA.Traits
TargetType type;
Actor actor;
- ITargetable targetable;
+ IEnumerable targetable;
FrozenActor frozen;
WPos pos;
int generation;
@@ -48,7 +48,7 @@ namespace OpenRA.Traits
return new Target
{
actor = a,
- targetable = a.TraitOrDefault(),
+ targetable = a.TraitsImplementing(),
type = TargetType.Actor,
generation = a.Generation,
};
@@ -83,15 +83,18 @@ namespace OpenRA.Traits
if (targeter == null || Type == TargetType.Invalid)
return false;
- if (targetable != null && !targetable.TargetableBy(actor, targeter))
+ var targeted = this.actor;
+ if (targeted != null && !targetable.Any(t => t.IsTraitEnabled() && t.TargetableBy(targeted, targeter)))
return false;
return true;
}
+ // Currently all or nothing.
+ // TODO: either replace based on target type or put in singleton trait
public bool RequiresForceFire
{
- get { return targetable != null && targetable.RequiresForceFire; }
+ get { return targetable != null && targetable.Any(Exts.IsTraitEnabled) && targetable.Where(Exts.IsTraitEnabled).All(t => t.RequiresForceFire); }
}
// Representative position - see Positions for the full set of targetable positions.
@@ -123,12 +126,13 @@ namespace OpenRA.Traits
switch (Type)
{
case TargetType.Actor:
- var targetable = actor.TraitOrDefault();
- if (targetable == null)
+ var targetable = actor.TraitsImplementing().Where(Exts.IsTraitEnabled);
+ if (!targetable.Any())
return new[] { actor.CenterPosition };
- var positions = targetable.TargetablePositions(actor);
- return positions.Any() ? positions : new[] { actor.CenterPosition };
+ var targeted = this.actor;
+ var positions = targetable.SelectMany(t => t.TargetablePositions(targeted)).Distinct();
+ return positions.Any() ? positions : new[] { targeted.CenterPosition };
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 ffd7ebb581..0421dbc3ff 100644
--- a/OpenRA.Game/Traits/TraitsInterfaces.cs
+++ b/OpenRA.Game/Traits/TraitsInterfaces.cs
@@ -315,6 +315,7 @@ namespace OpenRA.Traits
public interface ITargetable
{
+ // Check IsTraitEnabled or !IsTraitDisabled first
string[] TargetTypes { get; }
IEnumerable TargetablePositions(Actor self);
bool TargetableBy(Actor self, Actor byActor);
diff --git a/OpenRA.Mods.Common/AI/States/StateBase.cs b/OpenRA.Mods.Common/AI/States/StateBase.cs
index 7c898db728..fd255dbec5 100644
--- a/OpenRA.Mods.Common/AI/States/StateBase.cs
+++ b/OpenRA.Mods.Common/AI/States/StateBase.cs
@@ -63,13 +63,13 @@ namespace OpenRA.Mods.Common.AI
if (!a.HasTrait())
return false;
- var targetable = target.TraitOrDefault();
- if (targetable == null)
+ var targetTypes = target.TraitsImplementing().Where(Exts.IsTraitEnabled).SelectMany(t => t.TargetTypes);
+ if (!targetTypes.Any())
return false;
var arms = a.TraitsImplementing();
foreach (var arm in arms)
- if (arm.Weapon.IsValidTarget(targetable.TargetTypes))
+ if (arm.Weapon.IsValidTarget(targetTypes))
return true;
return false;
diff --git a/OpenRA.Mods.Common/AI/SupportPowerDecision.cs b/OpenRA.Mods.Common/AI/SupportPowerDecision.cs
index a931e3b1de..13b0ad968c 100644
--- a/OpenRA.Mods.Common/AI/SupportPowerDecision.cs
+++ b/OpenRA.Mods.Common/AI/SupportPowerDecision.cs
@@ -125,14 +125,11 @@ namespace OpenRA.Mods.Common.AI
if (a == null)
return 0;
- var targetable = a.TraitOrDefault();
- if (targetable == null)
+ var targetable = a.TraitsImplementing().Where(Exts.IsTraitEnabled);
+ if (!targetable.Any(t => t.TargetableBy(a, firedBy.PlayerActor)))
return 0;
- if (!targetable.TargetableBy(a, firedBy.PlayerActor))
- return 0;
-
- if (Types.Intersect(targetable.TargetTypes).Any())
+ if (Types.Intersect(targetable.SelectMany(t => t.TargetTypes)).Any())
{
switch (TargetMetric)
{
diff --git a/OpenRA.Mods.Common/Activities/Hunt.cs b/OpenRA.Mods.Common/Activities/Hunt.cs
index af520042c6..55ff87989d 100644
--- a/OpenRA.Mods.Common/Activities/Hunt.cs
+++ b/OpenRA.Mods.Common/Activities/Hunt.cs
@@ -29,8 +29,7 @@ namespace OpenRA.Mods.Common.Activities
bool IsTargetable(Actor self, Actor viewer)
{
- var targetable = self.TraitOrDefault();
- return targetable != null && targetable.TargetableBy(self, viewer);
+ return self.TraitsImplementing().Any(t => t.IsTraitEnabled() && t.TargetableBy(self, viewer));
}
public override Activity Tick(Actor self)
diff --git a/OpenRA.Mods.Common/Orders/UnitOrderTargeter.cs b/OpenRA.Mods.Common/Orders/UnitOrderTargeter.cs
index 13ac615446..175da6c5b4 100644
--- a/OpenRA.Mods.Common/Orders/UnitOrderTargeter.cs
+++ b/OpenRA.Mods.Common/Orders/UnitOrderTargeter.cs
@@ -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().Any(t => t.TargetTypes.Intersect(targetTypes).Any());
+ return target.TraitsImplementing().Any(t => t.IsTraitEnabled() && t.TargetTypes.Intersect(targetTypes).Any());
}
public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor)
diff --git a/OpenRA.Mods.Common/Traits/Air/TargetableAircraft.cs b/OpenRA.Mods.Common/Traits/Air/TargetableAircraft.cs
index e8d9924197..fbd60e8003 100644
--- a/OpenRA.Mods.Common/Traits/Air/TargetableAircraft.cs
+++ b/OpenRA.Mods.Common/Traits/Air/TargetableAircraft.cs
@@ -35,8 +35,7 @@ namespace OpenRA.Mods.Common.Traits
{
get
{
- return IsTraitDisabled ? None
- : (self.CenterPosition.Z > 0 ? info.TargetTypes : info.GroundedTargetTypes);
+ return (self.CenterPosition.Z > 0) ? info.TargetTypes : info.GroundedTargetTypes;
}
}
}
diff --git a/OpenRA.Mods.Common/Traits/Crates/DuplicateUnitCrateAction.cs b/OpenRA.Mods.Common/Traits/Crates/DuplicateUnitCrateAction.cs
index bfb78ec27b..f6a83327a6 100644
--- a/OpenRA.Mods.Common/Traits/Crates/DuplicateUnitCrateAction.cs
+++ b/OpenRA.Mods.Common/Traits/Crates/DuplicateUnitCrateAction.cs
@@ -57,8 +57,8 @@ namespace OpenRA.Mods.Common.Traits
if (info.ValidFactions.Any() && !info.ValidFactions.Contains(collector.Owner.Faction.InternalName))
return false;
- var targetable = collector.Info.Traits.GetOrDefault();
- if (targetable == null || !info.ValidTargets.Intersect(targetable.GetTargetTypes()).Any())
+ var targetable = collector.TraitsImplementing();
+ if (!info.ValidTargets.Intersect(targetable.SelectMany(t => t.TargetTypes)).Any())
return false;
var positionable = collector.TraitOrDefault();
diff --git a/OpenRA.Mods.Common/Traits/TargetableUnit.cs b/OpenRA.Mods.Common/Traits/TargetableUnit.cs
index 733c792b10..aa6c25fc6a 100644
--- a/OpenRA.Mods.Common/Traits/TargetableUnit.cs
+++ b/OpenRA.Mods.Common/Traits/TargetableUnit.cs
@@ -46,7 +46,7 @@ namespace OpenRA.Mods.Common.Traits
return cloak.IsVisible(self, viewer.Owner);
}
- public virtual string[] TargetTypes { get { return IsTraitDisabled ? None : Info.TargetTypes; } }
+ public virtual string[] TargetTypes { get { return Info.TargetTypes; } }
public virtual IEnumerable TargetablePositions(Actor self)
{
diff --git a/OpenRA.Mods.Common/Warheads/Warhead.cs b/OpenRA.Mods.Common/Warheads/Warhead.cs
index 42cbe57661..b9b3e1c1a6 100644
--- a/OpenRA.Mods.Common/Warheads/Warhead.cs
+++ b/OpenRA.Mods.Common/Warheads/Warhead.cs
@@ -9,6 +9,7 @@
#endregion
using System.Collections.Generic;
+using System.Linq;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Warheads
@@ -58,8 +59,8 @@ 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.TraitOrDefault();
- if (targetable == null || !IsValidTarget(targetable.TargetTypes))
+ var targetable = victim.TraitsImplementing().Where(Exts.IsTraitEnabled);
+ if (!IsValidTarget(targetable.SelectMany(t => t.TargetTypes)))
return false;
return true;
@@ -74,8 +75,8 @@ 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.Info.Traits.GetOrDefault();
- if (targetable == null || !IsValidTarget(targetable.GetTargetTypes()))
+ var targetable = victim.Info.Traits.WithInterface();
+ if (!IsValidTarget(targetable.SelectMany(t => t.GetTargetTypes())))
return false;
return true;
diff --git a/OpenRA.Mods.RA/Traits/Infiltration/Infiltrates.cs b/OpenRA.Mods.RA/Traits/Infiltration/Infiltrates.cs
index a53da96ba1..f8c4e6ace8 100644
--- a/OpenRA.Mods.RA/Traits/Infiltration/Infiltrates.cs
+++ b/OpenRA.Mods.RA/Traits/Infiltration/Infiltrates.cs
@@ -79,8 +79,8 @@ namespace OpenRA.Mods.RA.Traits
else
ai = order.TargetActor.Info;
- var i = ai.Traits.GetOrDefault();
- return i != null && i.GetTargetTypes().Intersect(Info.Types).Any();
+ return ai.Traits.WithInterface()
+ .SelectMany(t => t.GetTargetTypes()).Intersect(Info.Types).Any();
}
public string VoicePhraseForOrder(Actor self, Order order)
@@ -95,7 +95,8 @@ namespace OpenRA.Mods.RA.Traits
return;
var target = self.ResolveFrozenActorOrder(order, Color.Red);
- if (target.Type != TargetType.Actor)
+ if (target.Type != TargetType.Actor
+ || target.Actor.TraitsImplementing().SelectMany(t => t.TargetTypes).Intersect(Info.Types).Any())
return;
if (!order.Queued)
diff --git a/OpenRA.Mods.RA/Traits/TargetableSubmarine.cs b/OpenRA.Mods.RA/Traits/TargetableSubmarine.cs
index f90d1d79aa..c5aad54b27 100644
--- a/OpenRA.Mods.RA/Traits/TargetableSubmarine.cs
+++ b/OpenRA.Mods.RA/Traits/TargetableSubmarine.cs
@@ -34,8 +34,7 @@ namespace OpenRA.Mods.RA.Traits
{
get
{
- return IsTraitDisabled ? None
- : (cloak.Cloaked ? info.CloakedTargetTypes : info.TargetTypes);
+ return cloak.Cloaked ? info.CloakedTargetTypes : info.TargetTypes;
}
}
}