diff --git a/OpenRA.Mods.Common/Traits/Air/Aircraft.cs b/OpenRA.Mods.Common/Traits/Air/Aircraft.cs index e28942a754..b3cc705132 100644 --- a/OpenRA.Mods.Common/Traits/Air/Aircraft.cs +++ b/OpenRA.Mods.Common/Traits/Air/Aircraft.cs @@ -171,8 +171,8 @@ namespace OpenRA.Mods.Common.Traits public readonly AircraftInfo Info; readonly Actor self; - RepairableInfo repairableInfo; - RearmableInfo rearmableInfo; + Repairable repairable; + Rearmable rearmable; ConditionManager conditionManager; IDisposable reservation; IEnumerable speedModifiers; @@ -228,8 +228,8 @@ namespace OpenRA.Mods.Common.Traits protected virtual void Created(Actor self) { - repairableInfo = self.Info.TraitInfoOrDefault(); - rearmableInfo = self.Info.TraitInfoOrDefault(); + repairable = self.TraitOrDefault(); + rearmable = self.TraitOrDefault(); conditionManager = self.TraitOrDefault(); speedModifiers = self.TraitsImplementing().ToArray().Select(sm => sm.GetSpeedModifier()); cachedPosition = self.CenterPosition; @@ -469,8 +469,8 @@ namespace OpenRA.Mods.Common.Traits if (self.AppearsHostileTo(a)) return false; - return (rearmableInfo != null && rearmableInfo.RearmActors.Contains(a.Info.Name)) - || (repairableInfo != null && repairableInfo.RepairActors.Contains(a.Info.Name)); + return (rearmable != null && rearmable.Info.RearmActors.Contains(a.Info.Name)) + || (repairable != null && repairable.Info.RepairActors.Contains(a.Info.Name)); } public int MovementSpeed @@ -503,14 +503,23 @@ 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; + } + public virtual IEnumerable GetResupplyActivities(Actor a) { - var name = a.Info.Name; - if (rearmableInfo != null && rearmableInfo.RearmActors.Contains(name)) + // The ResupplyAircraft activity guarantees that we're on the helipad/repair depot + if (CanRearmAt(a)) yield return new Rearm(self, a, WDist.Zero); - // The ResupplyAircraft activity guarantees that we're on the helipad - if (repairableInfo != null && repairableInfo.RepairActors.Contains(name)) + if (CanRepairAt(a)) yield return new Repair(self, a, WDist.Zero); } @@ -521,20 +530,44 @@ namespace OpenRA.Mods.Common.Traits void INotifyBecomingIdle.OnBecomingIdle(Actor self) { - if (Info.VTOL && Info.LandWhenIdle) + OnBecomingIdle(self); + } + + protected virtual void OnBecomingIdle(Actor self) + { + var atLandAltitude = self.World.Map.DistanceAboveTerrain(CenterPosition) == Info.LandAltitude; + + // Work-around to prevent players from accidentally canceling resupply by pressing 'Stop', + // by re-queueing ResupplyAircraft as long as resupply hasn't finished and aircraft is still on resupplier. + // TODO: Investigate moving this back to ResolveOrder's "Stop" handling, + // once conflicts with other traits' "Stop" orders have been fixed. + if (atLandAltitude) + { + var host = GetActorBelow(); + if (host != null && (CanRearmAt(host) || CanRepairAt(host))) + { + self.QueueActivity(new ResupplyAircraft(self)); + return; + } + } + + if (!atLandAltitude && Info.VTOL && Info.LandWhenIdle) { if (Info.TurnToLand) self.QueueActivity(new Turn(self, Info.InitialFacing)); self.QueueActivity(new HeliLand(self, true)); } - else if (!Info.CanHover && (Info.TakeOffOnResupply || ReservedActor == null || self.World.Map.DistanceAboveTerrain(CenterPosition) != Info.LandAltitude)) + else if (!Info.CanHover && !atLandAltitude) self.QueueActivity(new FlyCircle(self, -1, Info.IdleTurnSpeed > -1 ? Info.IdleTurnSpeed : TurnSpeed)); - - // Temporary HACK for the AutoCarryall special case (needs CanHover, but also HeliFlyCircle on idle). - // Will go away soon (in a separate PR) with the arrival of ActionsWhenIdle. + else if (atLandAltitude && (Info.TakeOffOnResupply || ReservedActor == null)) + self.QueueActivity(new TakeOff(self)); else if (Info.CanHover && self.Info.HasTraitInfo() && Info.IdleTurnSpeed > -1) + { + // Temporary HACK for the AutoCarryall special case (needs CanHover, but also HeliFlyCircle on idle). + // Will go away soon (in a separate PR) with the arrival of ActionsWhenIdle. self.QueueActivity(new HeliFlyCircle(self, Info.IdleTurnSpeed > -1 ? Info.IdleTurnSpeed : TurnSpeed)); + } } #region Implement IPositionable @@ -717,13 +750,13 @@ namespace OpenRA.Mods.Common.Traits Order IIssueDeployOrder.IssueDeployOrder(Actor self, bool queued) { - if (rearmableInfo == null || !rearmableInfo.RearmActors.Any()) + if (rearmable == null || !rearmable.Info.RearmActors.Any()) return null; return new Order("ReturnToBase", self, queued); } - bool IIssueDeployOrder.CanIssueDeployOrder(Actor self) { return rearmableInfo != null && rearmableInfo.RearmActors.Any(); } + bool IIssueDeployOrder.CanIssueDeployOrder(Actor self) { return rearmable != null && rearmable.Info.RearmActors.Any(); } public string VoicePhraseForOrder(Actor self, Order order) { @@ -742,7 +775,7 @@ namespace OpenRA.Mods.Common.Traits case "Stop": return Info.Voice; case "ReturnToBase": - return rearmableInfo != null && rearmableInfo.RearmActors.Any() ? Info.Voice : null; + return rearmable != null && rearmable.Info.RearmActors.Any() ? Info.Voice : null; default: return null; } } @@ -791,15 +824,15 @@ namespace OpenRA.Mods.Common.Traits else if (order.OrderString == "Stop") { self.CancelActivity(); + + // HACK: If the player accidentally pressed 'Stop', we don't want this to cancel reservation. + // If unreserving is actually desired despite an actor below, it should be triggered from OnBecomingIdle. if (GetActorBelow() != null) - { - self.QueueActivity(new ResupplyAircraft(self)); return; - } UnReserve(); } - else if (order.OrderString == "ReturnToBase" && rearmableInfo != null && rearmableInfo.RearmActors.Any()) + else if (order.OrderString == "ReturnToBase" && rearmable != null && rearmable.Info.RearmActors.Any()) { if (!order.Queued) UnReserve();