diff --git a/OpenRA.Game/GameRules/Warhead.cs b/OpenRA.Game/GameRules/Warhead.cs index 25ee0e5e9e..7812bbf629 100644 --- a/OpenRA.Game/GameRules/Warhead.cs +++ b/OpenRA.Game/GameRules/Warhead.cs @@ -33,6 +33,18 @@ namespace OpenRA.GameRules [Desc("Delay in ticks before applying the warhead effect.", "0 = instant (old model).")] public readonly int Delay = 0; + HashSet validTargetSet; + HashSet invalidTargetSet; + + public bool IsValidTarget(IEnumerable targetTypes) + { + if (validTargetSet == null) + validTargetSet = new HashSet(ValidTargets); + if (invalidTargetSet == null) + invalidTargetSet = new HashSet(InvalidTargets); + return validTargetSet.Overlaps(targetTypes) && !invalidTargetSet.Overlaps(targetTypes); + } + /// Applies the warhead's effect against the target. public abstract void DoImpact(Target target, Actor firedBy, IEnumerable damageModifiers); @@ -52,8 +64,7 @@ namespace OpenRA.GameRules return false; var cellInfo = world.Map.GetTerrainInfo(cell); - if (!ValidTargets.Intersect(cellInfo.TargetTypes).Any() - || InvalidTargets.Intersect(cellInfo.TargetTypes).Any()) + if (!IsValidTarget(cellInfo.TargetTypes)) return false; return true; @@ -81,8 +92,7 @@ namespace OpenRA.GameRules // 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 || !ValidTargets.Intersect(targetable.TargetTypes).Any() - || InvalidTargets.Intersect(targetable.TargetTypes).Any()) + if (targetable == null || !IsValidTarget(targetable.TargetTypes)) return false; return true; @@ -101,8 +111,7 @@ namespace OpenRA.GameRules // 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 || !ValidTargets.Intersect(targetable.GetTargetTypes()).Any() - || InvalidTargets.Intersect(targetable.GetTargetTypes()).Any()) + if (targetable == null || !IsValidTarget(targetable.GetTargetTypes())) return false; return true; diff --git a/OpenRA.Game/GameRules/WeaponInfo.cs b/OpenRA.Game/GameRules/WeaponInfo.cs index 0c33926e05..83e96eae68 100644 --- a/OpenRA.Game/GameRules/WeaponInfo.cs +++ b/OpenRA.Game/GameRules/WeaponInfo.cs @@ -30,7 +30,7 @@ namespace OpenRA.GameRules public interface IProjectileInfo { IEffect Create(ProjectileArgs args); } - public class WeaponInfo + public sealed class WeaponInfo { [Desc("The maximum range the weapon can fire.")] public readonly WRange Range = WRange.Zero; @@ -64,9 +64,14 @@ namespace OpenRA.GameRules [FieldLoader.LoadUsing("LoadWarheads")] public readonly List Warheads = new List(); + readonly HashSet validTargetSet; + readonly HashSet invalidTargetSet; + public WeaponInfo(string name, MiniYaml content) { FieldLoader.Load(this, content); + validTargetSet = new HashSet(ValidTargets); + invalidTargetSet = new HashSet(InvalidTargets); } static object LoadProjectile(MiniYaml yaml) @@ -92,6 +97,11 @@ namespace OpenRA.GameRules return retList; } + public bool IsValidTarget(IEnumerable targetTypes) + { + return validTargetSet.Overlaps(targetTypes) && !invalidTargetSet.Overlaps(targetTypes); + } + /// Checks if the weapon is valid against (can target) the target. public bool IsValidAgainst(Target target, World world, Actor firedBy) { @@ -108,8 +118,7 @@ namespace OpenRA.GameRules return false; var cellInfo = world.Map.GetTerrainInfo(cell); - if (!ValidTargets.Intersect(cellInfo.TargetTypes).Any() - || InvalidTargets.Intersect(cellInfo.TargetTypes).Any()) + if (!IsValidTarget(cellInfo.TargetTypes)) return false; return true; @@ -122,8 +131,7 @@ namespace OpenRA.GameRules public bool IsValidAgainst(Actor victim, Actor firedBy) { var targetable = victim.TraitOrDefault(); - if (targetable == null || !ValidTargets.Intersect(targetable.TargetTypes).Any() - || InvalidTargets.Intersect(targetable.TargetTypes).Any()) + if (targetable == null || !IsValidTarget(targetable.TargetTypes)) return false; if (!Warheads.Any(w => w.IsValidAgainst(victim, firedBy))) @@ -136,8 +144,7 @@ namespace OpenRA.GameRules public bool IsValidAgainst(FrozenActor victim, Actor firedBy) { var targetable = victim.Info.Traits.GetOrDefault(); - if (targetable == null || !ValidTargets.Intersect(targetable.GetTargetTypes()).Any() - || InvalidTargets.Intersect(targetable.GetTargetTypes()).Any()) + if (targetable == null || !IsValidTarget(targetable.GetTargetTypes())) return false; if (!Warheads.Any(w => w.IsValidAgainst(victim, firedBy))) diff --git a/OpenRA.Mods.Common/AI/States/AirStates.cs b/OpenRA.Mods.Common/AI/States/AirStates.cs index c5d4e95258..095ee9f7f7 100644 --- a/OpenRA.Mods.Common/AI/States/AirStates.cs +++ b/OpenRA.Mods.Common/AI/States/AirStates.cs @@ -18,6 +18,8 @@ namespace OpenRA.Mods.Common.AI { abstract class AirStateBase : StateBase { + static readonly string[] AirTargetTypes = new[] { "Air" }; + protected const int MissileUnitMultiplier = 3; protected static int CountAntiAirUnits(IEnumerable units) @@ -34,7 +36,7 @@ namespace OpenRA.Mods.Common.AI var arms = unit.TraitsImplementing(); foreach (var a in arms) { - if (a.Weapon.ValidTargets.Contains("Air")) + if (a.Weapon.IsValidTarget(AirTargetTypes)) { missileUnitsCount++; break; diff --git a/OpenRA.Mods.Common/AI/States/StateBase.cs b/OpenRA.Mods.Common/AI/States/StateBase.cs index 2f5f07f8c8..a7fe882429 100644 --- a/OpenRA.Mods.Common/AI/States/StateBase.cs +++ b/OpenRA.Mods.Common/AI/States/StateBase.cs @@ -69,7 +69,7 @@ namespace OpenRA.Mods.Common.AI var arms = a.TraitsImplementing(); foreach (var arm in arms) - if (arm.Weapon.ValidTargets.Intersect(targetable.TargetTypes).Any()) + if (arm.Weapon.IsValidTarget(targetable.TargetTypes)) return true; return false;