From 8b7a71685c36b8c96aaa2dedfc201d17eb698c40 Mon Sep 17 00:00:00 2001 From: reaperrr Date: Sun, 15 Oct 2017 18:28:33 +0200 Subject: [PATCH] Remove AmmoPool-awareness from Armament --- OpenRA.Mods.Cnc/Activities/LayMines.cs | 2 +- .../Activities/Air/FlyAttack.cs | 6 ++- .../Activities/Air/HeliAttack.cs | 6 ++- .../Properties/AmmoPoolProperties.cs | 12 +---- OpenRA.Mods.Common/Traits/AmmoPool.cs | 47 +++++++++++++------ OpenRA.Mods.Common/Traits/Armament.cs | 14 ++---- .../Traits/Attack/AttackBase.cs | 30 +++++++----- 7 files changed, 66 insertions(+), 51 deletions(-) diff --git a/OpenRA.Mods.Cnc/Activities/LayMines.cs b/OpenRA.Mods.Cnc/Activities/LayMines.cs index d7bfcb01c5..d5675d4b56 100644 --- a/OpenRA.Mods.Cnc/Activities/LayMines.cs +++ b/OpenRA.Mods.Cnc/Activities/LayMines.cs @@ -96,7 +96,7 @@ namespace OpenRA.Mods.Cnc.Activities var pool = ammoPools.FirstOrDefault(x => x.Info.Name == info.AmmoPoolName); if (pool == null) return; - pool.TakeAmmo(); + pool.TakeAmmo(self, 1); } self.World.AddFrameEndTask( diff --git a/OpenRA.Mods.Common/Activities/Air/FlyAttack.cs b/OpenRA.Mods.Common/Activities/Air/FlyAttack.cs index 00855c554c..43accce195 100644 --- a/OpenRA.Mods.Common/Activities/Air/FlyAttack.cs +++ b/OpenRA.Mods.Common/Activities/Air/FlyAttack.cs @@ -22,6 +22,7 @@ namespace OpenRA.Mods.Common.Activities readonly Aircraft aircraft; readonly AttackPlane attackPlane; + readonly bool selfReloads; int ticksUntilTurn; public FlyAttack(Actor self, Target target) @@ -30,6 +31,7 @@ namespace OpenRA.Mods.Common.Activities aircraft = self.Trait(); attackPlane = self.TraitOrDefault(); ticksUntilTurn = attackPlane.AttackPlaneInfo.AttackTurnDelay; + selfReloads = self.TraitsImplementing().All(p => p.SelfReloads); } public override Activity Tick(Actor self) @@ -44,8 +46,8 @@ namespace OpenRA.Mods.Common.Activities if (!target.IsValidFor(self)) return NextActivity; - // If all valid weapons have depleted their ammo, return to RearmBuilding to reload and then resume the activity - if (attackPlane.Armaments.All(x => x.OutOfAmmo || !x.Weapon.IsValidAgainst(target, self.World, self))) + // If all valid weapons have depleted their ammo and RearmBuilding is defined, return to RearmBuilding to reload and then resume the activity + if (!selfReloads && aircraft.Info.RearmBuildings.Any() && attackPlane.Armaments.All(x => x.IsTraitPaused || !x.Weapon.IsValidAgainst(target, self.World, self))) return ActivityUtils.SequenceActivities(new ReturnToBase(self, aircraft.Info.AbortOnResupply), this); if (attackPlane != null) diff --git a/OpenRA.Mods.Common/Activities/Air/HeliAttack.cs b/OpenRA.Mods.Common/Activities/Air/HeliAttack.cs index 044cf337c5..a67a60f043 100644 --- a/OpenRA.Mods.Common/Activities/Air/HeliAttack.cs +++ b/OpenRA.Mods.Common/Activities/Air/HeliAttack.cs @@ -22,6 +22,7 @@ namespace OpenRA.Mods.Common.Activities readonly Aircraft helicopter; readonly AttackHeli attackHeli; readonly bool attackOnlyVisibleTargets; + readonly bool selfReloads; Target target; bool canHideUnderFog; @@ -46,6 +47,7 @@ namespace OpenRA.Mods.Common.Activities helicopter = self.Trait(); attackHeli = self.Trait(); this.attackOnlyVisibleTargets = attackOnlyVisibleTargets; + selfReloads = self.TraitsImplementing().All(p => p.SelfReloads); } public override Activity Tick(Actor self) @@ -72,8 +74,8 @@ namespace OpenRA.Mods.Common.Activities return new HeliFly(self, newTarget); } - // If all valid weapons have depleted their ammo, return to RearmBuilding to reload and then resume the activity - if (attackHeli.Armaments.All(x => x.OutOfAmmo || !x.Weapon.IsValidAgainst(target, self.World, self))) + // If all valid weapons have depleted their ammo and RearmBuilding is defined, return to RearmBuilding to reload and then resume the activity + if (!selfReloads && helicopter.Info.RearmBuildings.Any() && attackHeli.Armaments.All(x => x.IsTraitPaused || !x.Weapon.IsValidAgainst(target, self.World, self))) return ActivityUtils.SequenceActivities(new HeliReturnToBase(self, helicopter.Info.AbortOnResupply), this); var dist = targetPos - pos; diff --git a/OpenRA.Mods.Common/Scripting/Properties/AmmoPoolProperties.cs b/OpenRA.Mods.Common/Scripting/Properties/AmmoPoolProperties.cs index 1968de22d7..17ea14eff3 100644 --- a/OpenRA.Mods.Common/Scripting/Properties/AmmoPoolProperties.cs +++ b/OpenRA.Mods.Common/Scripting/Properties/AmmoPoolProperties.cs @@ -59,17 +59,9 @@ namespace OpenRA.Mods.Common.Scripting throw new LuaException("Invalid ammopool name {0} queried on actor {1}.".F(poolName, self)); if (amount > 0) - { - while (amount-- > 0) - if (!pool.GiveAmmo()) - return; - } + pool.GiveAmmo(self, amount); else - { - while (amount++ < 0) - if (!pool.TakeAmmo()) - return; - } + pool.TakeAmmo(self, -amount); } } } diff --git a/OpenRA.Mods.Common/Traits/AmmoPool.cs b/OpenRA.Mods.Common/Traits/AmmoPool.cs index 6eccc849b0..76f0be4080 100644 --- a/OpenRA.Mods.Common/Traits/AmmoPool.cs +++ b/OpenRA.Mods.Common/Traits/AmmoPool.cs @@ -18,9 +18,12 @@ namespace OpenRA.Mods.Common.Traits [Desc("Actor has a limited amount of ammo, after using it all the actor must reload in some way.")] public class AmmoPoolInfo : ITraitInfo { - [Desc("Name of this ammo pool, used to link armaments and reload traits to this pool.")] + [Desc("Name of this ammo pool, used to link reload traits to this pool.")] public readonly string Name = "primary"; + [Desc("Name(s) of armament(s) that use this pool.")] + public readonly string[] Armaments = { "primary", "secondary" }; + [Desc("How much ammo does this pool contain when fully loaded.")] public readonly int Ammo = 1; @@ -46,6 +49,10 @@ namespace OpenRA.Mods.Common.Traits [Desc("Time to reload per ReloadCount on airfield etc.")] public readonly int ReloadDelay = 50; + [GrantedConditionReference] + [Desc("The condition to grant to self if the pool has any ammo.")] + public readonly string AmmoCondition = null; + public object Create(ActorInitializer init) { return new AmmoPool(init.Self, this); } } @@ -53,7 +60,7 @@ namespace OpenRA.Mods.Common.Traits { public readonly AmmoPoolInfo Info; ConditionManager conditionManager; - int emptyToken = ConditionManager.InvalidConditionToken; + int token = ConditionManager.InvalidConditionToken; bool selfReloads; @@ -64,31 +71,30 @@ namespace OpenRA.Mods.Common.Traits public AmmoPool(Actor self, AmmoPoolInfo info) { Info = info; - if (Info.InitialAmmo < Info.Ammo && Info.InitialAmmo >= 0) - currentAmmo = Info.InitialAmmo; - else - currentAmmo = Info.Ammo; + currentAmmo = Info.InitialAmmo < Info.Ammo && Info.InitialAmmo >= 0 ? Info.InitialAmmo : Info.Ammo; } public int GetAmmoCount() { return currentAmmo; } public bool FullAmmo() { return currentAmmo == Info.Ammo; } public bool HasAmmo() { return currentAmmo > 0; } - public bool GiveAmmo() + public bool GiveAmmo(Actor self, int count) { - if (currentAmmo >= Info.Ammo) + if (currentAmmo >= Info.Ammo || count < 0) return false; - ++currentAmmo; + currentAmmo = (currentAmmo + count).Clamp(0, Info.Ammo); + UpdateCondition(self); return true; } - public bool TakeAmmo() + public bool TakeAmmo(Actor self, int count) { - if (currentAmmo <= 0) + if (currentAmmo <= 0 || count < 0) return false; - --currentAmmo; + currentAmmo = (currentAmmo - count).Clamp(0, Info.Ammo); + UpdateCondition(self); return true; } @@ -100,6 +106,7 @@ namespace OpenRA.Mods.Common.Traits { conditionManager = self.TraitOrDefault(); selfReloads = self.TraitsImplementing().Any(r => r.Info.AmmoPool == Info.Name && r.Info.RequiresCondition == null); + UpdateCondition(self); // HACK: Temporarily needed until Rearm activity is gone for good RemainingTicks = Info.ReloadDelay; @@ -107,12 +114,24 @@ namespace OpenRA.Mods.Common.Traits void INotifyAttack.Attacking(Actor self, Target target, Armament a, Barrel barrel) { - if (a != null && a.Info.AmmoPoolName == Info.Name) - TakeAmmo(); + if (a != null && Info.Armaments.Contains(a.Info.Name)) + TakeAmmo(self, 1); } void INotifyAttack.PreparingAttack(Actor self, Target target, Armament a, Barrel barrel) { } + void UpdateCondition(Actor self) + { + if (conditionManager == null || string.IsNullOrEmpty(Info.AmmoCondition)) + return; + + if (HasAmmo() && token == ConditionManager.InvalidConditionToken) + token = conditionManager.GrantCondition(self, Info.AmmoCondition); + + if (!HasAmmo() && token != ConditionManager.InvalidConditionToken) + token = conditionManager.RevokeCondition(self, token); + } + public IEnumerable GetPips(Actor self) { var pips = Info.PipCount >= 0 ? Info.PipCount : Info.Ammo; diff --git a/OpenRA.Mods.Common/Traits/Armament.cs b/OpenRA.Mods.Common/Traits/Armament.cs index 34ee3e1d2a..0be6c383f5 100644 --- a/OpenRA.Mods.Common/Traits/Armament.cs +++ b/OpenRA.Mods.Common/Traits/Armament.cs @@ -25,7 +25,7 @@ namespace OpenRA.Mods.Common.Traits } [Desc("Allows you to attach weapons to the unit (use @IdentifierSuffix for > 1)")] - public class ArmamentInfo : ConditionalTraitInfo, Requires + public class ArmamentInfo : PausableConditionalTraitInfo, Requires { public readonly string Name = "primary"; @@ -33,9 +33,6 @@ namespace OpenRA.Mods.Common.Traits [Desc("Has to be defined in weapons.yaml as well.")] public readonly string Weapon = null; - [Desc("Which limited ammo pool (if present) should this armament be assigned to.")] - public readonly string AmmoPoolName = "primary"; - [Desc("Which turret (if present) should this armament be assigned to.")] public readonly string Turret = "primary"; @@ -99,15 +96,13 @@ namespace OpenRA.Mods.Common.Traits } } - public class Armament : ConditionalTrait, ITick, IExplodeModifier + public class Armament : PausableConditionalTrait, ITick, IExplodeModifier { public readonly WeaponInfo Weapon; public readonly Barrel[] Barrels; readonly Actor self; Turreted turret; - AmmoPool ammoPool; - ReloadAmmoPool reloadAmmoPool; BodyOrientation coords; INotifyBurstComplete[] notifyBurstComplete; INotifyAttack[] notifyAttacks; @@ -157,8 +152,6 @@ namespace OpenRA.Mods.Common.Traits protected override void Created(Actor self) { turret = self.TraitsImplementing().FirstOrDefault(t => t.Name == Info.Turret); - ammoPool = self.TraitsImplementing().FirstOrDefault(la => la.Info.Name == Info.AmmoPoolName); - reloadAmmoPool = self.TraitsImplementing().FirstOrDefault(ra => ra.Info.AmmoPool == Info.AmmoPoolName); coords = self.Trait(); notifyBurstComplete = self.TraitsImplementing().ToArray(); notifyAttacks = self.TraitsImplementing().ToArray(); @@ -211,7 +204,7 @@ namespace OpenRA.Mods.Common.Traits protected virtual bool CanFire(Actor self, Target target) { - if (IsReloading || (ammoPool != null && !ammoPool.HasAmmo())) + if (IsReloading || IsTraitPaused) return false; if (turret != null && !turret.HasAchievedDesiredFacing) @@ -341,7 +334,6 @@ namespace OpenRA.Mods.Common.Traits } } - public virtual bool OutOfAmmo { get { return ammoPool != null && !ammoPool.HasAmmo() && (reloadAmmoPool == null || reloadAmmoPool.IsTraitDisabled); } } public virtual bool IsReloading { get { return FireDelay > 0 || IsTraitDisabled; } } public virtual bool AllowExplode { get { return !IsReloading; } } bool IExplodeModifier.ShouldExplode(Actor self) { return AllowExplode; } diff --git a/OpenRA.Mods.Common/Traits/Attack/AttackBase.cs b/OpenRA.Mods.Common/Traits/Attack/AttackBase.cs index d524fa1880..12afa9ebee 100644 --- a/OpenRA.Mods.Common/Traits/Attack/AttackBase.cs +++ b/OpenRA.Mods.Common/Traits/Attack/AttackBase.cs @@ -177,7 +177,7 @@ namespace OpenRA.Mods.Common.Traits // PERF: Avoid LINQ. foreach (var armament in Armaments) { - var checkIsValid = checkForCenterTargetingWeapons ? armament.Weapon.TargetActorCenter : !armament.OutOfAmmo; + var checkIsValid = checkForCenterTargetingWeapons ? armament.Weapon.TargetActorCenter : !armament.IsTraitPaused; if (checkIsValid && !armament.IsTraitDisabled && armament.Weapon.IsValidAgainst(t, self.World, self)) return true; } @@ -202,7 +202,7 @@ namespace OpenRA.Mods.Common.Traits if (armament.IsTraitDisabled) continue; - if (armament.OutOfAmmo) + if (armament.IsTraitPaused) continue; var range = armament.Weapon.MinRange; @@ -225,7 +225,7 @@ namespace OpenRA.Mods.Common.Traits if (armament.IsTraitDisabled) continue; - if (armament.OutOfAmmo) + if (armament.IsTraitPaused) continue; var range = armament.MaxRange(); @@ -248,7 +248,7 @@ namespace OpenRA.Mods.Common.Traits if (armament.IsTraitDisabled) continue; - if (armament.OutOfAmmo) + if (armament.IsTraitPaused) continue; if (!armament.Weapon.IsValidAgainst(target, self.World, self)) @@ -267,25 +267,33 @@ namespace OpenRA.Mods.Common.Traits if (IsTraitDisabled) return WDist.Zero; - // PERF: Avoid LINQ. var max = WDist.Zero; + + // We want actors to use only weapons with ammo for this, except when ALL weapons are out of ammo, + // then we use the paused, valid weapon with highest range. + var maxFallback = WDist.Zero; + + // PERF: Avoid LINQ. foreach (var armament in Armaments) { if (armament.IsTraitDisabled) continue; - if (armament.OutOfAmmo) - continue; - if (!armament.Weapon.IsValidAgainst(target, self.World, self)) continue; var range = armament.MaxRange(); + if (maxFallback < range) + maxFallback = range; + + if (armament.IsTraitPaused) + continue; + if (max < range) max = range; } - return max; + return max != WDist.Zero ? max : maxFallback; } // Enumerates all armaments, that this actor possesses, that can be used against Target t @@ -392,7 +400,7 @@ namespace OpenRA.Mods.Common.Traits // Use valid armament with highest range out of those that have ammo // If all are out of ammo, just use valid armament with highest range armaments = armaments.OrderByDescending(x => x.MaxRange()); - var a = armaments.FirstOrDefault(x => !x.OutOfAmmo); + var a = armaments.FirstOrDefault(x => !x.IsTraitPaused); if (a == null) a = armaments.First(); @@ -426,7 +434,7 @@ namespace OpenRA.Mods.Common.Traits // Use valid armament with highest range out of those that have ammo // If all are out of ammo, just use valid armament with highest range armaments = armaments.OrderByDescending(x => x.MaxRange()); - var a = armaments.FirstOrDefault(x => !x.OutOfAmmo); + var a = armaments.FirstOrDefault(x => !x.IsTraitPaused); if (a == null) a = armaments.First();