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; diff --git a/OpenRA.Mods.Common/Traits/Buildings/RepairableBuilding.cs b/OpenRA.Mods.Common/Traits/Buildings/RepairableBuilding.cs index 8f5028f11b..4718b7ba87 100644 --- a/OpenRA.Mods.Common/Traits/Buildings/RepairableBuilding.cs +++ b/OpenRA.Mods.Common/Traits/Buildings/RepairableBuilding.cs @@ -34,15 +34,18 @@ namespace OpenRA.Mods.Common.Traits { [Sync] public int RepairersHash { get { return Repairers.Aggregate(0, (code, player) => code ^ Sync.HashPlayer(player)); } } - public List Repairers = new List(); + public readonly List Repairers = new List(); readonly Health health; public bool RepairActive = false; + readonly Predicate isNotActiveAlly; + public RepairableBuilding(Actor self, RepairableBuildingInfo info) : base(info) { health = self.Trait(); + isNotActiveAlly = player => player.WinState != WinState.Undefined || player.Stances[self.Owner] != Stance.Ally; } public void RepairBuilding(Actor self, Player player) @@ -81,8 +84,7 @@ namespace OpenRA.Mods.Common.Traits if (remainingTicks == 0) { - Repairers = Repairers.Where(player => player.WinState == WinState.Undefined - && player.Stances[self.Owner] == Stance.Ally).ToList(); + Repairers.RemoveAll(isNotActiveAlly); // If after the previous operation there's no repairers left, stop if (!Repairers.Any()) return;