From 73895d07e25a5f63d0cb292036eb24802fd4e900 Mon Sep 17 00:00:00 2001 From: atlimit8 Date: Mon, 6 Feb 2017 12:13:00 -0600 Subject: [PATCH] Numeric constants for ConditionExpression --- OpenRA.Game/Support/ConditionExpression.cs | 46 +++++++++++++--- .../OpenRA.Game/ConditionExpressionTest.cs | 54 +++++++++++++++++-- mods/cnc/rules/defaults.yaml | 47 ++++++++-------- mods/d2k/rules/defaults.yaml | 47 ++++++++-------- mods/ra/maps/soviet-03/rules.yaml | 9 ++-- mods/ra/rules/defaults.yaml | 47 ++++++++-------- mods/ts/rules/defaults.yaml | 20 ++++--- 7 files changed, 178 insertions(+), 92 deletions(-) diff --git a/OpenRA.Game/Support/ConditionExpression.cs b/OpenRA.Game/Support/ConditionExpression.cs index bf406ead9c..90cbc52a0b 100644 --- a/OpenRA.Game/Support/ConditionExpression.cs +++ b/OpenRA.Game/Support/ConditionExpression.cs @@ -61,6 +61,13 @@ namespace OpenRA.Support : base(symbol, index, Associativity.Left, 0) { } } + class NumberToken : Token + { + public readonly int Value; + public NumberToken(int index, string symbol) + : base(symbol, index, Associativity.Left, 0) { Value = int.Parse(symbol); } + } + class AndToken : BinaryOperationToken { public AndToken(int index) : base("&&", index) { } } class OrToken : BinaryOperationToken { public OrToken(int index) : base("||", index) { } } class EqualsToken : BinaryOperationToken { public EqualsToken(int index) : base("==", index) { } } @@ -120,17 +127,17 @@ namespace OpenRA.Support for (var i = 0; i < tokens.Count - 1; i++) { - // Unary tokens must be followed by a variable, another unary token, or an opening parenthesis - if (tokens[i] is UnaryOperationToken && !(tokens[i + 1] is VariableToken || tokens[i + 1] is UnaryOperationToken - || tokens[i + 1] is OpenParenToken)) + // Unary tokens must be followed by a variable, number, another unary token, or an opening parenthesis + if (tokens[i] is UnaryOperationToken && !(tokens[i + 1] is VariableToken || tokens[i + 1] is NumberToken + || tokens[i + 1] is UnaryOperationToken || tokens[i + 1] is OpenParenToken)) throw new InvalidDataException("Unexpected token `{0}` at index {1}".F(tokens[i].Symbol, tokens[i].Index)); // Disallow empty parentheses if (tokens[i] is OpenParenToken && tokens[i + 1] is CloseParenToken) throw new InvalidDataException("Empty parenthesis at index {0}".F(tokens[i].Index)); - // A variable must be followed by a binary operation or by a closing parenthesis - if (tokens[i] is VariableToken && !(tokens[i + 1] is BinaryOperationToken || tokens[i + 1] is CloseParenToken)) + // A variable or number must be followed by a binary operation or by a closing parenthesis + if ((tokens[i] is VariableToken || tokens[i] is NumberToken) && !(tokens[i + 1] is BinaryOperationToken || tokens[i + 1] is CloseParenToken)) throw new InvalidDataException("Missing binary operation at index {0}".F(tokens[i + 1].Index)); } @@ -147,8 +154,8 @@ namespace OpenRA.Support for (var i = 1; i < tokens.Count - 1; i++) { if (tokens[i] is BinaryOperationToken && ( - !(tokens[i - 1] is CloseParenToken || tokens[i - 1] is VariableToken) || - !(tokens[i + 1] is OpenParenToken || tokens[i + 1] is VariableToken || tokens[i + 1] is UnaryOperationToken))) + !(tokens[i - 1] is CloseParenToken || tokens[i - 1] is VariableToken || tokens[i - 1] is NumberToken) || + !(tokens[i + 1] is OpenParenToken || tokens[i + 1] is VariableToken || tokens[i + 1] is NumberToken || tokens[i + 1] is UnaryOperationToken))) throw new InvalidDataException("Unexpected token `{0}` at index `{1}`".F(tokens[i].Symbol, tokens[i].Index)); } @@ -206,6 +213,27 @@ namespace OpenRA.Support throw new InvalidDataException("Unexpected character '|' at index {0}".F(start)); } + // Scan forwards until we find an non-digit character + if (c == '-' || char.IsDigit(expression[i])) + { + i++; + for (; i < expression.Length; i++) + { + c = expression[i]; + if (!char.IsDigit(c)) + { + if (!char.IsWhiteSpace(c) && c != '(' && c != ')' && c != '!' && c != '&' && c != '|' && c != '=' && c != '+') + throw new InvalidDataException("Number and variable merged at index {0}".F(start)); + + // Put the bad character back for the next parse attempt + i--; + return new NumberToken(start, expression.Substring(start, i - start + 1)); + } + } + + return new NumberToken(start, expression.Substring(start)); + } + // Scan forwards until we find an invalid name character for (; i < expression.Length; i++) { @@ -255,7 +283,7 @@ namespace OpenRA.Support while (!((temp = s.Pop()) is OpenParenToken)) yield return temp; } - else if (t is VariableToken) + else if (t is VariableToken || t is NumberToken) yield return t; else { @@ -286,6 +314,8 @@ namespace OpenRA.Support ApplyBinaryOperation(s, (x, y) => (y == x) ? 1 : 0); else if (t is NotToken) ApplyUnaryOperation(s, x => (x > 0) ? 0 : 1); + else if (t is NumberToken) + s.Push(((NumberToken)t).Value); else if (t is VariableToken) s.Push(ParseSymbol((VariableToken)t, symbols)); } diff --git a/OpenRA.Test/OpenRA.Game/ConditionExpressionTest.cs b/OpenRA.Test/OpenRA.Game/ConditionExpressionTest.cs index 3dccfb5489..94a8c06e1b 100644 --- a/OpenRA.Test/OpenRA.Game/ConditionExpressionTest.cs +++ b/OpenRA.Test/OpenRA.Game/ConditionExpressionTest.cs @@ -37,11 +37,27 @@ namespace OpenRA.Test Assert.True(new ConditionExpression(expression).Evaluate(testValues) > 0, expression); } + void AssertValue(string expression, int value) + { + Assert.AreEqual(value, new ConditionExpression(expression).Evaluate(testValues), expression); + } + void AssertParseFailure(string expression) { Assert.Throws(typeof(InvalidDataException), () => new ConditionExpression(expression).Evaluate(testValues), expression); } + [TestCase(TestName = "Numbers")] + public void TestNumbers() + { + AssertParseFailure("1a"); + AssertValue("0", 0); + AssertValue("1", 1); + AssertValue("12", 12); + AssertValue("-1", -1); + AssertValue("-12", -12); + } + [TestCase(TestName = "AND operation")] public void TestAnd() { @@ -49,6 +65,10 @@ namespace OpenRA.Test AssertFalse("false && false"); AssertFalse("true && false"); AssertFalse("false && true"); + AssertValue("2 && false", 0); + AssertValue("false && 2", 0); + AssertValue("3 && 2", 2); + AssertValue("2 && 3", 3); } [TestCase(TestName = "OR operation")] @@ -58,6 +78,10 @@ namespace OpenRA.Test AssertFalse("false || false"); AssertTrue("true || false"); AssertTrue("false || true"); + AssertValue("2 || false", 2); + AssertValue("false || 2", 2); + AssertValue("3 || 2", 3); + AssertValue("2 || 3", 2); } [TestCase(TestName = "Equals operation")] @@ -67,6 +91,15 @@ namespace OpenRA.Test AssertTrue("false == false"); AssertFalse("true == false"); AssertFalse("false == true"); + AssertTrue("1 == 1"); + AssertTrue("0 == 0"); + AssertFalse("1 == 0"); + AssertTrue("1 == true"); + AssertFalse("1 == false"); + AssertTrue("0 == false"); + AssertFalse("0 == true"); + AssertValue("12 == 12", 1); + AssertValue("1 == 12", 0); } [TestCase(TestName = "Not-equals operation")] @@ -76,15 +109,26 @@ namespace OpenRA.Test AssertFalse("false != false"); AssertTrue("true != false"); AssertTrue("false != true"); + AssertValue("1 != 2", 1); + AssertValue("1 != 1", 0); + AssertFalse("1 != true"); + AssertFalse("0 != false"); + AssertTrue("1 != false"); + AssertTrue("0 != true"); } [TestCase(TestName = "NOT operation")] public void TestNOT() { - AssertFalse("!true"); - AssertTrue("!false"); - AssertTrue("!!true"); - AssertFalse("!!false"); + AssertValue("!true", 0); + AssertValue("!false", 1); + AssertValue("!!true", 1); + AssertValue("!!false", 0); + AssertValue("!0", 1); + AssertValue("!1", 0); + AssertValue("!5", 0); + AssertValue("!!5", 1); + AssertValue("!-5", 1); } [TestCase(TestName = "Precedence")] @@ -134,7 +178,7 @@ namespace OpenRA.Test AssertParseFailure("true false"); AssertParseFailure("true & false"); AssertParseFailure("true | false"); - AssertParseFailure("true / false"); + AssertParseFailure("true : false"); AssertParseFailure("true & false && !"); AssertParseFailure("(true && !)"); AssertParseFailure("&& false"); diff --git a/mods/cnc/rules/defaults.yaml b/mods/cnc/rules/defaults.yaml index 60eee515ef..622e799649 100644 --- a/mods/cnc/rules/defaults.yaml +++ b/mods/cnc/rules/defaults.yaml @@ -17,66 +17,69 @@ ^GainsExperience: GainsExperience: Conditions: - 200: rank-veteran-1 - 400: rank-veteran-2 - 800: rank-veteran-3 - 1600: rank-elite + 200: rank-veteran + 400: rank-veteran + 800: rank-veteran + 1600: rank-veteran + GrantCondition@RANK-ELITE: + RequiresCondition: rank-veteran == 4 + Condition: rank-elite DamageMultiplier@RANK-1: - RequiresCondition: rank-veteran-1 && !rank-veteran-2 + RequiresCondition: rank-veteran == 1 Modifier: 95 DamageMultiplier@RANK-2: - RequiresCondition: rank-veteran-2 && !rank-veteran-3 + RequiresCondition: rank-veteran == 2 Modifier: 90 DamageMultiplier@RANK-3: - RequiresCondition: rank-veteran-3 && !rank-elite + RequiresCondition: rank-veteran == 3 Modifier: 85 DamageMultiplier@RANK-ELITE: RequiresCondition: rank-elite Modifier: 75 FirepowerMultiplier@RANK-1: - RequiresCondition: rank-veteran-1 && !rank-veteran-2 + RequiresCondition: rank-veteran == 1 Modifier: 105 FirepowerMultiplier@RANK-2: - RequiresCondition: rank-veteran-2 && !rank-veteran-3 + RequiresCondition: rank-veteran == 2 Modifier: 110 FirepowerMultiplier@RANK-3: - RequiresCondition: rank-veteran-3 && !rank-elite + RequiresCondition: rank-veteran == 3 Modifier: 120 FirepowerMultiplier@RANK-ELITE: RequiresCondition: rank-elite Modifier: 130 SpeedMultiplier@RANK-1: - RequiresCondition: rank-veteran-1 && !rank-veteran-2 + RequiresCondition: rank-veteran == 1 Modifier: 105 SpeedMultiplier@RANK-2: - RequiresCondition: rank-veteran-2 && !rank-veteran-3 + RequiresCondition: rank-veteran == 2 Modifier: 110 SpeedMultiplier@RANK-3: - RequiresCondition: rank-veteran-3 && !rank-elite + RequiresCondition: rank-veteran == 3 Modifier: 120 SpeedMultiplier@RANK-ELITE: RequiresCondition: rank-elite Modifier: 140 ReloadDelayMultiplier@RANK-1: - RequiresCondition: rank-veteran-1 && !rank-veteran-2 + RequiresCondition: rank-veteran == 1 Modifier: 95 ReloadDelayMultiplier@RANK-2: - RequiresCondition: rank-veteran-2 && !rank-veteran-3 + RequiresCondition: rank-veteran == 2 Modifier: 90 ReloadDelayMultiplier@RANK-3: - RequiresCondition: rank-veteran-3 && !rank-elite + RequiresCondition: rank-veteran == 3 Modifier: 85 ReloadDelayMultiplier@RANK-ELITE: RequiresCondition: rank-elite Modifier: 75 InaccuracyMultiplier@RANK-1: - RequiresCondition: rank-veteran-1 && !rank-veteran-2 + RequiresCondition: rank-veteran == 1 Modifier: 90 InaccuracyMultiplier@RANK-2: - RequiresCondition: rank-veteran-2 && !rank-veteran-3 + RequiresCondition: rank-veteran == 2 Modifier: 80 InaccuracyMultiplier@RANK-3: - RequiresCondition: rank-veteran-3 && !rank-elite + RequiresCondition: rank-veteran == 3 Modifier: 70 InaccuracyMultiplier@RANK-ELITE: RequiresCondition: rank-elite @@ -92,21 +95,21 @@ Sequence: rank-veteran-1 Palette: effect ReferencePoint: Bottom, Right - RequiresCondition: rank-veteran-1 && !rank-veteran-2 + RequiresCondition: rank-veteran == 1 ZOffset: 256 WithDecoration@RANK-2: Image: rank Sequence: rank-veteran-2 Palette: effect ReferencePoint: Bottom, Right - RequiresCondition: rank-veteran-2 && !rank-veteran-3 + RequiresCondition: rank-veteran == 2 ZOffset: 256 WithDecoration@RANK-3: Image: rank Sequence: rank-veteran-3 Palette: effect ReferencePoint: Bottom, Right - RequiresCondition: rank-veteran-3 && !rank-elite + RequiresCondition: rank-veteran == 3 ZOffset: 256 WithDecoration@RANK-ELITE: Image: rank diff --git a/mods/d2k/rules/defaults.yaml b/mods/d2k/rules/defaults.yaml index 01ce63a580..1febaaa6f7 100644 --- a/mods/d2k/rules/defaults.yaml +++ b/mods/d2k/rules/defaults.yaml @@ -17,66 +17,69 @@ ^GainsExperience: GainsExperience: Conditions: - 200: rank-veteran-1 - 400: rank-veteran-2 - 800: rank-veteran-3 - 1600: rank-elite + 200: rank-veteran + 400: rank-veteran + 800: rank-veteran + 1600: rank-veteran + GrantCondition@RANK-ELITE: + RequiresCondition: rank-veteran == 4 + Condition: rank-elite DamageMultiplier@RANK-1: - RequiresCondition: rank-veteran-1 && !rank-veteran-2 + RequiresCondition: rank-veteran == 1 Modifier: 96 DamageMultiplier@RANK-2: - RequiresCondition: rank-veteran-2 && !rank-veteran-3 + RequiresCondition: rank-veteran == 2 Modifier: 92 DamageMultiplier@RANK-3: - RequiresCondition: rank-veteran-3 && !rank-elite + RequiresCondition: rank-veteran == 3 Modifier: 88 DamageMultiplier@RANK-ELITE: RequiresCondition: rank-elite Modifier: 80 FirepowerMultiplier@RANK-1: - RequiresCondition: rank-veteran-1 && !rank-veteran-2 + RequiresCondition: rank-veteran == 1 Modifier: 105 FirepowerMultiplier@RANK-2: - RequiresCondition: rank-veteran-2 && !rank-veteran-3 + RequiresCondition: rank-veteran == 2 Modifier: 110 FirepowerMultiplier@RANK-3: - RequiresCondition: rank-veteran-3 && !rank-elite + RequiresCondition: rank-veteran == 3 Modifier: 115 FirepowerMultiplier@RANK-ELITE: RequiresCondition: rank-elite Modifier: 125 SpeedMultiplier@RANK-1: - RequiresCondition: rank-veteran-1 && !rank-veteran-2 + RequiresCondition: rank-veteran == 1 Modifier: 105 SpeedMultiplier@RANK-2: - RequiresCondition: rank-veteran-2 && !rank-veteran-3 + RequiresCondition: rank-veteran == 2 Modifier: 110 SpeedMultiplier@RANK-3: - RequiresCondition: rank-veteran-3 && !rank-elite + RequiresCondition: rank-veteran == 3 Modifier: 115 SpeedMultiplier@RANK-ELITE: RequiresCondition: rank-elite Modifier: 125 ReloadDelayMultiplier@RANK-1: - RequiresCondition: rank-veteran-1 && !rank-veteran-2 + RequiresCondition: rank-veteran == 1 Modifier: 96 ReloadDelayMultiplier@RANK-2: - RequiresCondition: rank-veteran-2 && !rank-veteran-3 + RequiresCondition: rank-veteran == 2 Modifier: 92 ReloadDelayMultiplier@RANK-3: - RequiresCondition: rank-veteran-3 && !rank-elite + RequiresCondition: rank-veteran == 3 Modifier: 88 ReloadDelayMultiplier@RANK-ELITE: RequiresCondition: rank-elite Modifier: 80 InaccuracyMultiplier@RANK-1: - RequiresCondition: rank-veteran-1 && !rank-veteran-2 + RequiresCondition: rank-veteran == 1 Modifier: 90 InaccuracyMultiplier@RANK-2: - RequiresCondition: rank-veteran-2 && !rank-veteran-3 + RequiresCondition: rank-veteran == 2 Modifier: 80 InaccuracyMultiplier@RANK-3: - RequiresCondition: rank-veteran-3 && !rank-elite + RequiresCondition: rank-veteran == 3 Modifier: 70 InaccuracyMultiplier@RANK-ELITE: RequiresCondition: rank-elite @@ -93,21 +96,21 @@ Sequence: rank-veteran-1 Palette: effect ReferencePoint: Bottom, Right - RequiresCondition: rank-veteran-1 && !rank-veteran-2 + RequiresCondition: rank-veteran == 1 ZOffset: 256 WithDecoration@RANK-2: Image: rank Sequence: rank-veteran-2 Palette: effect ReferencePoint: Bottom, Right - RequiresCondition: rank-veteran-2 && !rank-veteran-3 + RequiresCondition: rank-veteran == 2 ZOffset: 256 WithDecoration@RANK-3: Image: rank Sequence: rank-veteran-3 Palette: effect ReferencePoint: Bottom, Right - RequiresCondition: rank-veteran-3 && !rank-elite + RequiresCondition: rank-veteran == 3 ZOffset: 256 WithDecoration@RANK-ELITE: Image: rank diff --git a/mods/ra/maps/soviet-03/rules.yaml b/mods/ra/maps/soviet-03/rules.yaml index 13514e9f62..84073817b3 100644 --- a/mods/ra/maps/soviet-03/rules.yaml +++ b/mods/ra/maps/soviet-03/rules.yaml @@ -59,14 +59,11 @@ V05: DOG: # HACK: Disable experience without killing the linter -GainsExperience: - 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-VETERAN: + Condition: rank-veteran ExternalCondition@RANK-ELITE: Condition: rank-elite + -GrantCondition@RANK-ELITE: SPY: Mobile: diff --git a/mods/ra/rules/defaults.yaml b/mods/ra/rules/defaults.yaml index 42ff6e71ec..87595c442c 100644 --- a/mods/ra/rules/defaults.yaml +++ b/mods/ra/rules/defaults.yaml @@ -16,66 +16,69 @@ ^GainsExperience: GainsExperience: Conditions: - 200: rank-veteran-1 - 400: rank-veteran-2 - 800: rank-veteran-3 - 1600: rank-elite + 200: rank-veteran + 400: rank-veteran + 800: rank-veteran + 1600: rank-veteran + GrantCondition@RANK-ELITE: + RequiresCondition: rank-veteran == 4 + Condition: rank-elite DamageMultiplier@RANK-1: - RequiresCondition: rank-veteran-1 && !rank-veteran-2 + RequiresCondition: rank-veteran == 1 Modifier: 95 DamageMultiplier@RANK-2: - RequiresCondition: rank-veteran-2 && !rank-veteran-3 + RequiresCondition: rank-veteran == 2 Modifier: 90 DamageMultiplier@RANK-3: - RequiresCondition: rank-veteran-3 && !rank-elite + RequiresCondition: rank-veteran == 3 Modifier: 85 DamageMultiplier@RANK-ELITE: RequiresCondition: rank-elite Modifier: 75 FirepowerMultiplier@RANK-1: - RequiresCondition: rank-veteran-1 && !rank-veteran-2 + RequiresCondition: rank-veteran == 1 Modifier: 105 FirepowerMultiplier@RANK-2: - RequiresCondition: rank-veteran-2 && !rank-veteran-3 + RequiresCondition: rank-veteran == 2 Modifier: 110 FirepowerMultiplier@RANK-3: - RequiresCondition: rank-veteran-3 && !rank-elite + RequiresCondition: rank-veteran == 3 Modifier: 120 FirepowerMultiplier@RANK-ELITE: RequiresCondition: rank-elite Modifier: 130 SpeedMultiplier@RANK-1: - RequiresCondition: rank-veteran-1 && !rank-veteran-2 + RequiresCondition: rank-veteran == 1 Modifier: 105 SpeedMultiplier@RANK-2: - RequiresCondition: rank-veteran-2 && !rank-veteran-3 + RequiresCondition: rank-veteran == 2 Modifier: 110 SpeedMultiplier@RANK-3: - RequiresCondition: rank-veteran-3 && !rank-elite + RequiresCondition: rank-veteran == 3 Modifier: 120 SpeedMultiplier@RANK-ELITE: RequiresCondition: rank-elite Modifier: 140 ReloadDelayMultiplier@RANK-1: - RequiresCondition: rank-veteran-1 && !rank-veteran-2 + RequiresCondition: rank-veteran == 1 Modifier: 95 ReloadDelayMultiplier@RANK-2: - RequiresCondition: rank-veteran-2 && !rank-veteran-3 + RequiresCondition: rank-veteran == 2 Modifier: 90 ReloadDelayMultiplier@RANK-3: - RequiresCondition: rank-veteran-3 && !rank-elite + RequiresCondition: rank-veteran == 3 Modifier: 85 ReloadDelayMultiplier@RANK-ELITE: RequiresCondition: rank-elite Modifier: 75 InaccuracyMultiplier@RANK-1: - RequiresCondition: rank-veteran-1 && !rank-veteran-2 + RequiresCondition: rank-veteran == 1 Modifier: 90 InaccuracyMultiplier@RANK-2: - RequiresCondition: rank-veteran-2 && !rank-veteran-3 + RequiresCondition: rank-veteran == 2 Modifier: 80 InaccuracyMultiplier@RANK-3: - RequiresCondition: rank-veteran-3 && !rank-elite + RequiresCondition: rank-veteran == 3 Modifier: 70 InaccuracyMultiplier@RANK-ELITE: RequiresCondition: rank-elite @@ -91,21 +94,21 @@ Sequence: rank-veteran-1 Palette: effect ReferencePoint: Bottom, Right - RequiresCondition: rank-veteran-1 && !rank-veteran-2 + RequiresCondition: rank-veteran == 1 ZOffset: 256 WithDecoration@RANK-2: Image: rank Sequence: rank-veteran-2 Palette: effect ReferencePoint: Bottom, Right - RequiresCondition: rank-veteran-2 && !rank-veteran-3 + RequiresCondition: rank-veteran == 2 ZOffset: 256 WithDecoration@RANK-3: Image: rank Sequence: rank-veteran-3 Palette: effect ReferencePoint: Bottom, Right - RequiresCondition: rank-veteran-3 && !rank-elite + RequiresCondition: rank-veteran == 3 ZOffset: 256 WithDecoration@RANK-ELITE: Image: rank diff --git a/mods/ts/rules/defaults.yaml b/mods/ts/rules/defaults.yaml index f9c218adf4..5e65fde983 100644 --- a/mods/ts/rules/defaults.yaml +++ b/mods/ts/rules/defaults.yaml @@ -18,28 +18,34 @@ ^GainsExperience: GainsExperience: Conditions: - 500: rank-veteran - 1000: rank-elite + 500: rank + 1000: rank + GrantCondition@RANK-VETERAN: + RequiresCondition: rank == 1 + Condition: rank-veteran + GrantCondition@RANK-ELITE: + RequiresCondition: rank == 2 + Condition: rank-elite FirepowerMultiplier@VETERAN: - RequiresCondition: rank-veteran && !rank-elite + RequiresCondition: rank-veteran Modifier: 110 FirepowerMultiplier@ELITE: RequiresCondition: rank-elite Modifier: 130 DamageMultiplier@VETERAN: - RequiresCondition: rank-veteran && !rank-elite + RequiresCondition: rank-veteran Modifier: 90 DamageMultiplier@ELITE: RequiresCondition: rank-elite Modifier: 75 SpeedMultiplier@VETERAN: - RequiresCondition: rank-veteran && !rank-elite + RequiresCondition: rank-veteran Modifier: 120 SpeedMultiplier@ELITE: RequiresCondition: rank-elite Modifier: 140 ReloadDelayMultiplier@VETERAN: - RequiresCondition: rank-veteran && !rank-elite + RequiresCondition: rank-veteran Modifier: 90 ReloadDelayMultiplier@ELITE: RequiresCondition: rank-elite @@ -55,7 +61,7 @@ Sequence: veteran Palette: ra ReferencePoint: Bottom, Right - RequiresCondition: rank-veteran && !rank-elite + RequiresCondition: rank-veteran ZOffset: 256 WithDecoration@ELITE: Image: rank