diff --git a/OpenRA.Game/Support/ConditionExpression.cs b/OpenRA.Game/Support/ConditionExpression.cs index be4fb98798..e027a18df4 100644 --- a/OpenRA.Game/Support/ConditionExpression.cs +++ b/OpenRA.Game/Support/ConditionExpression.cs @@ -123,6 +123,10 @@ namespace OpenRA.Support Or, Equals, NotEquals, + LessThan, + LessThanOrEqual, + GreaterThan, + GreaterThanOrEqual, Invalid } @@ -130,6 +134,7 @@ namespace OpenRA.Support enum Precedence { Unary = 16, + Relation = 9, Equality = 8, And = 4, Or = 3, @@ -211,6 +216,18 @@ namespace OpenRA.Support case TokenType.NotEquals: yield return new TokenTypeInfo("!=", Precedence.Equality, OperandSides.Both); continue; + case TokenType.LessThan: + yield return new TokenTypeInfo("<", Precedence.Relation, OperandSides.Both); + continue; + case TokenType.LessThanOrEqual: + yield return new TokenTypeInfo("<=", Precedence.Relation, OperandSides.Both); + continue; + case TokenType.GreaterThan: + yield return new TokenTypeInfo(">", Precedence.Relation, OperandSides.Both); + continue; + case TokenType.GreaterThanOrEqual: + yield return new TokenTypeInfo(">=", Precedence.Relation, OperandSides.Both); + continue; } throw new InvalidProgramException("CreateTokenTypeInfoEnumeration is missing a TokenTypeInfo entry for TokenType.{0}".F( @@ -258,6 +275,26 @@ namespace OpenRA.Support return TokenType.Not; + case '<': + i++; + if (i < expression.Length && expression[i] == '=') + { + i++; + return TokenType.LessThanOrEqual; + } + + return TokenType.LessThan; + + case '>': + i++; + if (i < expression.Length && expression[i] == '=') + { + i++; + return TokenType.GreaterThanOrEqual; + } + + return TokenType.GreaterThan; + case '=': i++; if (i < expression.Length && expression[i] == '=') @@ -625,6 +662,38 @@ namespace OpenRA.Support continue; } + case TokenType.LessThan: + { + var y = ast.Pop(ExpressionType.Int); + var x = ast.Pop(ExpressionType.Int); + ast.Push(Expressions.Expression.LessThan(x, y)); + continue; + } + + case TokenType.LessThanOrEqual: + { + var y = ast.Pop(ExpressionType.Int); + var x = ast.Pop(ExpressionType.Int); + ast.Push(Expressions.Expression.LessThanOrEqual(x, y)); + continue; + } + + case TokenType.GreaterThan: + { + var y = ast.Pop(ExpressionType.Int); + var x = ast.Pop(ExpressionType.Int); + ast.Push(Expressions.Expression.GreaterThan(x, y)); + continue; + } + + case TokenType.GreaterThanOrEqual: + { + var y = ast.Pop(ExpressionType.Int); + var x = ast.Pop(ExpressionType.Int); + ast.Push(Expressions.Expression.GreaterThanOrEqual(x, y)); + continue; + } + case TokenType.Number: { ast.Push(Expressions.Expression.Constant(((NumberToken)t).Value)); diff --git a/OpenRA.Test/OpenRA.Game/ConditionExpressionTest.cs b/OpenRA.Test/OpenRA.Game/ConditionExpressionTest.cs index 71394a1050..78d6af181d 100644 --- a/OpenRA.Test/OpenRA.Game/ConditionExpressionTest.cs +++ b/OpenRA.Test/OpenRA.Game/ConditionExpressionTest.cs @@ -146,8 +146,67 @@ namespace OpenRA.Test AssertValue("!-5", 1); } - [TestCase(TestName = "Precedence")] - public void TestPrecedence() + [TestCase(TestName = "Relation operations")] + public void TestRelations() + { + AssertValue("2 < 5", 1); + AssertValue("0 < 5", 1); + AssertValue("5 < 2", 0); + AssertValue("5 < 5", 0); + AssertValue("-5 < 0", 1); + AssertValue("-2 < -5", 0); + AssertValue("-5 < -2", 1); + AssertValue("-5 < -5", 0); + AssertValue("-7 < 5", 1); + AssertValue("0 <= 5", 1); + AssertValue("2 <= 5", 1); + AssertValue("5 <= 2", 0); + AssertValue("5 <= 5", 1); + AssertValue("5 <= 0", 0); + AssertValue("-2 <= -5", 0); + AssertValue("-5 <= -2", 1); + AssertValue("-5 <= -5", 1); + AssertValue("-7 <= 5", 1); + AssertValue("0 <= -5", 0); + AssertValue("-5 <= 0", 1); + AssertValue("5 > 2", 1); + AssertValue("0 > 5", 0); + AssertValue("2 > 5", 0); + AssertValue("5 > 5", 0); + AssertValue("5 > 0", 1); + AssertValue("-2 > -5", 1); + AssertValue("-7 > -5", 0); + AssertValue("-5 > -5", 0); + AssertValue("-4 > -5", 1); + AssertValue("5 >= 0", 1); + AssertValue("0 >= 5", 0); + AssertValue("5 >= 2", 1); + AssertValue("2 >= 5", 0); + AssertValue("5 >= 5", 1); + AssertValue("-5 >= 0", 0); + AssertValue("0 >= -5", 1); + AssertValue("-7 >= 5", 0); + AssertValue("-5 >= -5", 1); + AssertValue("-4 >= -5", 1); + } + + [TestCase(TestName = "Relation Mixed Precedence")] + public void TestRelationMixedPrecedence() + { + AssertValue("5 <= 5 && 2 > 1", 1); + AssertValue("5 > 5 || 2 > 1", 1); + AssertValue("5 > 5 || 1 > 1", 0); + AssertValue("5 <= 5 == 2 > 1", 1); + AssertValue("5 > 5 == 2 > 1", 0); + AssertValue("5 > 5 == 1 > 1", 1); + AssertValue("5 <= 5 != 2 > 1", 0); + AssertValue("5 > 5 != 2 > 1", 1); + AssertValue("5 > 5 != 1 > 1", 0); + AssertValue("5 > 5 != 1 >= 1", 1); + } + + [TestCase(TestName = "AND-OR Precedence")] + public void TestAndOrPrecedence() { AssertTrue("true && false || true"); AssertFalse("false || false && true"); diff --git a/mods/cnc/rules/defaults.yaml b/mods/cnc/rules/defaults.yaml index 622e799649..cf38e6798c 100644 --- a/mods/cnc/rules/defaults.yaml +++ b/mods/cnc/rules/defaults.yaml @@ -22,7 +22,7 @@ 800: rank-veteran 1600: rank-veteran GrantCondition@RANK-ELITE: - RequiresCondition: rank-veteran == 4 + RequiresCondition: rank-veteran >= 4 Condition: rank-elite DamageMultiplier@RANK-1: RequiresCondition: rank-veteran == 1 diff --git a/mods/d2k/rules/defaults.yaml b/mods/d2k/rules/defaults.yaml index 1febaaa6f7..51ac9d51de 100644 --- a/mods/d2k/rules/defaults.yaml +++ b/mods/d2k/rules/defaults.yaml @@ -22,7 +22,7 @@ 800: rank-veteran 1600: rank-veteran GrantCondition@RANK-ELITE: - RequiresCondition: rank-veteran == 4 + RequiresCondition: rank-veteran >= 4 Condition: rank-elite DamageMultiplier@RANK-1: RequiresCondition: rank-veteran == 1 diff --git a/mods/ra/rules/defaults.yaml b/mods/ra/rules/defaults.yaml index 87595c442c..91bf77b37e 100644 --- a/mods/ra/rules/defaults.yaml +++ b/mods/ra/rules/defaults.yaml @@ -21,7 +21,7 @@ 800: rank-veteran 1600: rank-veteran GrantCondition@RANK-ELITE: - RequiresCondition: rank-veteran == 4 + RequiresCondition: rank-veteran >= 4 Condition: rank-elite DamageMultiplier@RANK-1: RequiresCondition: rank-veteran == 1 diff --git a/mods/ts/rules/defaults.yaml b/mods/ts/rules/defaults.yaml index 5e65fde983..92bdfbaea9 100644 --- a/mods/ts/rules/defaults.yaml +++ b/mods/ts/rules/defaults.yaml @@ -24,7 +24,7 @@ RequiresCondition: rank == 1 Condition: rank-veteran GrantCondition@RANK-ELITE: - RequiresCondition: rank == 2 + RequiresCondition: rank >= 2 Condition: rank-elite FirepowerMultiplier@VETERAN: RequiresCondition: rank-veteran