diff --git a/Source/Parser/Expressions/Trigger/MemoryValueExpression.cs b/Source/Parser/Expressions/Trigger/MemoryValueExpression.cs index c8c0c692..1d1a8f41 100644 --- a/Source/Parser/Expressions/Trigger/MemoryValueExpression.cs +++ b/Source/Parser/Expressions/Trigger/MemoryValueExpression.cs @@ -1548,23 +1548,37 @@ public ErrorExpression BuildTrigger(TriggerBuilderContext context, ExpressionBas for (int i = 0; i < memoryAccessors.Count; i++) { var memoryAccessor = memoryAccessors[i]; - var memoryAccessorRememberedExpression = memoryAccessor.RememberedValue; - if (memoryAccessorRememberedExpression != null) + for (int j = 0; j < 2; j++) { - if (rememberedExpression == null) + MemoryValueExpression memoryAccessorRememberedExpression = null; + if (j == 0) { - rememberedExpression = memoryAccessorRememberedExpression; + memoryAccessorRememberedExpression = memoryAccessor.RememberedValue; + } + else + { + var rememberExpression = memoryAccessor.MemoryAccessor as RememberRecallExpression; + if (rememberExpression != null) + memoryAccessorRememberedExpression = rememberExpression.RememberedValue; + } + + if (memoryAccessorRememberedExpression != null) + { + if (rememberedExpression == null) + { + rememberedExpression = memoryAccessorRememberedExpression; - if (i != 0) + if (i != 0) + { + memoryAccessors.RemoveAt(i); + memoryAccessors.Insert(0, memoryAccessor); + } + } + else if (rememberedExpression != memoryAccessorRememberedExpression) { - memoryAccessors.RemoveAt(i); - memoryAccessors.Insert(0, memoryAccessor); + return new ErrorExpression("Cannot combine multiple expressions with unique Remembered requirements", memoryAccessor); } } - else if (rememberedExpression != memoryAccessorRememberedExpression) - { - return new ErrorExpression("Cannot combine multiple expressions with unique Remembered requirements", memoryAccessor); - } } } diff --git a/Source/Parser/Expressions/Trigger/ModifiedMemoryAccessorExpression.cs b/Source/Parser/Expressions/Trigger/ModifiedMemoryAccessorExpression.cs index 26205548..b3ae90d3 100644 --- a/Source/Parser/Expressions/Trigger/ModifiedMemoryAccessorExpression.cs +++ b/Source/Parser/Expressions/Trigger/ModifiedMemoryAccessorExpression.cs @@ -446,6 +446,17 @@ public ExpressionBase ApplyMathematic(ExpressionBase right, MathematicOperation field = FieldFactory.CreateField(right); if (field.Type == FieldType.None) { + // cannot simplify to a single field. + // going to need to use remember/recall to generate the value. + if (ModifyingOperator != RequirementOperator.None) + { + // if the "this" object is modified, it also needs to be remember/recalled + // which only works if both sides match. that will be determined later. + // for now, just remember/recall both sides. + MemoryAccessor = RememberRecallExpression.WrapInRemember(this.Clone()); + ModifyingOperator = RequirementOperator.None; + } + _rememberModifier = RememberRecallExpression.WrapInRemember(right); if (_rememberModifier != null) { @@ -912,8 +923,6 @@ public ErrorExpression BuildTrigger(TriggerBuilderContext context) { if (_rememberModifier.RememberedValue != context.RememberedValue) { - context.RememberedValue = _rememberModifier.RememberedValue; - var error = _rememberModifier.BuildTrigger(context); if (error != null) return error; @@ -922,6 +931,10 @@ public ErrorExpression BuildTrigger(TriggerBuilderContext context) // into the next expression. remove it. we'll add it back soon. context.Trigger.Remove(context.LastRequirement); } + + var rememberExpression = MemoryAccessor as RememberRecallExpression; + if (rememberExpression != null && rememberExpression.RememberedValue != _rememberModifier.RememberedValue) + return new ErrorExpression("Cannot combine multiple expressions with unique Remembered requirements", this); } MemoryAccessor.BuildTrigger(context); diff --git a/Source/Parser/Expressions/Trigger/RememberRecallExpression.cs b/Source/Parser/Expressions/Trigger/RememberRecallExpression.cs index d2901cb8..04a51318 100644 --- a/Source/Parser/Expressions/Trigger/RememberRecallExpression.cs +++ b/Source/Parser/Expressions/Trigger/RememberRecallExpression.cs @@ -99,8 +99,23 @@ public override void GetMinMax(out long min, out long max) } public override ErrorExpression BuildTrigger(TriggerBuilderContext context) + { + // if the RememberedValue is already being Remembered, just output the {recall} + if (context.RememberedValue != RememberedValue) + { + var error = AppendRemember(context); + if (error != null) + return error; + } + + // this outputs the {recall} + return base.BuildTrigger(context); + } + + private ErrorExpression AppendRemember(TriggerBuilderContext context) { RememberedValue.BuildTrigger(context); + context.RememberedValue = RememberedValue; // if the RememberedValue only contains ModifiedMemoryReferences, BuildTrigger // will append an extra condition so the result can be compared to. @@ -121,9 +136,9 @@ public override ErrorExpression BuildTrigger(TriggerBuilderContext context) if (lastRequirement.Type != RequirementType.None) return new ErrorExpression("Cannot remember requirement", RememberedValue); - lastRequirement.Type = RequirementType.Remember; - return base.BuildTrigger(context); + lastRequirement.Type = RequirementType.Remember; + return null; } } } diff --git a/Source/Parser/Internal/TriggerBuilderContext.cs b/Source/Parser/Internal/TriggerBuilderContext.cs index c02ef5f2..3482d4d3 100644 --- a/Source/Parser/Internal/TriggerBuilderContext.cs +++ b/Source/Parser/Internal/TriggerBuilderContext.cs @@ -565,6 +565,8 @@ public void BeginAlt() { Trigger = new List(); Achievement.AlternateRequirements.Add(Trigger); + + RememberedValue = null; } } diff --git a/Tests/Parser/Expressions/Trigger/MemoryAccessorExpressionTests.cs b/Tests/Parser/Expressions/Trigger/MemoryAccessorExpressionTests.cs index 65ec615d..7a58ef71 100644 --- a/Tests/Parser/Expressions/Trigger/MemoryAccessorExpressionTests.cs +++ b/Tests/Parser/Expressions/Trigger/MemoryAccessorExpressionTests.cs @@ -43,6 +43,8 @@ public void TestAppendString(string input) [TestCase("prior(bit0(0x001234))", "p0xM001234")] [TestCase("dword(dword(0x1234) + ((word(0x2345) & 0x3FF) * 8 + 4))", "K:0x 002345&1023_A:{recall}*8_K:0xX001234_I:{recall}_0xX000004")] + [TestCase("byte(0x3B0038 - ((dword_be(0x3180) & 0x15) / (dword_be(0x3180) & 0x15)) * 0x4C068)", + "K:0xG003180&21_K:{recall}/{recall}_I:{recall}*4294655896_0xH3b0038")] public void TestBuildTrigger(string input, string expected) { var accessor = TriggerExpressionTests.Parse(input); diff --git a/Tests/Parser/Expressions/Trigger/RequirementClauseExpressionTests.cs b/Tests/Parser/Expressions/Trigger/RequirementClauseExpressionTests.cs index 902dd167..494febb9 100644 --- a/Tests/Parser/Expressions/Trigger/RequirementClauseExpressionTests.cs +++ b/Tests/Parser/Expressions/Trigger/RequirementClauseExpressionTests.cs @@ -69,7 +69,7 @@ public void TestBuildTrigger(string input, string expected) [TestCase("byte(0x001234) == 3 && trigger_when(__ornext(byte(word(0x002345) + 8) == 4 || (byte(word(0x002345) + 8) >= 7 && byte(word(0x002345) + 8) <= 10)))", "0xH001234=3_I:0x 002345_N:0xH000008>=7_I:0x 002345_O:0xH000008<=10_I:0x 002345_T:0xH000008=4")] [TestCase("dword(dword(0x1234) + ((word(0x2345) & 0x3FF) * 8 + 4)) == 6 && prev(dword(dword(0x1234) + ((word(0x2345) & 0x3FF) * 8 + 4))) == 0", - "K:0x 002345&1023_A:{recall}*8_K:0xX001234_I:{recall}_0xX000004=6_K:0x 002345&1023_A:{recall}*8_K:0xX001234_I:{recall}_d0xX000004=0")] // remember chain will be duplicated here, it'll get optimized out later + "K:0x 002345&1023_A:{recall}*8_K:0xX001234_I:{recall}_0xX000004=6_I:{recall}_d0xX000004=0")] public void TestBuildAchievement(string input, string expected) { var clause = TriggerExpressionTests.Parse(input);