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; } } }