From 8f1d8a67cc99e720618723e90ea0e21ff97f90f3 Mon Sep 17 00:00:00 2001 From: reaperrr Date: Thu, 27 Sep 2018 17:50:35 +0200 Subject: [PATCH] Remove RearmBuildings from Aircraft and Minelayer In favor of using Rearmable trait. --- OpenRA.Mods.Cnc/Activities/LayMines.cs | 8 +- OpenRA.Mods.Cnc/Traits/Minelayer.cs | 3 +- OpenRA.Mods.Common/AI/HackyAI.cs | 20 ++-- OpenRA.Mods.Common/AI/States/AirStates.cs | 6 +- .../Activities/Air/FlyAttack.cs | 13 ++- .../Activities/Air/HeliAttack.cs | 8 +- .../Activities/Air/HeliReturnToBase.cs | 12 ++- .../Activities/Air/ReturnToBase.cs | 13 ++- OpenRA.Mods.Common/Activities/Rearm.cs | 8 +- OpenRA.Mods.Common/OpenRA.Mods.Common.csproj | 1 + OpenRA.Mods.Common/Traits/Air/Aircraft.cs | 17 ++- OpenRA.Mods.Common/Traits/Air/ReturnOnIdle.cs | 2 +- OpenRA.Mods.Common/Traits/AmmoPool.cs | 6 -- OpenRA.Mods.Common/Traits/Repairable.cs | 14 ++- .../Rules/20180923/AddRearmable.cs | 102 ++++++++++++++++++ OpenRA.Mods.Common/UpdateRules/UpdatePath.cs | 1 + mods/ra/rules/aircraft.yaml | 12 ++- mods/ra/rules/vehicles.yaml | 2 + mods/ts/rules/aircraft.yaml | 12 ++- 19 files changed, 191 insertions(+), 69 deletions(-) create mode 100644 OpenRA.Mods.Common/UpdateRules/Rules/20180923/AddRearmable.cs diff --git a/OpenRA.Mods.Cnc/Activities/LayMines.cs b/OpenRA.Mods.Cnc/Activities/LayMines.cs index 1329d495ac..2218412ade 100644 --- a/OpenRA.Mods.Cnc/Activities/LayMines.cs +++ b/OpenRA.Mods.Cnc/Activities/LayMines.cs @@ -27,7 +27,7 @@ namespace OpenRA.Mods.Cnc.Activities readonly MinelayerInfo info; readonly AmmoPool[] ammoPools; readonly IMove movement; - readonly HashSet rearmBuildings; + readonly RearmableInfo rearmableInfo; public LayMines(Actor self) { @@ -35,7 +35,7 @@ namespace OpenRA.Mods.Cnc.Activities info = self.Info.TraitInfo(); ammoPools = self.TraitsImplementing().ToArray(); movement = self.Trait(); - rearmBuildings = info.RearmBuildings; + rearmableInfo = self.Info.TraitInfoOrDefault(); } public override Activity Tick(Actor self) @@ -43,11 +43,11 @@ namespace OpenRA.Mods.Cnc.Activities if (IsCanceled) return NextActivity; - if (ammoPools != null && ammoPools.Any(p => p.Info.Name == info.AmmoPoolName && !p.HasAmmo())) + if (rearmableInfo != null && ammoPools.Any(p => p.Info.Name == info.AmmoPoolName && !p.HasAmmo())) { // Rearm (and possibly repair) at rearm building, then back out here to refill the minefield some more var rearmTarget = self.World.Actors.Where(a => self.Owner.Stances[a.Owner] == Stance.Ally - && rearmBuildings.Contains(a.Info.Name)) + && rearmableInfo.RearmActors.Contains(a.Info.Name)) .ClosestTo(self); if (rearmTarget == null) diff --git a/OpenRA.Mods.Cnc/Traits/Minelayer.cs b/OpenRA.Mods.Cnc/Traits/Minelayer.cs index 55e6316592..8eb9a9b44d 100644 --- a/OpenRA.Mods.Cnc/Traits/Minelayer.cs +++ b/OpenRA.Mods.Cnc/Traits/Minelayer.cs @@ -21,10 +21,9 @@ using OpenRA.Traits; namespace OpenRA.Mods.Cnc.Traits { - public class MinelayerInfo : ITraitInfo + public class MinelayerInfo : ITraitInfo, Requires { [ActorReference] public readonly string Mine = "minv"; - [ActorReference] public readonly HashSet RearmBuildings = new HashSet { "fix" }; public readonly string AmmoPoolName = "primary"; diff --git a/OpenRA.Mods.Common/AI/HackyAI.cs b/OpenRA.Mods.Common/AI/HackyAI.cs index faf9e02078..de5f514ceb 100644 --- a/OpenRA.Mods.Common/AI/HackyAI.cs +++ b/OpenRA.Mods.Common/AI/HackyAI.cs @@ -443,22 +443,22 @@ namespace OpenRA.Mods.Common.AI CountBuildingByCommonName(Info.BuildingCommonNames.Barracks, Player) == 0; } - // For mods like RA (number of building must match the number of aircraft) + // For mods like RA (number of RearmActors must match the number of aircraft) bool HasAdequateAirUnitReloadBuildings(ActorInfo actorInfo) { var aircraftInfo = actorInfo.TraitInfoOrDefault(); if (aircraftInfo == null) return true; - // If the aircraft has at least 1 AmmoPool and defines 1 or more RearmBuildings, check if we have enough of those - var hasAmmoPool = actorInfo.TraitInfos().Any(); - if (hasAmmoPool && aircraftInfo.RearmBuildings.Count > 0) - { - var countOwnAir = CountActorsWithTrait(actorInfo.Name, Player); - var countBuildings = aircraftInfo.RearmBuildings.Sum(b => CountActorsWithTrait(b, Player)); - if (countOwnAir >= countBuildings) - return false; - } + // If actor isn't Rearmable, it doesn't need a RearmActor to reload + var rearmableInfo = actorInfo.TraitInfoOrDefault(); + if (rearmableInfo == null) + return true; + + var countOwnAir = CountActorsWithTrait(actorInfo.Name, Player); + var countBuildings = rearmableInfo.RearmActors.Sum(b => CountActorsWithTrait(b, Player)); + if (countOwnAir >= countBuildings) + return false; return true; } diff --git a/OpenRA.Mods.Common/AI/States/AirStates.cs b/OpenRA.Mods.Common/AI/States/AirStates.cs index 7736af9781..d836644198 100644 --- a/OpenRA.Mods.Common/AI/States/AirStates.cs +++ b/OpenRA.Mods.Common/AI/States/AirStates.cs @@ -127,7 +127,11 @@ namespace OpenRA.Mods.Common.AI protected static bool ReloadsAutomatically(Actor a) { var ammoPools = a.TraitsImplementing(); - return ammoPools.All(x => x.AutoReloads); + var rearmable = a.TraitOrDefault(); + if (rearmable == null) + return true; + + return ammoPools.All(ap => !rearmable.Info.AmmoPools.Contains(ap.Info.Name)); } protected static bool IsRearm(Actor a) diff --git a/OpenRA.Mods.Common/Activities/Air/FlyAttack.cs b/OpenRA.Mods.Common/Activities/Air/FlyAttack.cs index 4cdf330409..b7ad68364c 100644 --- a/OpenRA.Mods.Common/Activities/Air/FlyAttack.cs +++ b/OpenRA.Mods.Common/Activities/Air/FlyAttack.cs @@ -21,17 +21,17 @@ namespace OpenRA.Mods.Common.Activities readonly Target target; readonly Aircraft aircraft; readonly AttackPlane attackPlane; + readonly Rearmable rearmable; - readonly bool autoReloads; int ticksUntilTurn; public FlyAttack(Actor self, Target target) { this.target = target; aircraft = self.Trait(); - attackPlane = self.TraitOrDefault(); + attackPlane = self.Trait(); + rearmable = self.TraitOrDefault(); ticksUntilTurn = attackPlane.AttackPlaneInfo.AttackTurnDelay; - autoReloads = self.TraitsImplementing().All(p => p.AutoReloads); } public override Activity Tick(Actor self) @@ -46,12 +46,11 @@ namespace OpenRA.Mods.Common.Activities if (!target.IsValidFor(self)) return NextActivity; - // If all valid weapons have depleted their ammo and RearmBuilding is defined, return to RearmBuilding to reload and then resume the activity - if (!autoReloads && aircraft.Info.RearmBuildings.Any() && attackPlane.Armaments.All(x => x.IsTraitPaused || !x.Weapon.IsValidAgainst(target, self.World, self))) + // If all valid weapons have depleted their ammo and Rearmable trait exists, return to RearmActor to reload and then resume the activity + if (rearmable != null && 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) - attackPlane.DoAttack(self, target); + attackPlane.DoAttack(self, target); if (ChildActivity == null) { diff --git a/OpenRA.Mods.Common/Activities/Air/HeliAttack.cs b/OpenRA.Mods.Common/Activities/Air/HeliAttack.cs index d7fd73aa9e..aa2db66d26 100644 --- a/OpenRA.Mods.Common/Activities/Air/HeliAttack.cs +++ b/OpenRA.Mods.Common/Activities/Air/HeliAttack.cs @@ -22,7 +22,7 @@ namespace OpenRA.Mods.Common.Activities readonly Aircraft aircraft; readonly AttackHeli attackHeli; readonly bool attackOnlyVisibleTargets; - readonly bool autoReloads; + readonly Rearmable rearmable; Target target; bool canHideUnderFog; @@ -47,7 +47,7 @@ namespace OpenRA.Mods.Common.Activities aircraft = self.Trait(); attackHeli = self.Trait(); this.attackOnlyVisibleTargets = attackOnlyVisibleTargets; - autoReloads = self.TraitsImplementing().All(p => p.AutoReloads); + rearmable = self.TraitOrDefault(); } public override Activity Tick(Actor self) @@ -74,8 +74,8 @@ namespace OpenRA.Mods.Common.Activities return new HeliFly(self, newTarget); } - // If all valid weapons have depleted their ammo and RearmBuilding is defined, return to RearmBuilding to reload and then resume the activity - if (!autoReloads && aircraft.Info.RearmBuildings.Any() && attackHeli.Armaments.All(x => x.IsTraitPaused || !x.Weapon.IsValidAgainst(target, self.World, self))) + // If all valid weapons have depleted their ammo and Rearmable trait exists, return to RearmActor to reload and then resume the activity + if (rearmable != null && attackHeli.Armaments.All(x => x.IsTraitPaused || !x.Weapon.IsValidAgainst(target, self.World, self))) return ActivityUtils.SequenceActivities(new HeliReturnToBase(self, aircraft.Info.AbortOnResupply), this); var dist = targetPos - pos; diff --git a/OpenRA.Mods.Common/Activities/Air/HeliReturnToBase.cs b/OpenRA.Mods.Common/Activities/Air/HeliReturnToBase.cs index 101a54902e..d66111b587 100644 --- a/OpenRA.Mods.Common/Activities/Air/HeliReturnToBase.cs +++ b/OpenRA.Mods.Common/Activities/Air/HeliReturnToBase.cs @@ -20,6 +20,7 @@ namespace OpenRA.Mods.Common.Activities { readonly Aircraft aircraft; readonly RepairableInfo repairableInfo; + readonly Rearmable rearmable; readonly bool alwaysLand; readonly bool abortOnResupply; Actor dest; @@ -28,6 +29,7 @@ namespace OpenRA.Mods.Common.Activities { aircraft = self.Trait(); repairableInfo = self.Info.TraitInfoOrDefault(); + rearmable = self.TraitOrDefault(); this.alwaysLand = alwaysLand; this.abortOnResupply = abortOnResupply; this.dest = dest; @@ -35,9 +37,11 @@ namespace OpenRA.Mods.Common.Activities public Actor ChooseResupplier(Actor self, bool unreservedOnly) { - var rearmBuildings = aircraft.Info.RearmBuildings; + if (rearmable == null) + return null; + return self.World.Actors.Where(a => a.Owner == self.Owner - && rearmBuildings.Contains(a.Info.Name) + && rearmable.Info.RearmActors.Contains(a.Info.Name) && (!unreservedOnly || !Reservable.IsReserved(a))) .ClosestTo(self); } @@ -119,8 +123,8 @@ namespace OpenRA.Mods.Common.Activities if (repairableInfo != null && repairableInfo.RepairBuildings.Contains(dest.Info.Name) && self.GetDamageState() != DamageState.Undamaged) return true; - return aircraft.Info.RearmBuildings.Contains(dest.Info.Name) && self.TraitsImplementing() - .Any(p => !p.AutoReloads && !p.FullAmmo()); + return rearmable != null && rearmable.Info.RearmActors.Contains(dest.Info.Name) + && rearmable.RearmableAmmoPools.Any(p => !p.FullAmmo()); } } } diff --git a/OpenRA.Mods.Common/Activities/Air/ReturnToBase.cs b/OpenRA.Mods.Common/Activities/Air/ReturnToBase.cs index c76089d837..896651e1f2 100644 --- a/OpenRA.Mods.Common/Activities/Air/ReturnToBase.cs +++ b/OpenRA.Mods.Common/Activities/Air/ReturnToBase.cs @@ -23,6 +23,7 @@ namespace OpenRA.Mods.Common.Activities readonly Aircraft aircraft; readonly AircraftInfo aircraftInfo; readonly RepairableInfo repairableInfo; + readonly Rearmable rearmable; readonly bool alwaysLand; readonly bool abortOnResupply; bool isCalculated; @@ -37,14 +38,18 @@ namespace OpenRA.Mods.Common.Activities aircraft = self.Trait(); aircraftInfo = self.Info.TraitInfo(); repairableInfo = self.Info.TraitInfoOrDefault(); + rearmable = self.TraitOrDefault(); } public static Actor ChooseResupplier(Actor self, bool unreservedOnly) { - var rearmBuildings = self.Info.TraitInfo().RearmBuildings; + var rearmInfo = self.Info.TraitInfoOrDefault(); + if (rearmInfo == null) + return null; + return self.World.ActorsHavingTrait() .Where(a => a.Owner == self.Owner - && rearmBuildings.Contains(a.Info.Name) + && rearmInfo.RearmActors.Contains(a.Info.Name) && (!unreservedOnly || !Reservable.IsReserved(a))) .ClosestTo(self); } @@ -109,8 +114,8 @@ namespace OpenRA.Mods.Common.Activities if (repairableInfo != null && repairableInfo.RepairBuildings.Contains(dest.Info.Name) && self.GetDamageState() != DamageState.Undamaged) return true; - return aircraftInfo.RearmBuildings.Contains(dest.Info.Name) && self.TraitsImplementing() - .Any(p => !p.AutoReloads && !p.FullAmmo()); + return rearmable != null && rearmable.Info.RearmActors.Contains(dest.Info.Name) + && rearmable.RearmableAmmoPools.Any(p => !p.FullAmmo()); } public override Activity Tick(Actor self) diff --git a/OpenRA.Mods.Common/Activities/Rearm.cs b/OpenRA.Mods.Common/Activities/Rearm.cs index e53b0efe2b..f930fff1eb 100644 --- a/OpenRA.Mods.Common/Activities/Rearm.cs +++ b/OpenRA.Mods.Common/Activities/Rearm.cs @@ -21,13 +21,13 @@ namespace OpenRA.Mods.Common.Activities { readonly Target host; readonly WDist closeEnough; - readonly AmmoPool[] ammoPools; + readonly Rearmable rearmable; public Rearm(Actor self, Actor host, WDist closeEnough) { this.host = Target.FromActor(host); this.closeEnough = closeEnough; - ammoPools = self.TraitsImplementing().Where(p => !p.AutoReloads).ToArray(); + rearmable = self.Trait(); } protected override void OnFirstRun(Actor self) @@ -35,7 +35,7 @@ namespace OpenRA.Mods.Common.Activities // 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 ammoPools) + foreach (var pool in rearmable.RearmableAmmoPools) pool.RemainingTicks = pool.Info.ReloadDelay; if (host.Type == TargetType.Invalid) @@ -73,7 +73,7 @@ namespace OpenRA.Mods.Common.Activities return NextActivity; var complete = true; - foreach (var pool in ammoPools) + foreach (var pool in rearmable.RearmableAmmoPools) { if (!pool.FullAmmo()) { diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index 5a75d1ec1a..d0b222df53 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -931,6 +931,7 @@ + diff --git a/OpenRA.Mods.Common/Traits/Air/Aircraft.cs b/OpenRA.Mods.Common/Traits/Air/Aircraft.cs index 0771aef519..7b07a4726d 100644 --- a/OpenRA.Mods.Common/Traits/Air/Aircraft.cs +++ b/OpenRA.Mods.Common/Traits/Air/Aircraft.cs @@ -36,9 +36,6 @@ namespace OpenRA.Mods.Common.Traits [Desc("The speed at which the aircraft is repulsed from other aircraft. Specify -1 for normal movement speed.")] public readonly int RepulsionSpeed = -1; - [ActorReference] - public readonly HashSet RearmBuildings = new HashSet { }; - public readonly int InitialFacing = 0; public readonly int TurnSpeed = 255; @@ -155,6 +152,7 @@ namespace OpenRA.Mods.Common.Traits readonly Actor self; RepairableInfo repairableInfo; + RearmableInfo rearmableInfo; ConditionManager conditionManager; IDisposable reservation; IEnumerable speedModifiers; @@ -211,6 +209,7 @@ namespace OpenRA.Mods.Common.Traits protected virtual void Created(Actor self) { repairableInfo = self.Info.TraitInfoOrDefault(); + rearmableInfo = self.Info.TraitInfoOrDefault(); conditionManager = self.TraitOrDefault(); speedModifiers = self.TraitsImplementing().ToArray().Select(sm => sm.GetSpeedModifier()); cachedPosition = self.CenterPosition; @@ -450,7 +449,7 @@ namespace OpenRA.Mods.Common.Traits if (self.AppearsHostileTo(a)) return false; - return Info.RearmBuildings.Contains(a.Info.Name) + return (rearmableInfo != null && rearmableInfo.RearmActors.Contains(a.Info.Name)) || (repairableInfo != null && repairableInfo.RepairBuildings.Contains(a.Info.Name)); } @@ -487,7 +486,7 @@ namespace OpenRA.Mods.Common.Traits public virtual IEnumerable GetResupplyActivities(Actor a) { var name = a.Info.Name; - if (Info.RearmBuildings.Contains(name)) + if (rearmableInfo != null && rearmableInfo.RearmActors.Contains(name)) yield return new Rearm(self, a, WDist.Zero); // The ResupplyAircraft activity guarantees that we're on the helipad @@ -669,13 +668,13 @@ namespace OpenRA.Mods.Common.Traits Order IIssueDeployOrder.IssueDeployOrder(Actor self, bool queued) { - if (!Info.RearmBuildings.Any()) + if (rearmableInfo == null || !rearmableInfo.RearmActors.Any()) return null; return new Order("ReturnToBase", self, queued); } - bool IIssueDeployOrder.CanIssueDeployOrder(Actor self) { return Info.RearmBuildings.Any(); } + bool IIssueDeployOrder.CanIssueDeployOrder(Actor self) { return rearmableInfo != null && rearmableInfo.RearmActors.Any(); } public string VoicePhraseForOrder(Actor self, Order order) { @@ -689,7 +688,7 @@ namespace OpenRA.Mods.Common.Traits case "Stop": return Info.Voice; case "ReturnToBase": - return Info.RearmBuildings.Any() ? Info.Voice : null; + return rearmableInfo != null && rearmableInfo.RearmActors.Any() ? Info.Voice : null; default: return null; } } @@ -782,7 +781,7 @@ namespace OpenRA.Mods.Common.Traits self.QueueActivity(new HeliLand(self, true)); } } - else if (order.OrderString == "ReturnToBase" && Info.RearmBuildings.Any()) + else if (order.OrderString == "ReturnToBase" && rearmableInfo != null && rearmableInfo.RearmActors.Any()) { UnReserve(); self.CancelActivity(); diff --git a/OpenRA.Mods.Common/Traits/Air/ReturnOnIdle.cs b/OpenRA.Mods.Common/Traits/Air/ReturnOnIdle.cs index 182232cf9c..c333ffcade 100644 --- a/OpenRA.Mods.Common/Traits/Air/ReturnOnIdle.cs +++ b/OpenRA.Mods.Common/Traits/Air/ReturnOnIdle.cs @@ -15,7 +15,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { - [Desc("Return to a player owned RearmBuildings. If none available, head back to base and circle over it.")] + [Desc("Return to a player owned RearmActor. If none available, head back to base and circle over it.")] public class ReturnOnIdleInfo : ITraitInfo, Requires { public object Create(ActorInitializer init) { return new ReturnOnIdle(init.Self, this); } diff --git a/OpenRA.Mods.Common/Traits/AmmoPool.cs b/OpenRA.Mods.Common/Traits/AmmoPool.cs index af6d99d864..9134f67928 100644 --- a/OpenRA.Mods.Common/Traits/AmmoPool.cs +++ b/OpenRA.Mods.Common/Traits/AmmoPool.cs @@ -97,15 +97,9 @@ namespace OpenRA.Mods.Common.Traits return true; } - // This mostly serves to avoid complicated ReloadAmmoPool look-ups in various other places. - // TODO: Investigate removing this when the Rearm activity is replaced with a condition-based solution. - public bool AutoReloads { get; private set; } - void INotifyCreated.Created(Actor self) { conditionManager = self.TraitOrDefault(); - AutoReloads = 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 diff --git a/OpenRA.Mods.Common/Traits/Repairable.cs b/OpenRA.Mods.Common/Traits/Repairable.cs index 30a0de52ff..f315aff5a4 100644 --- a/OpenRA.Mods.Common/Traits/Repairable.cs +++ b/OpenRA.Mods.Common/Traits/Repairable.cs @@ -32,19 +32,23 @@ namespace OpenRA.Mods.Common.Traits public virtual object Create(ActorInitializer init) { return new Repairable(init.Self, this); } } - class Repairable : IIssueOrder, IResolveOrder, IOrderVoice + class Repairable : IIssueOrder, IResolveOrder, IOrderVoice, INotifyCreated { public readonly RepairableInfo Info; readonly IHealth health; readonly IMove movement; - readonly AmmoPool[] ammoPools; + Rearmable rearmable; public Repairable(Actor self, RepairableInfo info) { Info = info; health = self.Trait(); movement = self.Trait(); - ammoPools = self.TraitsImplementing().ToArray(); + } + + void INotifyCreated.Created(Actor self) + { + rearmable = self.TraitOrDefault(); } public IEnumerable Orders @@ -70,7 +74,7 @@ namespace OpenRA.Mods.Common.Traits bool CanRearmAt(Actor target) { - return Info.RepairBuildings.Contains(target.Info.Name); + return rearmable != null && rearmable.Info.RearmActors.Contains(target.Info.Name); } bool CanRepair() @@ -80,7 +84,7 @@ namespace OpenRA.Mods.Common.Traits bool CanRearm() { - return ammoPools.Any(x => !x.AutoReloads && !x.FullAmmo()); + return rearmable != null && rearmable.RearmableAmmoPools.Any(p => !p.FullAmmo()); } public string VoicePhraseForOrder(Actor self, Order order) diff --git a/OpenRA.Mods.Common/UpdateRules/Rules/20180923/AddRearmable.cs b/OpenRA.Mods.Common/UpdateRules/Rules/20180923/AddRearmable.cs new file mode 100644 index 0000000000..5eeae40407 --- /dev/null +++ b/OpenRA.Mods.Common/UpdateRules/Rules/20180923/AddRearmable.cs @@ -0,0 +1,102 @@ +#region Copyright & License Information +/* + * Copyright 2007-2018 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 System.Collections.Generic; + +namespace OpenRA.Mods.Common.UpdateRules.Rules +{ + public class AddRearmable : UpdateRule + { + public override string Name { get { return "Added Rearmable trait and move RearmBuildings properties there"; } } + public override string Description + { + get + { + return "Added Rearmable trait and replaced Aircraft.RearmBuildings and\n" + + "Minelayer.RearmBuildings with Rearmable.RearmActors."; + } + } + + public override IEnumerable UpdateActorNode(ModData modData, MiniYamlNode actorNode) + { + var aircraftNodes = actorNode.ChildrenMatching("Aircraft"); + var minelayerNodes = actorNode.ChildrenMatching("Minelayer"); + var ammoPoolNodes = actorNode.ChildrenMatching("AmmoPool"); + var addNodes = new List(); + + var ammoPoolNames = new List() { "primary" }; + foreach (var ap in ammoPoolNodes) + { + var poolName = ap.LastChildMatching("Name"); + if (poolName != null && poolName.NodeValue() != "primary") + ammoPoolNames.Add(poolName.NodeValue()); + } + + var rearmableAdded = false; + foreach (var aircraftNode in aircraftNodes) + { + var rearmBuildings = aircraftNode.LastChildMatching("RearmBuildings"); + if (rearmBuildings != null) + { + if (!rearmableAdded) + { + var rearmableNode = new MiniYamlNode("Rearmable", ""); + rearmBuildings.MoveAndRenameNode(aircraftNode, rearmableNode, "RearmActors"); + + // If the list has more than one entry, at least one of them won't be "primary" + if (ammoPoolNames.Count > 1) + { + var ammoPools = new MiniYamlNode("AmmoPools", string.Join(", ", ammoPoolNames)); + rearmableNode.AddNode(ammoPools); + } + + addNodes.Add(rearmableNode); + rearmableAdded = true; + } + else + aircraftNode.RemoveNodes("RearmBuildings"); + } + } + + // If it's a minelayer, it won't be an aircraft and rearmableAdded should still be false, so we can use it here + foreach (var minelayerNode in minelayerNodes) + { + var rearmableNode = new MiniYamlNode("Rearmable", ""); + + var rearmBuildings = minelayerNode.LastChildMatching("RearmBuildings"); + if (!rearmableAdded) + { + if (rearmBuildings != null) + rearmBuildings.MoveAndRenameNode(minelayerNode, rearmableNode, "RearmActors"); + else + rearmableNode.AddNode(new MiniYamlNode("RearmActors", "fix")); + + // If the list has more than one entry, at least one of them won't be "primary" + if (ammoPoolNames.Count > 1) + { + var ammoPools = new MiniYamlNode("AmmoPools", string.Join(", ", ammoPoolNames)); + rearmableNode.AddNode(ammoPools); + } + + addNodes.Add(rearmableNode); + rearmableAdded = true; + } + else if (rearmableAdded && rearmBuildings != null) + minelayerNode.RemoveNodes("RearmBuildings"); + } + + foreach (var node in addNodes) + actorNode.AddNode(node); + + yield break; + } + } +} diff --git a/OpenRA.Mods.Common/UpdateRules/UpdatePath.cs b/OpenRA.Mods.Common/UpdateRules/UpdatePath.cs index eb0d92de4c..5ea2410c4b 100644 --- a/OpenRA.Mods.Common/UpdateRules/UpdatePath.cs +++ b/OpenRA.Mods.Common/UpdateRules/UpdatePath.cs @@ -100,6 +100,7 @@ namespace OpenRA.Mods.Common.UpdateRules new RemoveHealthPercentageRing(), new RenameCrateActionNotification(), new RemoveRepairBuildingsFromAircraft(), + new AddRearmable(), }) }; diff --git a/mods/ra/rules/aircraft.yaml b/mods/ra/rules/aircraft.yaml index 6ee7edefb3..c1bc42b930 100644 --- a/mods/ra/rules/aircraft.yaml +++ b/mods/ra/rules/aircraft.yaml @@ -109,7 +109,6 @@ MIG: AttackPlane: FacingTolerance: 20 Aircraft: - RearmBuildings: afld, afld.ukraine CruiseAltitude: 2560 InitialFacing: 192 TurnSpeed: 4 @@ -138,6 +137,8 @@ MIG: Interval: 2 ProducibleWithLevel: Prerequisites: aircraft.upgraded + Rearmable: + RearmActors: afld, afld.ukraine YAK: Inherits: ^Plane @@ -175,7 +176,6 @@ YAK: AttackPlane: FacingTolerance: 20 Aircraft: - RearmBuildings: afld, afld.ukraine CruiseAltitude: 2560 InitialFacing: 192 TurnSpeed: 4 @@ -204,6 +204,8 @@ YAK: Prerequisites: aircraft.upgraded Selectable: DecorationBounds: 30,28,0,2 + Rearmable: + RearmActors: afld, afld.ukraine TRAN: Inherits: ^Helicopter @@ -292,7 +294,6 @@ HELI: AttackHeli: FacingTolerance: 20 Aircraft: - RearmBuildings: hpad LandWhenIdle: false InitialFacing: 224 TurnSpeed: 4 @@ -320,6 +321,8 @@ HELI: Prerequisites: aircraft.upgraded Selectable: DecorationBounds: 36,28 + Rearmable: + RearmActors: hpad HIND: Inherits: ^Helicopter @@ -357,7 +360,6 @@ HIND: AttackHeli: FacingTolerance: 20 Aircraft: - RearmBuildings: hpad LandWhenIdle: false InitialFacing: 224 TurnSpeed: 4 @@ -386,6 +388,8 @@ HIND: Prerequisites: aircraft.upgraded Selectable: DecorationBounds: 38,32 + Rearmable: + RearmActors: hpad U2: Inherits: ^NeutralPlane diff --git a/mods/ra/rules/vehicles.yaml b/mods/ra/rules/vehicles.yaml index 89d4a7bc75..c7ded51a68 100644 --- a/mods/ra/rules/vehicles.yaml +++ b/mods/ra/rules/vehicles.yaml @@ -492,6 +492,8 @@ MNLY: Weapon: ATMine RenderSprites: Image: MNLY + Rearmable: + RearmActors: fix TRUK: Inherits: ^Vehicle diff --git a/mods/ts/rules/aircraft.yaml b/mods/ts/rules/aircraft.yaml index 2c6877e233..0b7f3609f5 100644 --- a/mods/ts/rules/aircraft.yaml +++ b/mods/ts/rules/aircraft.yaml @@ -80,7 +80,6 @@ ORCA: Selectable: Bounds: 30,24 Aircraft: - RearmBuildings: gahpad, nahpad TurnSpeed: 5 Speed: 186 MoveIntoShroud: false @@ -109,6 +108,8 @@ ORCA: RenderSprites: SpawnActorOnDeath: Actor: ORCA.Husk + Rearmable: + RearmActors: gahpad, nahpad ORCAB: Inherits: ^Aircraft @@ -127,7 +128,6 @@ ORCAB: Selectable: Bounds: 30,24 Aircraft: - RearmBuildings: gahpad, nahpad CruiseAltitude: 3072 MaximumPitch: 120 TurnSpeed: 3 @@ -164,6 +164,8 @@ ORCAB: RequiresCondition: cruising SpawnActorOnDeath: Actor: ORCAB.Husk + Rearmable: + RearmActors: gahpad, nahpad ORCATRAN: Inherits: ^Helicopter @@ -257,7 +259,6 @@ SCRIN: Voiced: VoiceSet: Scrin Aircraft: - RearmBuildings: gahpad, nahpad CruiseAltitude: 2560 MaximumPitch: 90 TurnSpeed: 3 @@ -292,6 +293,8 @@ SCRIN: DeathSounds: SpawnActorOnDeath: Actor: SCRIN.Husk + Rearmable: + RearmActors: gahpad, nahpad APACHE: Inherits: ^Helicopter @@ -310,7 +313,6 @@ APACHE: Selectable: Bounds: 30,24 Aircraft: - RearmBuildings: gahpad, nahpad TurnSpeed: 5 Speed: 130 MoveIntoShroud: false @@ -345,6 +347,8 @@ APACHE: RenderSprites: SpawnActorOnDeath: Actor: APACHE.Husk + Rearmable: + RearmActors: gahpad, nahpad HUNTER: Inherits@2: ^ExistsInWorld