From ba4b5738d71f28d7a56f617b1197d3b7a101e69a Mon Sep 17 00:00:00 2001 From: reaperrr Date: Sun, 24 Mar 2019 16:26:14 +0100 Subject: [PATCH] Merge Rearm and Repair into Resupply activity Allows parallel rearming and repairing. --- OpenRA.Mods.Cnc/Activities/LayMines.cs | 3 +- .../Activities/Air/ResupplyAircraft.cs | 6 +- OpenRA.Mods.Common/Activities/Rearm.cs | 101 --------------- .../Activities/{Repair.cs => Resupply.cs} | 121 +++++++++++++----- OpenRA.Mods.Common/OpenRA.Mods.Common.csproj | 3 +- OpenRA.Mods.Common/Traits/Air/Aircraft.cs | 22 +--- .../BotModules/Squads/States/AirStates.cs | 4 +- OpenRA.Mods.Common/Traits/Repairable.cs | 4 +- OpenRA.Mods.Common/Traits/RepairableNear.cs | 14 +- 9 files changed, 109 insertions(+), 169 deletions(-) delete mode 100644 OpenRA.Mods.Common/Activities/Rearm.cs rename OpenRA.Mods.Common/Activities/{Repair.cs => Resupply.cs} (53%) diff --git a/OpenRA.Mods.Cnc/Activities/LayMines.cs b/OpenRA.Mods.Cnc/Activities/LayMines.cs index 9b3e7e63ac..222bae8604 100644 --- a/OpenRA.Mods.Cnc/Activities/LayMines.cs +++ b/OpenRA.Mods.Cnc/Activities/LayMines.cs @@ -61,8 +61,7 @@ namespace OpenRA.Mods.Cnc.Activities // Add a CloseEnough range of 512 to the Rearm/Repair activities in order to ensure that we're at the host actor QueueChild(self, new MoveAdjacentTo(self, Target.FromActor(rearmTarget)), true); QueueChild(self, movement.MoveTo(self.World.Map.CellContaining(rearmTarget.CenterPosition), rearmTarget)); - QueueChild(self, new Rearm(self, rearmTarget, new WDist(512))); - QueueChild(self, new Repair(self, rearmTarget, new WDist(512))); + QueueChild(self, new Resupply(self, rearmTarget, new WDist(512))); return this; } diff --git a/OpenRA.Mods.Common/Activities/Air/ResupplyAircraft.cs b/OpenRA.Mods.Common/Activities/Air/ResupplyAircraft.cs index a78e66a29d..66bf81eaf4 100644 --- a/OpenRA.Mods.Common/Activities/Air/ResupplyAircraft.cs +++ b/OpenRA.Mods.Common/Activities/Air/ResupplyAircraft.cs @@ -28,12 +28,8 @@ namespace OpenRA.Mods.Common.Activities if (host == null) return; - var resupplyActivities = aircraft.GetResupplyActivities(host).ToArray(); - if (resupplyActivities.Any()) - QueueChild(self, ActivityUtils.SequenceActivities(self, resupplyActivities)); - + QueueChild(self, new Resupply(self, host, WDist.Zero)); QueueChild(self, new AllowYieldingReservation(self)); - if (aircraft.Info.TakeOffOnResupply) QueueChild(self, new TakeOff(self, (a, b, c) => NextActivity == null && b.NextActivity == null)); } diff --git a/OpenRA.Mods.Common/Activities/Rearm.cs b/OpenRA.Mods.Common/Activities/Rearm.cs deleted file mode 100644 index 14cb7b963b..0000000000 --- a/OpenRA.Mods.Common/Activities/Rearm.cs +++ /dev/null @@ -1,101 +0,0 @@ -#region Copyright & License Information -/* - * Copyright 2007-2019 The OpenRA Developers (see AUTHORS) - * This file is part of OpenRA, which is free software. It is made - * available to you under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. For more - * information, see COPYING. - */ -#endregion - -using OpenRA.Activities; -using OpenRA.Mods.Common.Traits; -using OpenRA.Traits; - -namespace OpenRA.Mods.Common.Activities -{ - public class Rearm : Activity - { - readonly Target host; - readonly WDist closeEnough; - readonly Rearmable rearmable; - - public Rearm(Actor self, Actor host, WDist closeEnough) - { - this.host = Target.FromActor(host); - this.closeEnough = closeEnough; - rearmable = self.Trait(); - } - - protected override void OnFirstRun(Actor self) - { - // Reset the ReloadDelay to avoid any issues with early cancellation - // from previous reload attempts (explicit order, host building died, etc). - // HACK: this really shouldn't be managed from here - foreach (var pool in rearmable.RearmableAmmoPools) - pool.RemainingTicks = pool.Info.ReloadDelay; - - if (host.Type == TargetType.Invalid) - return; - - foreach (var notify in host.Actor.TraitsImplementing()) - notify.RearmingStarted(host.Actor, self); - } - - protected override void OnLastRun(Actor self) - { - if (host.Type == TargetType.Invalid) - return; - - foreach (var notify in host.Actor.TraitsImplementing()) - notify.RearmingFinished(host.Actor, self); - } - - protected override void OnActorDispose(Actor self) - { - // If the actor died (or will be disposed directly) this tick, Activity.TickOuter won't be ticked again, - // so we need to run OnLastRun directly (otherwise it would be skipped completely). - OnLastRun(self); - } - - public override Activity Tick(Actor self) - { - if (IsCanceling) - return NextActivity; - - if (host.Type == TargetType.Invalid) - return NextActivity; - - if (closeEnough.LengthSquared > 0 && !host.IsInRange(self.CenterPosition, closeEnough)) - return NextActivity; - - var complete = true; - foreach (var pool in rearmable.RearmableAmmoPools) - { - if (!pool.FullAmmo()) - { - Reload(self, host.Actor, pool); - complete = false; - } - } - - return complete ? NextActivity : this; - } - - void Reload(Actor self, Actor host, AmmoPool ammoPool) - { - if (--ammoPool.RemainingTicks <= 0) - { - foreach (var notify in host.TraitsImplementing()) - notify.Rearming(host, self); - - ammoPool.RemainingTicks = ammoPool.Info.ReloadDelay; - if (!string.IsNullOrEmpty(ammoPool.Info.RearmSound)) - Game.Sound.PlayToPlayer(SoundType.World, self.Owner, ammoPool.Info.RearmSound, self.CenterPosition); - - ammoPool.GiveAmmo(self, ammoPool.Info.ReloadCount); - } - } - } -} diff --git a/OpenRA.Mods.Common/Activities/Repair.cs b/OpenRA.Mods.Common/Activities/Resupply.cs similarity index 53% rename from OpenRA.Mods.Common/Activities/Repair.cs rename to OpenRA.Mods.Common/Activities/Resupply.cs index e23a21c1b3..a33d8564af 100644 --- a/OpenRA.Mods.Common/Activities/Repair.cs +++ b/OpenRA.Mods.Common/Activities/Resupply.cs @@ -17,48 +17,90 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Activities { - public class Repair : Activity + public class Resupply : Activity { readonly IHealth health; readonly RepairsUnits[] allRepairsUnits; readonly Target host; readonly WDist closeEnough; readonly Repairable repairable; + readonly RepairableNear repairableNear; + readonly Rearmable rearmable; int remainingTicks; - bool played = false; + bool played; + bool paused; + bool repairComplete; + bool rearmComplete; - public Repair(Actor self, Actor host, WDist closeEnough) + public Resupply(Actor self, Actor host, WDist closeEnough) { this.host = Target.FromActor(host); this.closeEnough = closeEnough; allRepairsUnits = host.TraitsImplementing().ToArray(); health = self.TraitOrDefault(); repairable = self.TraitOrDefault(); + repairableNear = self.TraitOrDefault(); + rearmable = self.TraitOrDefault(); + + repairComplete = health == null || health.DamageState == DamageState.Undamaged + || !allRepairsUnits.Any() + || ((repairable == null || !repairable.Info.RepairActors.Contains(host.Info.Name)) + && (repairableNear == null || !repairableNear.Info.RepairActors.Contains(host.Info.Name))); + + rearmComplete = rearmable == null || !rearmable.Info.RearmActors.Contains(host.Info.Name) || rearmable.RearmableAmmoPools.All(p => p.FullAmmo()); } protected override void OnFirstRun(Actor self) { - if (host.Actor.IsDead) + if (host.Type == TargetType.Invalid) return; - foreach (var depot in host.Actor.TraitsImplementing()) - depot.BeforeRepair(host.Actor, self); + if (!repairComplete) + foreach (var notifyRepair in host.Actor.TraitsImplementing()) + notifyRepair.BeforeRepair(host.Actor, self); + + if (!rearmComplete) + { + foreach (var notifyRearm in host.Actor.TraitsImplementing()) + notifyRearm.RearmingStarted(host.Actor, self); + + // Reset the ReloadDelay to avoid any issues with early cancellation + // from previous reload attempts (explicit order, host building died, etc). + // HACK: this really shouldn't be managed from here + foreach (var pool in rearmable.RearmableAmmoPools) + pool.RemainingTicks = pool.Info.ReloadDelay; + } } public override Activity Tick(Actor self) { if (IsCanceling) - { - if (remainingTicks-- == 0) - return NextActivity; + return NextActivity; - return this; - } + if (host.Type == TargetType.Invalid || health == null) + return NextActivity; + if (closeEnough.LengthSquared > 0 && !host.IsInRange(self.CenterPosition, closeEnough)) + return NextActivity; + + if (!repairComplete) + RepairTick(self); + + if (!rearmComplete) + RearmTick(self); + + if (repairComplete && rearmComplete) + return NextActivity; + + return this; + } + + void RepairTick(Actor self) + { // First active. RepairsUnits repairsUnits = null; - var paused = false; + paused = false; foreach (var r in allRepairsUnits) { if (!r.IsTraitDisabled) @@ -74,13 +116,12 @@ namespace OpenRA.Mods.Common.Activities } if (repairsUnits == null) - return paused ? this : NextActivity; + { + if (!paused) + repairComplete = true; - if (host.Type == TargetType.Invalid || health == null) - return NextActivity; - - if (closeEnough.LengthSquared > 0 && !host.IsInRange(self.CenterPosition, closeEnough)) - return NextActivity; + return; + } if (health.DamageState == DamageState.Undamaged) { @@ -93,7 +134,11 @@ namespace OpenRA.Mods.Common.Activities Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", repairsUnits.Info.FinishRepairingNotification, self.Owner.Faction.InternalName); - return NextActivity; + foreach (var notifyRepair in host.Actor.TraitsImplementing()) + notifyRepair.AfterRepair(host.Actor, self); + + repairComplete = true; + return; } if (remainingTicks == 0) @@ -114,7 +159,7 @@ namespace OpenRA.Mods.Common.Activities if (!self.Owner.PlayerActor.Trait().TakeCash(cost, true)) { remainingTicks = 1; - return this; + return; } self.InflictDamage(host.Actor, new Damage(-hpToRepair)); @@ -126,24 +171,38 @@ namespace OpenRA.Mods.Common.Activities } else --remainingTicks; - - return this; } - protected override void OnLastRun(Actor self) + void RearmTick(Actor self) { - if (host.Actor.IsDead) - return; + rearmComplete = true; + foreach (var pool in rearmable.RearmableAmmoPools) + { + if (!pool.FullAmmo()) + { + Reload(self, host.Actor, pool); + rearmComplete = false; + } + } - foreach (var depot in host.Actor.TraitsImplementing()) - depot.AfterRepair(host.Actor, self); + if (rearmComplete) + foreach (var notifyRearm in host.Actor.TraitsImplementing()) + notifyRearm.RearmingFinished(host.Actor, self); } - protected override void OnActorDispose(Actor self) + void Reload(Actor self, Actor host, AmmoPool ammoPool) { - // If the actor died (or will be disposed directly) this tick, Activity.TickOuter won't be ticked again, - // so we need to run OnLastRun directly (otherwise it would be skipped completely). - OnLastRun(self); + if (--ammoPool.RemainingTicks <= 0) + { + foreach (var notify in host.TraitsImplementing()) + notify.Rearming(host, self); + + ammoPool.RemainingTicks = ammoPool.Info.ReloadDelay; + if (!string.IsNullOrEmpty(ammoPool.Info.RearmSound)) + Game.Sound.PlayToPlayer(SoundType.World, self.Owner, ammoPool.Info.RearmSound, self.CenterPosition); + + ammoPool.GiveAmmo(self, ammoPool.Info.ReloadCount); + } } } } diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index 97c112b5e1..24f8c30dfa 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -100,9 +100,8 @@ - - + diff --git a/OpenRA.Mods.Common/Traits/Air/Aircraft.cs b/OpenRA.Mods.Common/Traits/Air/Aircraft.cs index dea900a9fa..1b0bf97a3e 100644 --- a/OpenRA.Mods.Common/Traits/Air/Aircraft.cs +++ b/OpenRA.Mods.Common/Traits/Air/Aircraft.cs @@ -541,16 +541,6 @@ namespace OpenRA.Mods.Common.Traits return Info.LandableTerrainTypes.Contains(type); } - public bool CanRearmAt(Actor host) - { - return rearmable != null && rearmable.Info.RearmActors.Contains(host.Info.Name) && rearmable.RearmableAmmoPools.Any(p => !p.FullAmmo()); - } - - public bool CanRepairAt(Actor host) - { - return repairable != null && repairable.Info.RepairActors.Contains(host.Info.Name) && self.GetDamageState() != DamageState.Undamaged; - } - bool IsBlockedBy(Actor self, Actor otherActor, Actor ignoreActor) { // We are not blocked by the actor we are ignoring. @@ -580,14 +570,14 @@ namespace OpenRA.Mods.Common.Traits return true; } - public virtual IEnumerable GetResupplyActivities(Actor a) + public bool CanRearmAt(Actor host) { - // The ResupplyAircraft activity guarantees that we're on the helipad/repair depot - if (CanRearmAt(a)) - yield return new Rearm(self, a, WDist.Zero); + return rearmable != null && rearmable.Info.RearmActors.Contains(host.Info.Name) && rearmable.RearmableAmmoPools.Any(p => !p.FullAmmo()); + } - if (CanRepairAt(a)) - yield return new Repair(self, a, WDist.Zero); + public bool CanRepairAt(Actor host) + { + return repairable != null && repairable.Info.RepairActors.Contains(host.Info.Name) && self.GetDamageState() != DamageState.Undamaged; } public void ModifyDeathActorInit(Actor self, TypeDictionary init) diff --git a/OpenRA.Mods.Common/Traits/BotModules/Squads/States/AirStates.cs b/OpenRA.Mods.Common/Traits/BotModules/Squads/States/AirStates.cs index d9d9310060..46424a0a32 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/Squads/States/AirStates.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/Squads/States/AirStates.cs @@ -140,7 +140,7 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads var activity = a.CurrentActivity; var type = activity.GetType(); - if (type == typeof(Rearm) || type == typeof(ResupplyAircraft)) + if (type == typeof(Resupply) || type == typeof(ResupplyAircraft)) return true; var next = activity.NextActivity; @@ -148,7 +148,7 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads return false; var nextType = next.GetType(); - if (nextType == typeof(Rearm) || nextType == typeof(ResupplyAircraft)) + if (nextType == typeof(Resupply) || nextType == typeof(ResupplyAircraft)) return true; return false; diff --git a/OpenRA.Mods.Common/Traits/Repairable.cs b/OpenRA.Mods.Common/Traits/Repairable.cs index 859d2efc81..f7269056ee 100644 --- a/OpenRA.Mods.Common/Traits/Repairable.cs +++ b/OpenRA.Mods.Common/Traits/Repairable.cs @@ -134,11 +134,9 @@ namespace OpenRA.Mods.Common.Traits // TODO: This is hacky, but almost every single component affected // will need to be rewritten anyway, so this is OK for now. self.QueueActivity(movement.MoveTo(self.World.Map.CellContaining(targetActor.CenterPosition), targetActor)); - if (CanRearmAt(targetActor) && CanRearm()) - self.QueueActivity(new Rearm(self, targetActor, new WDist(512))); // Add a CloseEnough range of 512 to ensure we're at the host actor - self.QueueActivity(new Repair(self, targetActor, new WDist(512))); + self.QueueActivity(new Resupply(self, targetActor, new WDist(512))); var rp = targetActor.TraitOrDefault(); if (rp != null) diff --git a/OpenRA.Mods.Common/Traits/RepairableNear.cs b/OpenRA.Mods.Common/Traits/RepairableNear.cs index 976394968a..d763da418a 100644 --- a/OpenRA.Mods.Common/Traits/RepairableNear.cs +++ b/OpenRA.Mods.Common/Traits/RepairableNear.cs @@ -31,14 +31,14 @@ namespace OpenRA.Mods.Common.Traits class RepairableNear : IIssueOrder, IResolveOrder, IOrderVoice { + public readonly RepairableNearInfo Info; readonly Actor self; - readonly RepairableNearInfo info; readonly IMove movement; public RepairableNear(Actor self, RepairableNearInfo info) { this.self = self; - this.info = info; + Info = info; movement = self.Trait(); } @@ -61,7 +61,7 @@ namespace OpenRA.Mods.Common.Traits bool CanRepairAt(Actor target) { - return info.RepairActors.Contains(target.Info.Name); + return Info.RepairActors.Contains(target.Info.Name); } bool ShouldRepair() @@ -71,7 +71,7 @@ namespace OpenRA.Mods.Common.Traits public string VoicePhraseForOrder(Actor self, Order order) { - return order.OrderString == "RepairNear" && ShouldRepair() ? info.Voice : null; + return order.OrderString == "RepairNear" && ShouldRepair() ? Info.Voice : null; } public void ResolveOrder(Actor self, Order order) @@ -87,8 +87,8 @@ namespace OpenRA.Mods.Common.Traits if (!order.Queued) self.CancelActivity(); - self.QueueActivity(movement.MoveWithinRange(order.Target, info.CloseEnough, targetLineColor: Color.Green)); - self.QueueActivity(new Repair(self, order.Target.Actor, info.CloseEnough)); + self.QueueActivity(movement.MoveWithinRange(order.Target, Info.CloseEnough, targetLineColor: Color.Green)); + self.QueueActivity(new Resupply(self, order.Target.Actor, Info.CloseEnough)); self.SetTargetLine(order.Target, Color.Green, false); } @@ -98,7 +98,7 @@ namespace OpenRA.Mods.Common.Traits var repairBuilding = self.World.ActorsWithTrait() .Where(a => !a.Actor.IsDead && a.Actor.IsInWorld && a.Actor.Owner.IsAlliedWith(self.Owner) && - info.RepairActors.Contains(a.Actor.Info.Name)) + Info.RepairActors.Contains(a.Actor.Info.Name)) .OrderBy(p => (self.Location - p.Actor.Location).LengthSquared); // Worst case FirstOrDefault() will return a TraitPair, which is OK.