From da5a725458423528a5b816480712cc410afa5865 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 18 Feb 2017 17:06:47 +0000 Subject: [PATCH] Add support for per-source and total external condition caps. --- OpenRA.Mods.Common/OpenRA.Mods.Common.csproj | 2 +- .../Properties/ConditionProperties.cs | 24 +-- .../Traits/Conditions/ConditionManager.cs | 34 +---- .../Traits/Conditions/ExternalCondition.cs | 142 ++++++++++++++++++ .../Traits/Conditions/ExternalConditions.cs | 25 --- .../Conditions/ProximityExternalCondition.cs | 25 +-- .../GrantExternalConditionCrateAction.cs | 12 +- .../GrantExternalConditionPower.cs | 12 +- .../UtilityCommands/UpgradeRules.cs | 18 +++ .../Warheads/GrantExternalConditionWarhead.cs | 9 +- mods/cnc/rules/defaults.yaml | 4 +- mods/ra/maps/allies-03a/rules.yaml | 8 +- mods/ra/maps/allies-03b/rules.yaml | 8 +- mods/ra/maps/desert-shellmap/rules.yaml | 24 +-- mods/ra/maps/soviet-03/rules.yaml | 10 +- mods/ra/rules/defaults.yaml | 4 +- mods/ra/rules/structures.yaml | 4 +- mods/ts/maps/fields-of-green/map.yaml | 12 +- mods/ts/rules/civilian-structures.yaml | 3 +- mods/ts/rules/defaults.yaml | 21 ++- 20 files changed, 266 insertions(+), 135 deletions(-) create mode 100644 OpenRA.Mods.Common/Traits/Conditions/ExternalCondition.cs delete mode 100644 OpenRA.Mods.Common/Traits/Conditions/ExternalConditions.cs diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index b292906005..b9e1fe0516 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -777,7 +777,7 @@ - + diff --git a/OpenRA.Mods.Common/Scripting/Properties/ConditionProperties.cs b/OpenRA.Mods.Common/Scripting/Properties/ConditionProperties.cs index 46f4872fb4..71d7b22894 100644 --- a/OpenRA.Mods.Common/Scripting/Properties/ConditionProperties.cs +++ b/OpenRA.Mods.Common/Scripting/Properties/ConditionProperties.cs @@ -20,15 +20,16 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Scripting { [ScriptPropertyGroup("General")] - public class ConditionProperties : ScriptActorProperties, Requires + public class ConditionProperties : ScriptActorProperties, Requires { - readonly ConditionManager conditionManager; + readonly ExternalCondition[] externalConditions; + readonly Dictionary> legacyShim = new Dictionary>(); public ConditionProperties(ScriptContext context, Actor self) : base(context, self) { - conditionManager = self.Trait(); + externalConditions = self.TraitsImplementing().ToArray(); } [Desc("Grant an external condition on this actor and return the revocation token.", @@ -36,22 +37,27 @@ namespace OpenRA.Mods.Common.Scripting "If duration > 0 the condition will be automatically revoked after the defined number of ticks")] public int GrantCondition(string condition, int duration = 0) { - if (!conditionManager.AcceptsExternalCondition(Self, condition, duration > 0)) - throw new InvalidDataException("Condition `{0}` has not been listed on an ExternalConditions trait".F(condition)); + var external = externalConditions + .FirstOrDefault(t => t.Info.Condition == condition && t.CanGrantCondition(Self, this)); - return conditionManager.GrantCondition(Self, condition, true, duration); + if (external == null) + throw new InvalidDataException("Condition `{0}` has not been listed on an enabled ExternalCondition trait".F(condition)); + + return external.GrantCondition(Self, this, duration); } [Desc("Revoke a condition using the token returned by GrantCondition.")] public void RevokeCondition(int token) { - conditionManager.RevokeCondition(Self, token); + foreach (var external in externalConditions) + external.TryRevokeCondition(Self, this, token); } [Desc("Check whether this actor accepts a specific external condition.")] - public bool AcceptsCondition(string condition, bool timed = false) + public bool AcceptsCondition(string condition) { - return conditionManager.AcceptsExternalCondition(Self, condition, timed); + return externalConditions + .Any(t => t.Info.Condition == condition && t.CanGrantCondition(Self, this)); } [Desc("Grant an upgrade to this actor. DEPRECATED! Will be removed.")] diff --git a/OpenRA.Mods.Common/Traits/Conditions/ConditionManager.cs b/OpenRA.Mods.Common/Traits/Conditions/ConditionManager.cs index 805756aaf5..b70f5460ae 100644 --- a/OpenRA.Mods.Common/Traits/Conditions/ConditionManager.cs +++ b/OpenRA.Mods.Common/Traits/Conditions/ConditionManager.cs @@ -63,9 +63,6 @@ namespace OpenRA.Mods.Common.Traits /// Each granted condition receives a unique token that is used when revoking. Dictionary tokens = new Dictionary(); - /// Set of whitelisted externally grantable conditions cached from ExternalConditions traits. - string[] externalConditions = { }; - /// Set of conditions that are monitored for stacked bonuses, and the bonus conditions that they grant. readonly Dictionary stackedConditions = new Dictionary(); @@ -114,12 +111,6 @@ namespace OpenRA.Mods.Common.Traits conditionCache[kv.Value] = conditionState.Tokens.Count > 0; } - // Build external condition whitelist - externalConditions = self.Info.TraitInfos() - .SelectMany(t => t.Conditions) - .Distinct() - .ToArray(); - foreach (var sc in self.Info.TraitInfos()) { stackedConditions[sc.Condition] = sc.StackedConditions; @@ -172,11 +163,8 @@ namespace OpenRA.Mods.Common.Traits /// The token that is used to revoke this condition. /// Validate against the external condition whitelist. /// Automatically revoke condition after this delay if non-zero. - public int GrantCondition(Actor self, string condition, bool external = false, int duration = 0) + public int GrantCondition(Actor self, string condition, int duration = 0) { - if (external && !externalConditions.Contains(condition)) - return InvalidConditionToken; - var token = nextToken++; tokens.Add(token, condition); @@ -218,26 +206,6 @@ namespace OpenRA.Mods.Common.Traits return InvalidConditionToken; } - /// Returns true if the given external condition will have an effect on this actor. - public bool AcceptsExternalCondition(Actor self, string condition, bool timed = false) - { - if (state == null) - throw new InvalidOperationException("AcceptsExternalCondition cannot be queried before the actor has been fully created."); - - if (!externalConditions.Contains(condition)) - return false; - - // A timed condition can always replace an existing timed condition (resetting its duration) - if (timed && timers.ContainsKey(condition)) - return true; - - string[] sc; - if (stackedConditions.TryGetValue(condition, out sc)) - return stackedTokens[condition].Count < sc.Length; - - return !conditionCache[condition]; - } - /// Returns whether the specified token is valid for RevokeCondition public bool TokenValid(Actor self, int token) { diff --git a/OpenRA.Mods.Common/Traits/Conditions/ExternalCondition.cs b/OpenRA.Mods.Common/Traits/Conditions/ExternalCondition.cs new file mode 100644 index 0000000000..8e9c0ab91d --- /dev/null +++ b/OpenRA.Mods.Common/Traits/Conditions/ExternalCondition.cs @@ -0,0 +1,142 @@ +#region Copyright & License Information +/* + * Copyright 2007-2017 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.Collections.Generic; +using System.Linq; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits +{ + [Desc("Allows a condition to be granted from an external source (Lua, warheads, etc).")] + public class ExternalConditionInfo : ITraitInfo, Requires + { + [GrantedConditionReference] + [FieldLoader.Require] + public readonly string Condition = null; + + [Desc("If > 0, restrict the number of times that this condition can be granted by a single source.")] + public readonly int SourceCap = 0; + + [Desc("If > 0, restrict the number of times that this condition can be granted by any source.")] + public readonly int TotalCap = 0; + + public object Create(ActorInitializer init) { return new ExternalCondition(init.Self, this); } + } + + public class ExternalCondition + { + class TimedToken + { + public int Token; + public int Expires; + } + + public readonly ExternalConditionInfo Info; + readonly ConditionManager conditionManager; + readonly Dictionary> permanentTokens = new Dictionary>(); + readonly Dictionary> timedTokens = new Dictionary>(); + + public ExternalCondition(Actor self, ExternalConditionInfo info) + { + Info = info; + conditionManager = self.Trait(); + } + + public bool CanGrantCondition(Actor self, object source) + { + if (conditionManager == null || source == null) + return false; + + // Timed tokens do not count towards the source cap: the condition with the shortest + // remaining duration can always be revoked to make room. + if (Info.SourceCap > 0 && permanentTokens.GetOrAdd(source).Count >= Info.SourceCap) + return false; + + if (Info.TotalCap > 0 && permanentTokens.Values.SelectMany(t => t).Count() >= Info.TotalCap) + return false; + + return true; + } + + public int GrantCondition(Actor self, object source, int duration = 0) + { + if (conditionManager == null || source == null || !CanGrantCondition(self, source)) + return ConditionManager.InvalidConditionToken; + + var token = conditionManager.GrantCondition(self, Info.Condition, duration); + var permanent = permanentTokens.GetOrAdd(source); + + if (duration > 0) + { + var timed = timedTokens.GetOrAdd(source); + + // Remove expired tokens + timed.RemoveWhere(t => t.Expires < self.World.WorldTick); + + // Check level caps + if (Info.SourceCap > 0) + { + if (permanent.Count + timed.Count >= Info.SourceCap) + { + var expire = timed.MinByOrDefault(t => t.Expires); + if (expire != null) + { + timed.Remove(expire); + if (conditionManager.TokenValid(self, expire.Token)) + conditionManager.RevokeCondition(self, expire.Token); + } + } + } + + if (Info.TotalCap > 0) + { + var totalCount = permanentTokens.Values.SelectMany(t => t).Count() + timedTokens.Values.SelectMany(t => t).Count(); + if (totalCount >= Info.TotalCap) + { + // Prefer tokens from the same source + var expire = timedTokens.SelectMany(t => t.Value.Select(tt => new Tuple(t.Key, tt))) + .MinByOrDefault(t => t.Item2.Expires); + if (expire != null) + { + if (conditionManager.TokenValid(self, expire.Item2.Token)) + conditionManager.RevokeCondition(self, expire.Item2.Token); + + timedTokens[expire.Item1].Remove(expire.Item2); + } + } + } + + timed.Add(new TimedToken { Expires = self.World.WorldTick + duration, Token = token }); + } + else + permanent.Add(token); + + return token; + } + + /// Revokes the external condition with the given token if it was granted by this trait. + /// true if the now-revoked condition was originally granted by this trait. + public bool TryRevokeCondition(Actor self, object source, int token) + { + if (conditionManager == null || source == null) + return false; + + var removed = permanentTokens.GetOrAdd(source).Remove(token) || + timedTokens.GetOrAdd(source).RemoveWhere(t => t.Token == token) > 0; + + if (removed && conditionManager.TokenValid(self, token)) + conditionManager.RevokeCondition(self, token); + + return true; + } + } +} diff --git a/OpenRA.Mods.Common/Traits/Conditions/ExternalConditions.cs b/OpenRA.Mods.Common/Traits/Conditions/ExternalConditions.cs deleted file mode 100644 index f7d5bd98ff..0000000000 --- a/OpenRA.Mods.Common/Traits/Conditions/ExternalConditions.cs +++ /dev/null @@ -1,25 +0,0 @@ -#region Copyright & License Information -/* - * Copyright 2007-2017 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 OpenRA.Traits; - -namespace OpenRA.Mods.Common.Traits -{ - [Desc("Lists conditions that are accepted from external sources (Lua, warheads, etc).", - "Externally granted conditions that aren't explicitly whitelisted will be silently ignored.")] - public class ExternalConditionsInfo : TraitInfo - { - [GrantedConditionReference] - public readonly string[] Conditions = { }; - } - - public class ExternalConditions { } -} diff --git a/OpenRA.Mods.Common/Traits/Conditions/ProximityExternalCondition.cs b/OpenRA.Mods.Common/Traits/Conditions/ProximityExternalCondition.cs index 416c49bf39..2a46ed1465 100644 --- a/OpenRA.Mods.Common/Traits/Conditions/ProximityExternalCondition.cs +++ b/OpenRA.Mods.Common/Traits/Conditions/ProximityExternalCondition.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Linq; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -104,13 +105,18 @@ namespace OpenRA.Mods.Common.Traits if (a == self && !info.AffectsParent) return; + if (tokens.ContainsKey(a)) + return; + var stance = self.Owner.Stances[a.Owner]; if (!info.ValidStances.HasStance(stance)) return; - var cm = a.TraitOrDefault(); - if (cm != null && !tokens.ContainsKey(a) && cm.AcceptsExternalCondition(a, info.Condition)) - tokens[a] = cm.GrantCondition(a, info.Condition, true); + var external = a.TraitsImplementing() + .FirstOrDefault(t => t.Info.Condition == info.Condition && t.CanGrantCondition(a, self)); + + if (external != null) + tokens[a] = external.GrantCondition(a, self); } public void UnitProducedByOther(Actor self, Actor producer, Actor produced) @@ -130,9 +136,11 @@ namespace OpenRA.Mods.Common.Traits if (!info.ValidStances.HasStance(stance)) return; - var cm = produced.TraitOrDefault(); - if (cm != null && cm.AcceptsExternalCondition(produced, info.Condition)) - tokens[produced] = cm.GrantCondition(produced, info.Condition, true); + var external = produced.TraitsImplementing() + .FirstOrDefault(t => t.Info.Condition == info.Condition && t.CanGrantCondition(produced, self)); + + if (external != null) + tokens[produced] = external.GrantCondition(produced, self); } } @@ -146,9 +154,8 @@ namespace OpenRA.Mods.Common.Traits return; tokens.Remove(a); - var cm = a.TraitOrDefault(); - if (cm != null) - cm.RevokeCondition(a, token); + foreach (var external in a.TraitsImplementing()) + external.TryRevokeCondition(a, self, token); } } } diff --git a/OpenRA.Mods.Common/Traits/Crates/GrantExternalConditionCrateAction.cs b/OpenRA.Mods.Common/Traits/Crates/GrantExternalConditionCrateAction.cs index 0a9ea8a2bc..b9ef96ff62 100644 --- a/OpenRA.Mods.Common/Traits/Crates/GrantExternalConditionCrateAction.cs +++ b/OpenRA.Mods.Common/Traits/Crates/GrantExternalConditionCrateAction.cs @@ -47,8 +47,8 @@ namespace OpenRA.Mods.Common.Traits bool AcceptsCondition(Actor a) { - var cm = a.TraitOrDefault(); - return cm != null && cm.AcceptsExternalCondition(a, info.Condition, info.Duration > 0); + return a.TraitsImplementing() + .Any(t => t.Info.Condition == info.Condition && t.CanGrantCondition(a, self)); } public override int GetSelectionShares(Actor collector) @@ -71,11 +71,11 @@ namespace OpenRA.Mods.Common.Traits if (!a.IsInWorld || a.IsDead) continue; - var cm = a.TraitOrDefault(); + var external = a.TraitsImplementing() + .FirstOrDefault(t => t.Info.Condition == info.Condition && t.CanGrantCondition(a, self)); - // Condition token is ignored because we never revoke this condition. - if (cm != null) - cm.GrantCondition(a, info.Condition, true, info.Duration); + if (external != null) + external.GrantCondition(a, self, info.Duration); } }); diff --git a/OpenRA.Mods.Common/Traits/SupportPowers/GrantExternalConditionPower.cs b/OpenRA.Mods.Common/Traits/SupportPowers/GrantExternalConditionPower.cs index 2e00ebefc9..f8f70c3600 100644 --- a/OpenRA.Mods.Common/Traits/SupportPowers/GrantExternalConditionPower.cs +++ b/OpenRA.Mods.Common/Traits/SupportPowers/GrantExternalConditionPower.cs @@ -69,11 +69,11 @@ namespace OpenRA.Mods.Common.Traits foreach (var a in UnitsInRange(order.TargetLocation)) { - var cm = a.TraitOrDefault(); + var external = a.TraitsImplementing() + .FirstOrDefault(t => t.Info.Condition == info.Condition && t.CanGrantCondition(a, self)); - // Condition token is ignored because we never revoke this condition. - if (cm != null) - cm.GrantCondition(a, info.Condition, true, info.Duration); + if (external != null) + external.GrantCondition(a, self, info.Duration); } } @@ -90,8 +90,8 @@ namespace OpenRA.Mods.Common.Traits if (!a.Owner.IsAlliedWith(Self.Owner)) return false; - var cm = a.TraitOrDefault(); - return cm != null && cm.AcceptsExternalCondition(a, info.Condition, info.Duration > 0); + return a.TraitsImplementing() + .Any(t => t.Info.Condition == info.Condition && t.CanGrantCondition(a, Self)); }); } diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs index 16421a5862..1851661868 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs @@ -850,6 +850,24 @@ namespace OpenRA.Mods.Common.UtilityCommands } } + if (engineVersion < 20170218) + { + var externalConditions = node.Value.Nodes.Where(n => n.Key.StartsWith("ExternalConditions", StringComparison.Ordinal)); + foreach (var ec in externalConditions.ToList()) + { + var conditionsNode = ec.Value.Nodes.FirstOrDefault(n => n.Key == "Conditions"); + if (conditionsNode != null) + { + var conditions = FieldLoader.GetValue("", conditionsNode.Value.Value); + foreach (var c in conditions) + node.Value.Nodes.Add(new MiniYamlNode("ExternalCondition@" + c.ToUpperInvariant(), + new MiniYaml("", new List() { new MiniYamlNode("Condition", c) }))); + + node.Value.Nodes.Remove(ec); + } + } + } + UpgradeActorRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1); } diff --git a/OpenRA.Mods.Common/Warheads/GrantExternalConditionWarhead.cs b/OpenRA.Mods.Common/Warheads/GrantExternalConditionWarhead.cs index 31884f8431..5d2da1342c 100644 --- a/OpenRA.Mods.Common/Warheads/GrantExternalConditionWarhead.cs +++ b/OpenRA.Mods.Common/Warheads/GrantExternalConditionWarhead.cs @@ -10,6 +10,7 @@ #endregion using System.Collections.Generic; +using System.Linq; using OpenRA.Mods.Common.Traits; using OpenRA.Traits; @@ -36,11 +37,11 @@ namespace OpenRA.Mods.Common.Warheads if (!IsValidAgainst(a, firedBy)) continue; - var cm = a.TraitOrDefault(); + var external = a.TraitsImplementing() + .FirstOrDefault(t => t.Info.Condition == Condition && t.CanGrantCondition(a, firedBy)); - // Condition token is ignored because we never revoke this condition. - if (cm != null && cm.AcceptsExternalCondition(a, Condition, Duration > 0)) - cm.GrantCondition(a, Condition, true, Duration); + if (external != null) + external.GrantCondition(a, firedBy, Duration); } } } diff --git a/mods/cnc/rules/defaults.yaml b/mods/cnc/rules/defaults.yaml index 6e49ece993..60eee515ef 100644 --- a/mods/cnc/rules/defaults.yaml +++ b/mods/cnc/rules/defaults.yaml @@ -123,8 +123,8 @@ CloakSound: trans1.aud UncloakSound: trans1.aud RequiresCondition: cloak - ExternalConditions@CLOAK: - Conditions: cloak + ExternalCondition@CLOAK: + Condition: cloak ^Vehicle: Inherits@1: ^ExistsInWorld diff --git a/mods/ra/maps/allies-03a/rules.yaml b/mods/ra/maps/allies-03a/rules.yaml index 21570e38fb..68228ffcc2 100644 --- a/mods/ra/maps/allies-03a/rules.yaml +++ b/mods/ra/maps/allies-03a/rules.yaml @@ -34,20 +34,20 @@ HACKE6: CaptureTypes: building Targetable: RequiresCondition: !jail - ExternalConditions@JAIL: - Conditions: jail Targetable@PRISONER: TargetTypes: Prisoner RenderSprites: Image: E6 + ExternalCondition@JAIL: + Condition: jail MEDI: Targetable: RequiresCondition: !jail - ExternalConditions@JAIL: - Conditions: jail Targetable@PRISONER: TargetTypes: Prisoner + ExternalCondition@JAIL: + Condition: jail PRISON: HiddenUnderShroud: diff --git a/mods/ra/maps/allies-03b/rules.yaml b/mods/ra/maps/allies-03b/rules.yaml index 78d533e0c0..485c28fa76 100644 --- a/mods/ra/maps/allies-03b/rules.yaml +++ b/mods/ra/maps/allies-03b/rules.yaml @@ -35,20 +35,20 @@ HACKE6: WithInfantryBody: Targetable: RequiresCondition: !jail - ExternalConditions@JAIL: - Conditions: jail Targetable@PRISONER: TargetTypes: Prisoner RenderSprites: Image: E6 + ExternalCondition@JAIL: + Condition: jail MEDI: Targetable: RequiresCondition: !jail - ExternalConditions@JAIL: - Conditions: jail Targetable@PRISONER: TargetTypes: Prisoner + ExternalCondition@JAIL: + Condition: jail PRISON: HiddenUnderShroud: diff --git a/mods/ra/maps/desert-shellmap/rules.yaml b/mods/ra/maps/desert-shellmap/rules.yaml index 63f2a7a1cb..c7546f9c42 100644 --- a/mods/ra/maps/desert-shellmap/rules.yaml +++ b/mods/ra/maps/desert-shellmap/rules.yaml @@ -22,8 +22,8 @@ World: DamageMultiplier@UNKILLABLE: RequiresCondition: unkillable Modifier: 0 - ExternalConditions: - Conditions: unkillable + ExternalCondition@UNKILLABLE: + Condition: unkillable ^Tank: GivesBounty: @@ -33,8 +33,8 @@ World: DamageMultiplier@UNKILLABLE: RequiresCondition: unkillable Modifier: 0 - ExternalConditions: - Conditions: unkillable + ExternalCondition@UNKILLABLE: + Condition: unkillable ^Infantry: GivesBounty: @@ -50,8 +50,8 @@ World: DamageMultiplier@UNKILLABLE: RequiresCondition: unkillable Modifier: 0 - ExternalConditions: - Conditions: unkillable + ExternalCondition@UNKILLABLE: + Condition: unkillable ^Ship: GivesBounty: @@ -61,8 +61,8 @@ World: DamageMultiplier@UNKILLABLE: RequiresCondition: unkillable Modifier: 0 - ExternalConditions: - Conditions: unkillable + ExternalCondition@UNKILLABLE: + Condition: unkillable ^Plane: GivesBounty: @@ -70,8 +70,8 @@ World: DamageMultiplier@UNKILLABLE: RequiresCondition: unkillable Modifier: 0 - ExternalConditions: - Conditions: unkillable + ExternalCondition@UNKILLABLE: + Condition: unkillable ^Building: GivesBounty: @@ -79,8 +79,8 @@ World: DamageMultiplier@UNKILLABLE: RequiresCondition: unkillable Modifier: 0 - ExternalConditions: - Conditions: unkillable + ExternalCondition@UNKILLABLE: + Condition: unkillable OILB: CashTrickler: diff --git a/mods/ra/maps/soviet-03/rules.yaml b/mods/ra/maps/soviet-03/rules.yaml index 199bf660d9..13514e9f62 100644 --- a/mods/ra/maps/soviet-03/rules.yaml +++ b/mods/ra/maps/soviet-03/rules.yaml @@ -59,8 +59,14 @@ V05: DOG: # HACK: Disable experience without killing the linter -GainsExperience: - ExternalConditions@EXPERIENCE: - Conditions: rank-veteran-1, rank-veteran-2, rank-veteran-3, rank-elite + ExternalCondition@RANK-VETERAN-1: + Condition: rank-veteran-1 + ExternalCondition@RANK-VETERAN-2: + Condition: rank-veteran-2 + ExternalCondition@RANK-VETERAN-3: + Condition: rank-veteran-3 + ExternalCondition@RANK-ELITE: + Condition: rank-elite SPY: Mobile: diff --git a/mods/ra/rules/defaults.yaml b/mods/ra/rules/defaults.yaml index 4797957197..42ff6e71ec 100644 --- a/mods/ra/rules/defaults.yaml +++ b/mods/ra/rules/defaults.yaml @@ -123,8 +123,8 @@ Modifier: 0 TimedConditionBar: Condition: invulnerability - ExternalConditions@INVULNERABILITY: - Conditions: invulnerability + ExternalCondition@INVULNERABILITY: + Condition: invulnerability ^Vehicle: Inherits@1: ^ExistsInWorld diff --git a/mods/ra/rules/structures.yaml b/mods/ra/rules/structures.yaml index 02050912f7..903482d46b 100644 --- a/mods/ra/rules/structures.yaml +++ b/mods/ra/rules/structures.yaml @@ -539,8 +539,6 @@ DOME: Bib: ProvidesRadar: RequiresCondition: !jammed && !disabled - ExternalConditions@JAMMED: - Conditions: jammed InfiltrateForExploration: DetectCloaked: Range: 10c0 @@ -550,6 +548,8 @@ DOME: ProvidesPrerequisite@buildingname: GrantConditionOnDisabled@IDISABLE: Condition: disabled + ExternalCondition@JAMMED: + Condition: jammed PBOX: Inherits: ^Defense diff --git a/mods/ts/maps/fields-of-green/map.yaml b/mods/ts/maps/fields-of-green/map.yaml index c7c0017e0d..a3600c2d7c 100644 --- a/mods/ts/maps/fields-of-green/map.yaml +++ b/mods/ts/maps/fields-of-green/map.yaml @@ -1448,17 +1448,17 @@ Rules: DamageMultiplier@UNKILLABLE: RequiresCondition: unkillable Modifier: 0 - ExternalConditions: - Conditions: unkillable + ExternalCondition@UNKILLABLE: + Condition: unkillable NAOBEL: DamageMultiplier@UNKILLABLE: RequiresCondition: unkillable Modifier: 0 - ExternalConditions: - Conditions: unkillable + ExternalCondition@UNKILLABLE: + Condition: unkillable NALASR: DamageMultiplier@UNKILLABLE: RequiresCondition: unkillable Modifier: 0 - ExternalConditions: - Conditions: unkillable + ExternalCondition@UNKILLABLE: + Condition: unkillable diff --git a/mods/ts/rules/civilian-structures.yaml b/mods/ts/rules/civilian-structures.yaml index 9b3da11ba9..1a649092f8 100644 --- a/mods/ts/rules/civilian-structures.yaml +++ b/mods/ts/rules/civilian-structures.yaml @@ -1296,7 +1296,8 @@ GALITE: SelectionDecorations: VisualBounds: 25, 35, 0, -12 -Cloak@EXTERNALCLOAK: - -ExternalConditions@EXTERNALCLOAK: + -ExternalCondition@CLOAKGENERATOR: + -ExternalCondition@CRATE-CLOAK: TSTLAMP: Inherits: GALITE diff --git a/mods/ts/rules/defaults.yaml b/mods/ts/rules/defaults.yaml index 89c11b9d78..f9c218adf4 100644 --- a/mods/ts/rules/defaults.yaml +++ b/mods/ts/rules/defaults.yaml @@ -75,8 +75,12 @@ SpeedMultiplier@CRATES: RequiresCondition: crate-speed Modifier: 170 - ExternalConditions@CRATES: - Conditions: crate-firepower, crate-damage, crate-speed + ExternalCondition@CRATE-FIREPOWER: + Condition: crate-firepower + ExternalCondition@CRATE-DAMAGE: + Condition: crate-damage + ExternalCondition@CRATE-SPEED: + Condition: crate-speed ^EmpDisable: WithColoredOverlay@EMPDISABLE: @@ -96,8 +100,8 @@ PowerMultiplier@EMPDISABLE: RequiresCondition: empdisable Modifier: 0 - ExternalConditions@EMPDISABLE: - Conditions: empdisable + ExternalCondition@EMPDISABLE: + Condition: empdisable ^Cloakable: Cloak@EXTERNALCLOAK: @@ -108,8 +112,10 @@ CloakSound: cloak5.aud UncloakSound: cloak5.aud UncloakOn: Attack, Unload, Infiltrate, Demolish, Damage - ExternalConditions@EXTERNALCLOAK: - Conditions: cloakgenerator, crate-cloak + ExternalCondition@CLOAKGENERATOR: + Condition: cloakgenerator + ExternalCondition@CRATE-CLOAK: + Condition: crate-cloak ^BasicBuilding: Inherits@1: ^ExistsInWorld @@ -206,7 +212,8 @@ RenderSprites: Palette: player -Cloak@EXTERNALCLOAK: - -ExternalConditions@EXTERNALCLOAK: + -ExternalCondition@CLOAKGENERATOR: + -ExternalCondition@CRATE-CLOAK: ^CivBillboard: Inherits: ^CivBuilding