From 54340591e32fda6c95c8f5bf4059f44f91a622bb Mon Sep 17 00:00:00 2001 From: Mustafa Alperen Seki Date: Mon, 31 Jan 2022 15:13:07 +0300 Subject: [PATCH] Unhardcode VeteranProductionIconOverlay. --- .../Traits/ProducibleWithLevel.cs | 3 +- ...lay.cs => ProductionIconOverlayManager.cs} | 47 +++++++++++------- .../Render/WithProductionIconOverlay.cs | 40 +++++++++++++++ .../UnhardcodeVeteranProductionIconOverlay.cs | 49 +++++++++++++++++++ OpenRA.Mods.Common/UpdateRules/UpdatePath.cs | 1 + mods/ra/rules/aircraft.yaml | 15 ++++++ mods/ra/rules/infantry.yaml | 27 ++++++++++ mods/ra/rules/player.yaml | 3 +- mods/ra/rules/vehicles.yaml | 36 ++++++++++++++ 9 files changed, 199 insertions(+), 22 deletions(-) rename OpenRA.Mods.Common/Traits/Render/{VeteranProductionIconOverlay.cs => ProductionIconOverlayManager.cs} (66%) create mode 100644 OpenRA.Mods.Common/Traits/Render/WithProductionIconOverlay.cs create mode 100644 OpenRA.Mods.Common/UpdateRules/Rules/20201213/UnhardcodeVeteranProductionIconOverlay.cs diff --git a/OpenRA.Mods.Common/Traits/ProducibleWithLevel.cs b/OpenRA.Mods.Common/Traits/ProducibleWithLevel.cs index 3147157f75..023e7b602f 100644 --- a/OpenRA.Mods.Common/Traits/ProducibleWithLevel.cs +++ b/OpenRA.Mods.Common/Traits/ProducibleWithLevel.cs @@ -15,8 +15,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { [Desc("Actors possessing this trait should define the GainsExperience trait. When the prerequisites are fulfilled, ", - "this trait grants a level-up to newly spawned actors. If additionally the actor's owning player defines the ProductionIconOverlay ", - "trait, the production queue icon renders with an overlay defined in that trait.")] + "this trait grants a level-up to newly spawned actors.")] public class ProducibleWithLevelInfo : TraitInfo, Requires { public readonly string[] Prerequisites = Array.Empty(); diff --git a/OpenRA.Mods.Common/Traits/Render/VeteranProductionIconOverlay.cs b/OpenRA.Mods.Common/Traits/Render/ProductionIconOverlayManager.cs similarity index 66% rename from OpenRA.Mods.Common/Traits/Render/VeteranProductionIconOverlay.cs rename to OpenRA.Mods.Common/Traits/Render/ProductionIconOverlayManager.cs index bac1da5429..c3821b4bec 100644 --- a/OpenRA.Mods.Common/Traits/Render/VeteranProductionIconOverlay.cs +++ b/OpenRA.Mods.Common/Traits/Render/ProductionIconOverlayManager.cs @@ -10,17 +10,20 @@ #endregion using System.Collections.Generic; +using System.Linq; using OpenRA.Graphics; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits.Render { [TraitLocation(SystemActors.Player)] - [Desc("Attach this to the player actor. When attached, enables all actors possessing the ProducibleWithLevel ", - "trait to have their production queue icons render with an overlay defined in this trait. ", - "The icon change occurs when ProducibleWithLevel.Prerequisites are met.")] - public class VeteranProductionIconOverlayInfo : TraitInfo, Requires + [Desc("Attach this to the player actor. Required for WithProductionIconOverlay trait on actors to work.")] + public class ProductionIconOverlayManagerInfo : TraitInfo, Requires, IRulesetLoaded { + [FieldLoader.Require] + [Desc("Type of the overlay. Prerequisites from WithProductionIconOverlay traits with matching types determine when this overlay will be enabled.")] + public readonly string Type = null; + [FieldLoader.Require] [Desc("Image used for the overlay.")] public readonly string Image = null; @@ -33,24 +36,30 @@ namespace OpenRA.Mods.Common.Traits.Render [Desc("Palette to render the sprite in. Reference the world actor's PaletteFrom* traits.")] public readonly string Palette = "chrome"; - public override object Create(ActorInitializer init) { return new VeteranProductionIconOverlay(init, this); } + public virtual void RulesetLoaded(Ruleset rules, ActorInfo ai) + { + if (rules.Actors[SystemActors.Player].TraitInfos().Where(piom => piom != this && piom.Type == Type).Any()) + throw new YamlException($"Multiple 'ProductionIconOverlayManager's with type '{Type}' exist."); + } + + public override object Create(ActorInitializer init) { return new ProductionIconOverlayManager(init, this); } } - public class VeteranProductionIconOverlay : ITechTreeElement, IProductionIconOverlay + public class ProductionIconOverlayManager : ITechTreeElement, IProductionIconOverlay { + readonly Actor self; + readonly Sprite sprite; + readonly ProductionIconOverlayManagerInfo info; + // HACK: TechTree doesn't associate Watcher.Key with the registering ITechTreeElement. // So in a situation where multiple ITechTreeElements register Watchers with the same Key, // and one removes its Watcher, all other ITechTreeElements' Watchers get removed too. // This makes sure that the keys are unique with respect to the registering ITechTreeElement. - const string Prefix = "ProductionIconOverlay."; - - readonly Actor self; - readonly Sprite sprite; - readonly VeteranProductionIconOverlayInfo info; + readonly string prefix; readonly Dictionary overlayActive = new Dictionary(); - public VeteranProductionIconOverlay(ActorInitializer init, VeteranProductionIconOverlayInfo info) + public ProductionIconOverlayManager(ActorInitializer init, ProductionIconOverlayManagerInfo info) { self = init.Self; @@ -60,13 +69,13 @@ namespace OpenRA.Mods.Common.Traits.Render this.info = info; + prefix = info.Type + "."; var ttc = self.Trait(); foreach (var a in self.World.Map.Rules.Actors.Values) { - var uwc = a.TraitInfoOrDefault(); - if (uwc != null) - ttc.Add(MakeKey(a.Name), uwc.Prerequisites, 0, this); + foreach (var wpio in a.TraitInfos().Where(wpio => wpio.Types.Contains(info.Type))) + ttc.Add(MakeKey(a.Name), wpio.Prerequisites, 0, this); } } @@ -88,14 +97,14 @@ namespace OpenRA.Mods.Common.Traits.Render return isActive; } - static string MakeKey(string name) + string MakeKey(string name) { - return Prefix + name; + return prefix + name; } - static string GetName(string key) + string GetName(string key) { - return key.Substring(Prefix.Length); + return key.Substring(prefix.Length); } public void PrerequisitesAvailable(string key) diff --git a/OpenRA.Mods.Common/Traits/Render/WithProductionIconOverlay.cs b/OpenRA.Mods.Common/Traits/Render/WithProductionIconOverlay.cs new file mode 100644 index 0000000000..2ff743400e --- /dev/null +++ b/OpenRA.Mods.Common/Traits/Render/WithProductionIconOverlay.cs @@ -0,0 +1,40 @@ +#region Copyright & License Information +/* + * Copyright 2007-2022 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; +using System.Linq; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits.Render +{ + [Desc("Displays overlays from `ProductionIconOverlayManager` with matching types when defined prerequisites are granted.")] + public class WithProductionIconOverlayInfo : TraitInfo, IRulesetLoaded + { + [FieldLoader.Require] + public readonly string[] Types = Array.Empty(); + + public readonly string[] Prerequisites = Array.Empty(); + + public virtual void RulesetLoaded(Ruleset rules, ActorInfo ai) + { + foreach (var type in Types) + { + if (!rules.Actors[SystemActors.Player].TraitInfos().Where(piom => piom.Type == type).Any()) + throw new YamlException($"A 'ProductionIconOverlayManager' with type '{type}' doesn't exist."); + + if (ai.TraitInfos().Where(wpio => wpio != this && wpio.Types.Contains(type)).Any()) + throw new YamlException($"Multiple 'WithProductionIconOverlay's with type '{type}' exist on the actor."); + } + } + } + + public class WithProductionIconOverlay { } +} diff --git a/OpenRA.Mods.Common/UpdateRules/Rules/20201213/UnhardcodeVeteranProductionIconOverlay.cs b/OpenRA.Mods.Common/UpdateRules/Rules/20201213/UnhardcodeVeteranProductionIconOverlay.cs new file mode 100644 index 0000000000..0b1f6cc54a --- /dev/null +++ b/OpenRA.Mods.Common/UpdateRules/Rules/20201213/UnhardcodeVeteranProductionIconOverlay.cs @@ -0,0 +1,49 @@ +#region Copyright & License Information +/* + * Copyright 2007-2022 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; +using System.Linq; + +namespace OpenRA.Mods.Common.UpdateRules.Rules +{ + public class UnhardcodeVeteranProductionIconOverlay : UpdateRule + { + public override string Name => "VeteranProductionIconOverlay is changed to ProductionIconOverlayManager giving it more customisation."; + + public override string Description => "ProductionIconOverlayManager now works with the new WithProductionIconOverlay trait, instead of ProducibleWithLevel."; + + readonly List locations = new List(); + + public override IEnumerable AfterUpdate(ModData modData) + { + if (locations.Any()) + yield return "Icon overlay logic has been split from ProducibleWithLevel to WithProductionIconOverlay trait.\n" + + "If you have been using VeteranProductionIconOverlay trait, add WithProductionIconOverlay to following actors:\n" + + UpdateUtils.FormatMessageList(locations); + + locations.Clear(); + } + + public override IEnumerable UpdateActorNode(ModData modData, MiniYamlNode actorNode) + { + foreach (var veteranProductionIconOverlay in actorNode.ChildrenMatching("VeteranProductionIconOverlay")) + { + veteranProductionIconOverlay.RenameKey("ProductionIconOverlayManager"); + veteranProductionIconOverlay.AddNode(new MiniYamlNode("Type", "Veterancy")); + } + + foreach (var producibleWithLevel in actorNode.ChildrenMatching("ProducibleWithLevel")) + locations.Add($"{actorNode.Key}: {producibleWithLevel.Key} ({actorNode.Location.Filename})"); + + yield break; + } + } +} diff --git a/OpenRA.Mods.Common/UpdateRules/UpdatePath.cs b/OpenRA.Mods.Common/UpdateRules/UpdatePath.cs index 3ad601ddb1..928ec9840d 100644 --- a/OpenRA.Mods.Common/UpdateRules/UpdatePath.cs +++ b/OpenRA.Mods.Common/UpdateRules/UpdatePath.cs @@ -95,6 +95,7 @@ namespace OpenRA.Mods.Common.UpdateRules new SplitNukePowerMissileImage(), new ReplaceSequenceEmbeddedPalette(), new UnhardcodeBaseBuilderBotModule(), + new UnhardcodeVeteranProductionIconOverlay(), }) }; diff --git a/mods/ra/rules/aircraft.yaml b/mods/ra/rules/aircraft.yaml index d663b2b561..e8d3b4afaf 100644 --- a/mods/ra/rules/aircraft.yaml +++ b/mods/ra/rules/aircraft.yaml @@ -131,6 +131,9 @@ MIG: RequiresSelection: true GrantConditionOnDamageState@SmokeTrail: Condition: enable-smoke + WithProductionIconOverlay: + Types: Veterancy + Prerequisites: aircraft.upgraded YAK: Inherits: ^Plane @@ -210,6 +213,9 @@ YAK: PipCount: 6 GrantConditionOnDamageState@SmokeTrail: Condition: enable-smoke + WithProductionIconOverlay: + Types: Veterancy + Prerequisites: aircraft.upgraded TRAN: Inherits: ^Helicopter @@ -343,6 +349,9 @@ HELI: RequiresSelection: true GrantConditionOnDamageState@SmokeTrail: Condition: enable-smoke + WithProductionIconOverlay: + Types: Veterancy + Prerequisites: aircraft.upgraded HIND: Inherits: ^Helicopter @@ -426,6 +435,9 @@ HIND: PipCount: 6 GrantConditionOnDamageState@SmokeTrail: Condition: enable-smoke + WithProductionIconOverlay: + Types: Veterancy + Prerequisites: aircraft.upgraded U2: Inherits: ^NeutralPlane @@ -544,3 +556,6 @@ MH60: PipCount: 6 GrantConditionOnDamageState@SmokeTrail: Condition: enable-smoke + WithProductionIconOverlay: + Types: Veterancy + Prerequisites: aircraft.upgraded diff --git a/mods/ra/rules/infantry.yaml b/mods/ra/rules/infantry.yaml index 3f8bc3d8c6..88273d6ec2 100644 --- a/mods/ra/rules/infantry.yaml +++ b/mods/ra/rules/infantry.yaml @@ -103,6 +103,9 @@ E1: IsPlayerPalette: true ProducibleWithLevel: Prerequisites: barracks.upgraded + WithProductionIconOverlay: + Types: Veterancy + Prerequisites: barracks.upgraded E1R1: Inherits: E1 @@ -114,6 +117,9 @@ E1R1: UpdatesPlayerStatistics: OverrideActor: e1 -Buildable: + WithProductionIconOverlay: + Types: Veterancy + Prerequisites: techlevel.infonly E2: Inherits: ^Soldier @@ -158,6 +164,9 @@ E2: DamageSource: Killer ProducibleWithLevel: Prerequisites: barracks.upgraded + WithProductionIconOverlay: + Types: Veterancy + Prerequisites: barracks.upgraded E3: Inherits: ^Soldier @@ -202,6 +211,9 @@ E3: Prerequisites: barracks.upgraded AutoTarget: ScanRadius: 5 + WithProductionIconOverlay: + Types: Veterancy + Prerequisites: barracks.upgraded E3R1: Inherits: E3 @@ -213,6 +225,9 @@ E3R1: UpdatesPlayerStatistics: OverrideActor: e3 -Buildable: + WithProductionIconOverlay: + Types: Veterancy + Prerequisites: techlevel.infonly E4: Inherits: ^Soldier @@ -254,6 +269,9 @@ E4: IsPlayerPalette: true ProducibleWithLevel: Prerequisites: barracks.upgraded + WithProductionIconOverlay: + Types: Veterancy + Prerequisites: barracks.upgraded E6: Inherits: ^Soldier @@ -436,6 +454,9 @@ E7: VoiceSet: TanyaVoice ProducibleWithLevel: Prerequisites: barracks.upgraded + WithProductionIconOverlay: + Types: Veterancy + Prerequisites: barracks.upgraded MEDI: Inherits: ^Soldier @@ -706,6 +727,9 @@ SHOK: VoiceSet: ShokVoice ProducibleWithLevel: Prerequisites: barracks.upgraded + WithProductionIconOverlay: + Types: Veterancy + Prerequisites: barracks.upgraded SNIPER: Inherits: ^Soldier @@ -759,6 +783,9 @@ SNIPER: -MustBeDestroyed: ProducibleWithLevel: Prerequisites: barracks.upgraded + WithProductionIconOverlay: + Types: Veterancy + Prerequisites: barracks.upgraded Zombie: Inherits: ^Soldier diff --git a/mods/ra/rules/player.yaml b/mods/ra/rules/player.yaml index faf22404d1..530cddb842 100644 --- a/mods/ra/rules/player.yaml +++ b/mods/ra/rules/player.yaml @@ -172,7 +172,8 @@ Player: Id: unrestricted GrantConditionOnPrerequisiteManager: EnemyWatcher: - VeteranProductionIconOverlay: + ProductionIconOverlayManager: + Type: Veterancy Image: iconchevrons Sequence: veteran ResourceStorageWarning: diff --git a/mods/ra/rules/vehicles.yaml b/mods/ra/rules/vehicles.yaml index 6e05c72fbe..31f3c46ed2 100644 --- a/mods/ra/rules/vehicles.yaml +++ b/mods/ra/rules/vehicles.yaml @@ -47,6 +47,9 @@ V2RL: Prerequisites: vehicles.upgraded Selectable: DecorationBounds: 1194, 1194 + WithProductionIconOverlay: + Types: Veterancy + Prerequisites: vehicles.upgraded 1TNK: Inherits: ^TrackedVehicle @@ -89,6 +92,9 @@ V2RL: WithSpriteTurret: ProducibleWithLevel: Prerequisites: vehicles.upgraded + WithProductionIconOverlay: + Types: Veterancy + Prerequisites: vehicles.upgraded 2TNK: Inherits: ^TrackedVehicle @@ -135,6 +141,9 @@ V2RL: Prerequisites: vehicles.upgraded Selectable: DecorationBounds: 1194, 1194 + WithProductionIconOverlay: + Types: Veterancy + Prerequisites: vehicles.upgraded 3TNK: Inherits: ^TrackedVehicle @@ -181,6 +190,9 @@ V2RL: Prerequisites: vehicles.upgraded Selectable: DecorationBounds: 1194, 1194 + WithProductionIconOverlay: + Types: Veterancy + Prerequisites: vehicles.upgraded 4TNK: Inherits: ^TrackedVehicle @@ -240,6 +252,9 @@ V2RL: Prerequisites: vehicles.upgraded Selectable: DecorationBounds: 1877, 1621, 0, -170 + WithProductionIconOverlay: + Types: Veterancy + Prerequisites: vehicles.upgraded ARTY: Inherits: ^TrackedVehicle @@ -285,6 +300,9 @@ ARTY: LoadedChance: 75 ProducibleWithLevel: Prerequisites: vehicles.upgraded + WithProductionIconOverlay: + Types: Veterancy + Prerequisites: vehicles.upgraded HARV: Inherits: ^Vehicle @@ -432,6 +450,9 @@ JEEP: LoadingCondition: notmobile ProducibleWithLevel: Prerequisites: vehicles.upgraded + WithProductionIconOverlay: + Types: Veterancy + Prerequisites: vehicles.upgraded APC: Inherits: ^TrackedVehicle @@ -475,6 +496,9 @@ APC: LoadingCondition: notmobile ProducibleWithLevel: Prerequisites: vehicles.upgraded + WithProductionIconOverlay: + Types: Veterancy + Prerequisites: vehicles.upgraded MNLY: Inherits: ^TrackedVehicle @@ -663,6 +687,9 @@ TTNK: Prerequisites: vehicles.upgraded Selectable: DecorationBounds: 1280, 1280 + WithProductionIconOverlay: + Types: Veterancy + Prerequisites: vehicles.upgraded FTRK: Inherits: ^Vehicle @@ -712,6 +739,9 @@ FTRK: Prerequisites: vehicles.upgraded Selectable: DecorationBounds: 1194, 1194 + WithProductionIconOverlay: + Types: Veterancy + Prerequisites: vehicles.upgraded DTRK: Inherits: ^Vehicle @@ -797,6 +827,9 @@ CTNK: Prerequisites: vehicles.upgraded Selectable: DecorationBounds: 1280, 1280 + WithProductionIconOverlay: + Types: Veterancy + Prerequisites: vehicles.upgraded QTNK: Inherits: ^TrackedVehicle @@ -896,3 +929,6 @@ STNK: -MustBeDestroyed: ProducibleWithLevel: Prerequisites: vehicles.upgraded + WithProductionIconOverlay: + Types: Veterancy + Prerequisites: vehicles.upgraded