From 74abf58f3ed0fa8f126e6ca02592ab661f6077f3 Mon Sep 17 00:00:00 2001 From: penev92 Date: Wed, 27 May 2015 14:35:28 +0300 Subject: [PATCH 1/7] Remove redundant crate definition from D2k (no actor accepts a `cloak` upgrade) --- mods/d2k/rules/misc.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/mods/d2k/rules/misc.yaml b/mods/d2k/rules/misc.yaml index 230809106e..7a95489235 100644 --- a/mods/d2k/rules/misc.yaml +++ b/mods/d2k/rules/misc.yaml @@ -103,10 +103,6 @@ crate: SelectionShares: 0 NoBaseSelectionShares: 9001 Units: mcv - GrantUpgradeCrateAction@cloak: - SelectionShares: 5 - Effect: cloak - Upgrades: cloak RenderSprites: Palette: effect WithCrateBody: From f1ec71b17d4e8d1131104a176da0ee7c05eea07d Mon Sep 17 00:00:00 2001 From: penev92 Date: Thu, 11 Jun 2015 13:31:40 +0300 Subject: [PATCH 2/7] Document GlobalUpgradable --- OpenRA.Mods.Common/Traits/GlobalUpgradable.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/OpenRA.Mods.Common/Traits/GlobalUpgradable.cs b/OpenRA.Mods.Common/Traits/GlobalUpgradable.cs index de0c96f672..f02b748503 100644 --- a/OpenRA.Mods.Common/Traits/GlobalUpgradable.cs +++ b/OpenRA.Mods.Common/Traits/GlobalUpgradable.cs @@ -13,9 +13,13 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { + [Desc("Grants upgrades to the actor this is attached to when prerequisites are available.")] public class GlobalUpgradableInfo : ITraitInfo, Requires { + [Desc("List of upgrades to apply.")] public readonly string[] Upgrades = { }; + + [Desc("List of required prerequisites.")] public readonly string[] Prerequisites = { }; public object Create(ActorInitializer init) { return new GlobalUpgradable(init.Self, this); } From 2ab7abcee490a8ddf370e60b2b05e51dece5dc4b Mon Sep 17 00:00:00 2001 From: penev92 Date: Wed, 27 May 2015 17:00:33 +0300 Subject: [PATCH 3/7] Add ScriptUpgradesCache class to enable maps to explicitly declare what upgrades their Lua scripts will use --- OpenRA.Mods.Common/OpenRA.Mods.Common.csproj | 1 + .../Scripting/Properties/UpgradeProperties.cs | 34 ++++++++++++++++--- .../Scripting/ScriptUpgradesCache.cs | 33 ++++++++++++++++++ mods/ra/maps/desert-shellmap/map.yaml | 2 ++ mods/ra/maps/fort-lonestar/map.yaml | 2 ++ 5 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 OpenRA.Mods.Common/Scripting/ScriptUpgradesCache.cs diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index 7ddbb3a6a0..d1ba89c909 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -199,6 +199,7 @@ + diff --git a/OpenRA.Mods.Common/Scripting/Properties/UpgradeProperties.cs b/OpenRA.Mods.Common/Scripting/Properties/UpgradeProperties.cs index eba3b40e6e..c0d016b38b 100644 --- a/OpenRA.Mods.Common/Scripting/Properties/UpgradeProperties.cs +++ b/OpenRA.Mods.Common/Scripting/Properties/UpgradeProperties.cs @@ -8,6 +8,9 @@ */ #endregion +using System; +using System.IO; +using System.Linq; using OpenRA.Mods.Common.Traits; using OpenRA.Scripting; using OpenRA.Traits; @@ -17,35 +20,56 @@ namespace OpenRA.Mods.Common.Scripting [ScriptPropertyGroup("General")] public class UpgradeProperties : ScriptActorProperties, Requires { - UpgradeManager um; + readonly UpgradeManager um; + readonly ScriptUpgradesCache validUpgrades; + public UpgradeProperties(ScriptContext context, Actor self) : base(context, self) { um = self.Trait(); + validUpgrades = self.World.WorldActor.TraitOrDefault(); } [Desc("Grant an upgrade to this actor.")] public void GrantUpgrade(string upgrade) { - um.GrantUpgrade(Self, upgrade, this); + if (validUpgrades == null) + throw new InvalidOperationException("Can not grant upgrades because there is no ScriptUpgradesCache defined!"); + + if (validUpgrades.Info.Upgrades.Contains(upgrade)) + um.GrantUpgrade(Self, upgrade, this); + else + throw new InvalidDataException("The ScriptUpgradesCache does not contain a definition for upgrade `{0}`".F(upgrade)); } [Desc("Revoke an upgrade that was previously granted using GrantUpgrade.")] public void RevokeUpgrade(string upgrade) { - um.RevokeUpgrade(Self, upgrade, this); + if (validUpgrades == null) + throw new InvalidOperationException("Can not grant upgrades because there is no ScriptUpgradesCache defined!"); + + if (validUpgrades.Info.Upgrades.Contains(upgrade)) + um.RevokeUpgrade(Self, upgrade, this); + else + throw new InvalidDataException("The ScriptUpgradesCache does not contain a definition for upgrade `{0}`".F(upgrade)); } [Desc("Grant a limited-time upgrade to this actor.")] public void GrantTimedUpgrade(string upgrade, int duration) { - um.GrantTimedUpgrade(Self, upgrade, duration); + if (validUpgrades == null) + throw new InvalidOperationException("Can not grant upgrades because there is no ScriptUpgradesCache defined!"); + + if (validUpgrades.Info.Upgrades.Contains(upgrade)) + um.GrantTimedUpgrade(Self, upgrade, duration); + else + throw new InvalidDataException("The ScriptUpgradesCache does not contain a definition for upgrade `{0}`".F(upgrade)); } [Desc("Check whether this actor accepts a specific upgrade.")] public bool AcceptsUpgrade(string upgrade) { - return um.AcceptsUpgrade(Self, upgrade); + return validUpgrades != null && validUpgrades.Info.Upgrades.Contains(upgrade) && um.AcceptsUpgrade(Self, upgrade); } } } \ No newline at end of file diff --git a/OpenRA.Mods.Common/Scripting/ScriptUpgradesCache.cs b/OpenRA.Mods.Common/Scripting/ScriptUpgradesCache.cs new file mode 100644 index 0000000000..8ed69dfc0c --- /dev/null +++ b/OpenRA.Mods.Common/Scripting/ScriptUpgradesCache.cs @@ -0,0 +1,33 @@ +#region Copyright & License Information +/* + * Copyright 2007-2015 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. For more information, + * see COPYING. + */ +#endregion + +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Scripting +{ + [Desc("Allows granting upgrades to actors from Lua scripts.")] + public class ScriptUpgradesCacheInfo : ITraitInfo + { + [Desc("Upgrades that can be granted from the scripts.")] + public readonly string[] Upgrades = { }; + + public object Create(ActorInitializer init) { return new ScriptUpgradesCache(this); } + } + + public sealed class ScriptUpgradesCache + { + public readonly ScriptUpgradesCacheInfo Info; + + public ScriptUpgradesCache(ScriptUpgradesCacheInfo info) + { + Info = info; + } + } +} diff --git a/mods/ra/maps/desert-shellmap/map.yaml b/mods/ra/maps/desert-shellmap/map.yaml index f1a3e27e83..04ffff3df0 100644 --- a/mods/ra/maps/desert-shellmap/map.yaml +++ b/mods/ra/maps/desert-shellmap/map.yaml @@ -1259,6 +1259,8 @@ Rules: ValuePerUnit: 0 LuaScript: Scripts: desert-shellmap.lua + ScriptUpgradesCache: + Upgrades: unkillable -StartGameNotification: ^Vehicle: GivesBounty: diff --git a/mods/ra/maps/fort-lonestar/map.yaml b/mods/ra/maps/fort-lonestar/map.yaml index 175e16a0ee..b05d28267f 100644 --- a/mods/ra/maps/fort-lonestar/map.yaml +++ b/mods/ra/maps/fort-lonestar/map.yaml @@ -488,6 +488,8 @@ Rules: -MPStartLocations: LuaScript: Scripts: fort-lonestar.lua + ScriptUpgradesCache: + Upgrades: unkillable FORTCRATE: Inherits: ^Crate SupportPowerCrateAction@parabombs: From fbbd9a7eaa1a9ca13fd791025c17f828fc9171be Mon Sep 17 00:00:00 2001 From: penev92 Date: Thu, 11 Jun 2015 13:35:26 +0300 Subject: [PATCH 4/7] Add UpgradeGrantedReferenceAttribute --- OpenRA.Game/Traits/LintAttributes.cs | 5 ++++- OpenRA.Mods.Common/Scripting/ScriptUpgradesCache.cs | 1 + OpenRA.Mods.Common/Traits/Crates/GrantUpgradeCrateAction.cs | 2 ++ OpenRA.Mods.Common/Traits/GlobalUpgradable.cs | 1 + OpenRA.Mods.Common/Traits/SupportPowers/GrantUpgradePower.cs | 1 + OpenRA.Mods.Common/Traits/Upgrades/DeployToUpgrade.cs | 1 + OpenRA.Mods.Common/Traits/Upgrades/UpgradeActorsNear.cs | 1 + OpenRA.Mods.Common/Warheads/GrantUpgradeWarhead.cs | 1 + 8 files changed, 12 insertions(+), 1 deletion(-) diff --git a/OpenRA.Game/Traits/LintAttributes.cs b/OpenRA.Game/Traits/LintAttributes.cs index e65029bef7..005ad50c31 100644 --- a/OpenRA.Game/Traits/LintAttributes.cs +++ b/OpenRA.Game/Traits/LintAttributes.cs @@ -29,7 +29,7 @@ namespace OpenRA.Traits [AttributeUsage(AttributeTargets.Field)] public sealed class SequenceReferenceAttribute : Attribute { - public readonly string ImageReference; // the field name in the same trait info that contains the image name + public readonly string ImageReference; // The field name in the same trait info that contains the image name. public readonly bool Prefix; public SequenceReferenceAttribute(string imageReference = null, bool prefix = false) { @@ -37,4 +37,7 @@ namespace OpenRA.Traits Prefix = prefix; } } + + [AttributeUsage(AttributeTargets.Field)] + public sealed class UpgradeGrantedReferenceAttribute : Attribute { } } diff --git a/OpenRA.Mods.Common/Scripting/ScriptUpgradesCache.cs b/OpenRA.Mods.Common/Scripting/ScriptUpgradesCache.cs index 8ed69dfc0c..2016665991 100644 --- a/OpenRA.Mods.Common/Scripting/ScriptUpgradesCache.cs +++ b/OpenRA.Mods.Common/Scripting/ScriptUpgradesCache.cs @@ -15,6 +15,7 @@ namespace OpenRA.Mods.Common.Scripting [Desc("Allows granting upgrades to actors from Lua scripts.")] public class ScriptUpgradesCacheInfo : ITraitInfo { + [UpgradeGrantedReference] [Desc("Upgrades that can be granted from the scripts.")] public readonly string[] Upgrades = { }; diff --git a/OpenRA.Mods.Common/Traits/Crates/GrantUpgradeCrateAction.cs b/OpenRA.Mods.Common/Traits/Crates/GrantUpgradeCrateAction.cs index 3c2d20bf5f..52737ec4e1 100644 --- a/OpenRA.Mods.Common/Traits/Crates/GrantUpgradeCrateAction.cs +++ b/OpenRA.Mods.Common/Traits/Crates/GrantUpgradeCrateAction.cs @@ -9,12 +9,14 @@ #endregion using System.Linq; +using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { [Desc("Grants an upgrade to the collector.")] public class GrantUpgradeCrateActionInfo : CrateActionInfo { + [UpgradeGrantedReference] [Desc("The upgrades to apply.")] public readonly string[] Upgrades = { }; diff --git a/OpenRA.Mods.Common/Traits/GlobalUpgradable.cs b/OpenRA.Mods.Common/Traits/GlobalUpgradable.cs index f02b748503..a49b854d9b 100644 --- a/OpenRA.Mods.Common/Traits/GlobalUpgradable.cs +++ b/OpenRA.Mods.Common/Traits/GlobalUpgradable.cs @@ -16,6 +16,7 @@ namespace OpenRA.Mods.Common.Traits [Desc("Grants upgrades to the actor this is attached to when prerequisites are available.")] public class GlobalUpgradableInfo : ITraitInfo, Requires { + [UpgradeGrantedReference] [Desc("List of upgrades to apply.")] public readonly string[] Upgrades = { }; diff --git a/OpenRA.Mods.Common/Traits/SupportPowers/GrantUpgradePower.cs b/OpenRA.Mods.Common/Traits/SupportPowers/GrantUpgradePower.cs index 18f2e1aaae..2e4b8f3ab1 100644 --- a/OpenRA.Mods.Common/Traits/SupportPowers/GrantUpgradePower.cs +++ b/OpenRA.Mods.Common/Traits/SupportPowers/GrantUpgradePower.cs @@ -19,6 +19,7 @@ namespace OpenRA.Mods.Common.Traits { class GrantUpgradePowerInfo : SupportPowerInfo { + [UpgradeGrantedReference] [Desc("The upgrades to apply.")] public readonly string[] Upgrades = { }; diff --git a/OpenRA.Mods.Common/Traits/Upgrades/DeployToUpgrade.cs b/OpenRA.Mods.Common/Traits/Upgrades/DeployToUpgrade.cs index e22fcb2cc2..379b5e62a2 100644 --- a/OpenRA.Mods.Common/Traits/Upgrades/DeployToUpgrade.cs +++ b/OpenRA.Mods.Common/Traits/Upgrades/DeployToUpgrade.cs @@ -18,6 +18,7 @@ namespace OpenRA.Mods.Common.Traits { public class DeployToUpgradeInfo : ITraitInfo, Requires { + [UpgradeGrantedReference] [Desc("The upgrades to grant when deploying and revoke when undeploying.")] public readonly string[] Upgrades = { }; diff --git a/OpenRA.Mods.Common/Traits/Upgrades/UpgradeActorsNear.cs b/OpenRA.Mods.Common/Traits/Upgrades/UpgradeActorsNear.cs index 7ed534b575..1dd9bd049c 100644 --- a/OpenRA.Mods.Common/Traits/Upgrades/UpgradeActorsNear.cs +++ b/OpenRA.Mods.Common/Traits/Upgrades/UpgradeActorsNear.cs @@ -18,6 +18,7 @@ namespace OpenRA.Mods.Common.Traits [Desc("Applies an upgrade to actors within a specified range.")] public class UpgradeActorsNearInfo : ITraitInfo { + [UpgradeGrantedReference] [Desc("The upgrades to grant.")] public readonly string[] Upgrades = { }; diff --git a/OpenRA.Mods.Common/Warheads/GrantUpgradeWarhead.cs b/OpenRA.Mods.Common/Warheads/GrantUpgradeWarhead.cs index fb8976fd95..d06509ec11 100644 --- a/OpenRA.Mods.Common/Warheads/GrantUpgradeWarhead.cs +++ b/OpenRA.Mods.Common/Warheads/GrantUpgradeWarhead.cs @@ -20,6 +20,7 @@ namespace OpenRA.Mods.Common.Warheads { public class GrantUpgradeWarhead : Warhead { + [UpgradeGrantedReference] [Desc("The upgrades to apply.")] public readonly string[] Upgrades = { }; From 2647811a134a972cc8f7cb5ba89a7d8ef48b724d Mon Sep 17 00:00:00 2001 From: penev92 Date: Fri, 12 Jun 2015 14:20:26 +0300 Subject: [PATCH 5/7] Add UpgradeUsedReferenceAttribute --- OpenRA.Game/Traits/LintAttributes.cs | 3 +++ OpenRA.Mods.Common/Traits/Upgrades/UpgradableTrait.cs | 2 ++ 2 files changed, 5 insertions(+) diff --git a/OpenRA.Game/Traits/LintAttributes.cs b/OpenRA.Game/Traits/LintAttributes.cs index 005ad50c31..9c5c9cc209 100644 --- a/OpenRA.Game/Traits/LintAttributes.cs +++ b/OpenRA.Game/Traits/LintAttributes.cs @@ -40,4 +40,7 @@ namespace OpenRA.Traits [AttributeUsage(AttributeTargets.Field)] public sealed class UpgradeGrantedReferenceAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Field)] + public sealed class UpgradeUsedReferenceAttribute : Attribute { } } diff --git a/OpenRA.Mods.Common/Traits/Upgrades/UpgradableTrait.cs b/OpenRA.Mods.Common/Traits/Upgrades/UpgradableTrait.cs index 536b15e49a..06e2d4044d 100644 --- a/OpenRA.Mods.Common/Traits/Upgrades/UpgradableTrait.cs +++ b/OpenRA.Mods.Common/Traits/Upgrades/UpgradableTrait.cs @@ -7,6 +7,7 @@ * see COPYING. */ #endregion + using System.Collections.Generic; using System.Linq; using OpenRA.Traits; @@ -16,6 +17,7 @@ namespace OpenRA.Mods.Common.Traits /// Use as base class for *Info to subclass of UpgradableTrait. (See UpgradableTrait.) public abstract class UpgradableTraitInfo { + [UpgradeUsedReference] [Desc("The upgrade types which can enable or disable this trait.")] public readonly string[] UpgradeTypes = { }; From 7bb95a16586ef571c37cabe57f6080c21d8ed920 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Mail=C3=A4nder?= Date: Thu, 11 Jun 2015 19:18:33 +0300 Subject: [PATCH 6/7] Add LintExt and move GetFieldValues() --- OpenRA.Mods.Common/Lint/LintExts.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/OpenRA.Mods.Common/Lint/LintExts.cs b/OpenRA.Mods.Common/Lint/LintExts.cs index 04dfecb045..e7dcb5d5d1 100644 --- a/OpenRA.Mods.Common/Lint/LintExts.cs +++ b/OpenRA.Mods.Common/Lint/LintExts.cs @@ -10,7 +10,6 @@ using System; using System.Reflection; -using OpenRA.Traits; namespace OpenRA.Mods.Common.Lint { @@ -20,7 +19,7 @@ namespace OpenRA.Mods.Common.Lint { var type = fieldInfo.FieldType; if (type == typeof(string)) - return new string[] { (string)fieldInfo.GetValue(ruleInfo) }; + return new[] { (string)fieldInfo.GetValue(ruleInfo) }; if (type == typeof(string[])) return (string[])fieldInfo.GetValue(ruleInfo); From 696680bad50bec2b650b55e086645e11313437da Mon Sep 17 00:00:00 2001 From: penev92 Date: Wed, 27 May 2015 14:37:05 +0300 Subject: [PATCH 7/7] Add lint check for actor upgrades --- OpenRA.Mods.Common/Lint/CheckUpgrades.cs | 153 +++++++++++++++++++ OpenRA.Mods.Common/OpenRA.Mods.Common.csproj | 1 + 2 files changed, 154 insertions(+) create mode 100644 OpenRA.Mods.Common/Lint/CheckUpgrades.cs diff --git a/OpenRA.Mods.Common/Lint/CheckUpgrades.cs b/OpenRA.Mods.Common/Lint/CheckUpgrades.cs new file mode 100644 index 0000000000..aedf046b69 --- /dev/null +++ b/OpenRA.Mods.Common/Lint/CheckUpgrades.cs @@ -0,0 +1,153 @@ +#region Copyright & License Information +/* + * Copyright 2007-2015 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. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using OpenRA.Mods.Common.Traits; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Lint +{ + public class CheckUpgrades : ILintPass + { + public void Run(Action emitError, Action emitWarning, Map map) + { + CheckUpgradesValidity(emitError, map); + CheckUpgradesUsage(emitError, emitWarning, map); + } + + private static void CheckUpgradesValidity(Action emitError, Map map) + { + var upgradesGranted = GetAllGrantedUpgrades(emitError, map).ToHashSet(); + + foreach (var actorInfo in map.Rules.Actors) + { + foreach (var trait in actorInfo.Value.Traits) + { + var fields = trait.GetType().GetFields(); + foreach (var field in fields.Where(x => x.HasAttribute())) + { + var values = LintExts.GetFieldValues(trait, field, emitError); + foreach (var value in values.Where(x => !upgradesGranted.Contains(x))) + emitError("Actor type `{0}` uses upgrade `{1}` that is not granted by anything!".F(actorInfo.Key, value)); + } + } + } + } + + private static void CheckUpgradesUsage(Action emitError, Action emitWarning, Map map) + { + var upgradesUsed = GetAllUsedUpgrades(emitError, map).ToHashSet(); + + // Check all upgrades granted by traits. + foreach (var actorInfo in map.Rules.Actors) + { + foreach (var trait in actorInfo.Value.Traits) + { + var fields = trait.GetType().GetFields(); + foreach (var field in fields.Where(x => x.HasAttribute())) + { + var values = LintExts.GetFieldValues(trait, field, emitError); + foreach (var value in values.Where(x => !upgradesUsed.Contains(x))) + emitWarning("Actor type `{0}` grants upgrade `{1}` that is not used by anything!".F(actorInfo.Key, value)); + } + } + } + + // Check all upgrades granted by warheads. + foreach (var weapon in map.Rules.Weapons) + { + foreach (var warhead in weapon.Value.Warheads) + { + var fields = warhead.GetType().GetFields(); + foreach (var field in fields.Where(x => x.HasAttribute())) + { + var values = LintExts.GetFieldValues(warhead, field, emitError); + foreach (var value in values.Where(x => !upgradesUsed.Contains(x))) + emitWarning("Weapon type `{0}` grants upgrade `{1}` that is not used by anything!".F(weapon.Key, value)); + } + } + } + } + + static IEnumerable GetAllGrantedUpgrades(Action emitError, Map map) + { + // Get all upgrades granted by traits. + foreach (var actorInfo in map.Rules.Actors) + { + foreach (var trait in actorInfo.Value.Traits) + { + var fields = trait.GetType().GetFields(); + foreach (var field in fields.Where(x => x.HasAttribute())) + { + var values = LintExts.GetFieldValues(trait, field, emitError); + foreach (var value in values) + yield return value; + } + } + } + + // Get all upgrades granted by warheads. + foreach (var weapon in map.Rules.Weapons) + { + foreach (var warhead in weapon.Value.Warheads) + { + var fields = warhead.GetType().GetFields(); + foreach (var field in fields.Where(x => x.HasAttribute())) + { + var values = LintExts.GetFieldValues(warhead, field, emitError); + foreach (var value in values) + yield return value; + } + } + } + + // TODO: HACK because GainsExperience grants upgrades differently to most other sources. + var gainsExperience = map.Rules.Actors.SelectMany(x => x.Value.Traits.WithInterface() + .SelectMany(y => y.Upgrades.SelectMany(z => z.Value))); + + foreach (var upgrade in gainsExperience) + yield return upgrade; + + // TODO: HACK because Pluggable grants upgrades differently to most other sources. + var pluggable = map.Rules.Actors.SelectMany(x => x.Value.Traits.WithInterface() + .SelectMany(y => y.Upgrades.SelectMany(z => z.Value))); + + foreach (var upgrade in pluggable) + yield return upgrade; + } + + static IEnumerable GetAllUsedUpgrades(Action emitError, Map map) + { + foreach (var actorInfo in map.Rules.Actors) + { + foreach (var trait in actorInfo.Value.Traits) + { + var fields = trait.GetType().GetFields(); + foreach (var field in fields.Where(x => x.HasAttribute())) + { + var values = LintExts.GetFieldValues(trait, field, emitError); + foreach (var value in values) + yield return value; + } + } + } + + // TODO: HACK because GainsExperience and GainsStatUpgrades do not play by the rules... + // We assume everything GainsExperience grants is used by GainsStatUpgrade + var gainsExperience = map.Rules.Actors.SelectMany(x => x.Value.Traits.WithInterface() + .SelectMany(y => y.Upgrades.SelectMany(z => z.Value))); + + foreach (var upgrade in gainsExperience) + yield return upgrade; + } + } +} \ No newline at end of file diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index d1ba89c909..8bdb35218a 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -186,6 +186,7 @@ +