diff --git a/OpenRA.Mods.Common/Activities/Resupply.cs b/OpenRA.Mods.Common/Activities/Resupply.cs index 16c3dabc70..e2e3ee83f7 100644 --- a/OpenRA.Mods.Common/Activities/Resupply.cs +++ b/OpenRA.Mods.Common/Activities/Resupply.cs @@ -10,9 +10,11 @@ #endregion using System; +using System.Collections.Generic; using System.Linq; using OpenRA.Activities; using OpenRA.Mods.Common.Traits; +using OpenRA.Primitives; using OpenRA.Traits; namespace OpenRA.Mods.Common.Activities @@ -27,9 +29,13 @@ namespace OpenRA.Mods.Common.Activities readonly RepairableNear repairableNear; readonly Rearmable rearmable; readonly INotifyResupply[] notifyResupplies; + readonly ICallForTransport[] transportCallers; + readonly IMove move; + readonly Aircraft aircraft; int remainingTicks; bool played; + bool actualResupplyStarted; ResupplyType activeResupplyTypes = ResupplyType.None; public Resupply(Actor self, Actor host, WDist closeEnough) @@ -42,6 +48,9 @@ namespace OpenRA.Mods.Common.Activities repairableNear = self.TraitOrDefault(); rearmable = self.TraitOrDefault(); notifyResupplies = host.TraitsImplementing().ToArray(); + transportCallers = self.TraitsImplementing().ToArray(); + move = self.Trait(); + aircraft = move as Aircraft; var cannotRepairAtHost = health == null || health.DamageState == DamageState.Undamaged || !allRepairsUnits.Any() @@ -56,23 +65,6 @@ namespace OpenRA.Mods.Common.Activities activeResupplyTypes |= ResupplyType.Rearm; } - protected override void OnFirstRun(Actor self) - { - if (host.Type == TargetType.Invalid) - return; - - if (activeResupplyTypes > 0) - foreach (var notifyResupply in notifyResupplies) - notifyResupply.BeforeResupply(host.Actor, self, activeResupplyTypes); - - // 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 - if (activeResupplyTypes.HasFlag(ResupplyType.Rearm)) - foreach (var pool in rearmable.RearmableAmmoPools) - pool.RemainingTicks = pool.Info.ReloadDelay; - } - public override bool Tick(Actor self) { // HACK: If the activity is cancelled while we're already resupplying (or about to start resupplying), @@ -83,7 +75,6 @@ namespace OpenRA.Mods.Common.Activities foreach (var notifyResupply in notifyResupplies) notifyResupply.ResupplyTick(host.Actor, self, ResupplyType.None); - var aircraft = self.TraitOrDefault(); if (aircraft != null) { aircraft.AllowYieldingReservation(); @@ -92,14 +83,15 @@ namespace OpenRA.Mods.Common.Activities return true; } - else if (self.Info.HasTraitInfo()) + else if (repairableNear != null) + return true; + else { - QueueChild(self.Trait().MoveToTarget(self, host)); + QueueChild(move.MoveToTarget(self, host)); return false; } } - else if (IsCanceling || host.Type == TargetType.Invalid - || (closeEnough.LengthSquared > 0 && !host.IsInRange(self.CenterPosition, closeEnough))) + else if (IsCanceling || host.Type != TargetType.Actor || !host.Actor.IsInWorld || host.Actor.IsDead) { // This is necessary to ensure host resupply actions (like animations) finish properly foreach (var notifyResupply in notifyResupplies) @@ -107,6 +99,50 @@ namespace OpenRA.Mods.Common.Activities return true; } + else if (activeResupplyTypes != 0 && aircraft == null && + (closeEnough.LengthSquared > 0 && !host.IsInRange(self.CenterPosition, closeEnough))) + { + var targetCell = self.World.Map.CellContaining(host.Actor.CenterPosition); + List movement = new List(); + + movement.Add(move.MoveWithinRange(host, closeEnough, targetLineColor: Color.Green)); + + // HACK: Repairable needs the actor to move to host center. + // TODO: Get rid of this or at least replace it with something less hacky. + if (repairableNear == null) + movement.Add(move.MoveTo(targetCell, host.Actor)); + + var moveActivities = ActivityUtils.SequenceActivities(movement.ToArray()); + + var delta = (self.CenterPosition - host.CenterPosition).LengthSquared; + var transport = transportCallers.FirstOrDefault(t => t.MinimumDistance.LengthSquared < delta); + if (transport != null) + { + QueueChild(new WaitForTransport(self, moveActivities)); + + // TODO: Make this compatible with RepairableNear + transport.RequestTransport(self, targetCell, new Resupply(self, host.Actor, closeEnough)); + } + else + QueueChild(moveActivities); + + return false; + } + + // We don't want to trigger this until we've reached the resupplier and can start resupplying + if (!actualResupplyStarted && activeResupplyTypes > 0) + { + actualResupplyStarted = true; + foreach (var notifyResupply in notifyResupplies) + notifyResupply.BeforeResupply(host.Actor, self, activeResupplyTypes); + + // 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 + if (activeResupplyTypes.HasFlag(ResupplyType.Rearm)) + foreach (var pool in rearmable.RearmableAmmoPools) + pool.RemainingTicks = pool.Info.ReloadDelay; + } if (activeResupplyTypes.HasFlag(ResupplyType.Repair)) RepairTick(self); @@ -119,10 +155,18 @@ namespace OpenRA.Mods.Common.Activities if (activeResupplyTypes == 0) { - var aircraft = self.TraitOrDefault(); if (aircraft != null) aircraft.AllowYieldingReservation(); + if (self.CurrentActivity.NextActivity == null) + { + var rp = host.Actor.TraitOrDefault(); + if (rp != null) + Queue(move.MoveTo(rp.Location, repairableNear != null ? null : host.Actor)); + else if (repairableNear == null) + Queue(move.MoveToTarget(self, host)); + } + return true; } diff --git a/OpenRA.Mods.Common/Traits/Repairable.cs b/OpenRA.Mods.Common/Traits/Repairable.cs index e399dd4e08..214e41ca41 100644 --- a/OpenRA.Mods.Common/Traits/Repairable.cs +++ b/OpenRA.Mods.Common/Traits/Repairable.cs @@ -44,20 +44,20 @@ namespace OpenRA.Mods.Common.Traits { public readonly RepairableInfo Info; readonly IHealth health; - readonly IMove movement; Rearmable rearmable; bool requireForceMove; + bool isAircraft; public Repairable(Actor self, RepairableInfo info) { Info = info; health = self.Trait(); - movement = self.Trait(); } void INotifyCreated.Created(Actor self) { rearmable = self.TraitOrDefault(); + isAircraft = self.Info.HasTraitInfo(); } public IEnumerable Orders @@ -106,58 +106,38 @@ namespace OpenRA.Mods.Common.Traits void IResolveOrder.ResolveOrder(Actor self, Order order) { - if (order.OrderString == "Repair") - { - // Repair orders are only valid for own/allied actors, - // which are guaranteed to never be frozen. - if (order.Target.Type != TargetType.Actor) - return; + if (order.OrderString != "Repair") + return; - // Aircraft handle Repair orders directly in the Aircraft trait - if (self.Info.HasTraitInfo()) - return; - - if (!CanRepairAt(order.Target.Actor) || (!CanRepair() && !CanRearm())) - return; - - if (!order.Queued) - self.CancelActivity(); - - self.SetTargetLine(order.Target, Color.Green); - var activities = ActivityUtils.SequenceActivities( - movement.MoveToTarget(self, order.Target, targetLineColor: Color.Green), - new CallFunc(() => AfterReachActivities(self, order, movement))); - - self.QueueActivity(new WaitForTransport(self, activities)); - TryCallTransport(self, order.Target, new CallFunc(() => AfterReachActivities(self, order, movement))); - } - } - - void AfterReachActivities(Actor self, Order order, IMove movement) - { + // Repair orders are only valid for own/allied actors, + // which are guaranteed to never be frozen. if (order.Target.Type != TargetType.Actor) return; - var targetActor = order.Target.Actor; - if (!targetActor.IsInWorld || targetActor.IsDead || targetActor.TraitsImplementing().All(r => r.IsTraitDisabled)) + // Aircraft handle Repair orders directly in the Aircraft trait + // TODO: Move the order handling of both this trait and Aircraft to a generalistic DockManager + if (isAircraft) return; - // 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 (!CanRepairAt(order.Target.Actor) || (!CanRepair() && !CanRearm())) + return; - // Add a CloseEnough range of 512 to ensure we're at the host actor - self.QueueActivity(new Resupply(self, targetActor, new WDist(512))); + if (!order.Queued) + self.CancelActivity(); - var rp = targetActor.TraitOrDefault(); - if (rp != null) - { - self.QueueActivity(new CallFunc(() => - { - self.SetTargetLine(Target.FromCell(self.World, rp.Location), Color.Green); - self.QueueActivity(movement.MoveTo(rp.Location, targetActor)); - })); - } + self.SetTargetLine(order.Target, Color.Green); + self.QueueActivity(new Resupply(self, order.Target.Actor, new WDist(512))); + } + + IEnumerable IObservesVariables.GetVariableObservers() + { + if (Info.RequireForceMoveCondition != null) + yield return new VariableObserver(RequireForceMoveConditionChanged, Info.RequireForceMoveCondition.Variables); + } + + void RequireForceMoveConditionChanged(Actor self, IReadOnlyDictionary conditions) + { + requireForceMove = Info.RequireForceMoveCondition.Evaluate(conditions); } public Actor FindRepairBuilding(Actor self) @@ -172,27 +152,5 @@ namespace OpenRA.Mods.Common.Traits // Worst case FirstOrDefault() will return a TraitPair, which is OK. return repairBuilding.FirstOrDefault().Actor; } - - static void TryCallTransport(Actor self, Target target, Activity nextActivity) - { - var targetCell = self.World.Map.CellContaining(target.CenterPosition); - var delta = (self.CenterPosition - target.CenterPosition).LengthSquared; - var transports = self.TraitsImplementing() - .Where(t => t.MinimumDistance.LengthSquared < delta); - - foreach (var t in transports) - t.RequestTransport(self, targetCell, nextActivity); - } - - IEnumerable IObservesVariables.GetVariableObservers() - { - if (Info.RequireForceMoveCondition != null) - yield return new VariableObserver(RequireForceMoveConditionChanged, Info.RequireForceMoveCondition.Variables); - } - - void RequireForceMoveConditionChanged(Actor self, IReadOnlyDictionary conditions) - { - requireForceMove = Info.RequireForceMoveCondition.Evaluate(conditions); - } } } diff --git a/OpenRA.Mods.Common/Traits/RepairableNear.cs b/OpenRA.Mods.Common/Traits/RepairableNear.cs index 7b4dbcd1da..acf9a236fd 100644 --- a/OpenRA.Mods.Common/Traits/RepairableNear.cs +++ b/OpenRA.Mods.Common/Traits/RepairableNear.cs @@ -41,14 +41,12 @@ namespace OpenRA.Mods.Common.Traits { public readonly RepairableNearInfo Info; readonly Actor self; - readonly IMove movement; bool requireForceMove; public RepairableNear(Actor self, RepairableNearInfo info) { this.self = self; Info = info; - movement = self.Trait(); } public IEnumerable Orders @@ -104,10 +102,8 @@ namespace OpenRA.Mods.Common.Traits if (!order.Queued) self.CancelActivity(); - self.QueueActivity(movement.MoveWithinRange(order.Target, Info.CloseEnough, targetLineColor: Color.Green)); + self.SetTargetLine(order.Target, Color.Green); self.QueueActivity(new Resupply(self, order.Target.Actor, Info.CloseEnough)); - - self.SetTargetLine(order.Target, Color.Green, false); } public Actor FindRepairBuilding(Actor self)