private ParseErrorExpression ExecuteAchievementComparison(ComparisonExpression comparison, InterpreterScope scope) { _equalityModifiers.Clear(); var context = scope.GetContext <TriggerBuilderContext>(); var insertIndex = context.Trigger.Count; var left = comparison.Left; var right = comparison.Right; var op = GetRequirementOperator(comparison.Operation); if (left.Type == ExpressionType.IntegerConstant) { if (right.Type == ExpressionType.IntegerConstant) { // comparing two constants, convert to always_true or always_false var requirement = new Requirement { Left = new Field { Type = FieldType.Value, Value = (uint)((IntegerConstantExpression)left).Value }, Operator = op, Right = new Field { Type = FieldType.Value, Value = (uint)((IntegerConstantExpression)right).Value }, }; if (Evaluate(requirement)) { context.Trigger.Add(AlwaysTrueFunction.CreateAlwaysTrueRequirement()); } else { context.Trigger.Add(AlwaysFalseFunction.CreateAlwaysFalseRequirement()); } return(null); } // swap the operands and operator so the constant is on the right var temp = left; left = right; right = temp; op = GetReversedRequirementOperator(op); } var error = ExecuteAchievementExpression(left, scope); if (error != null) { return(error); } var integerRight = right as IntegerConstantExpression; if (integerRight != null) { int newValue = integerRight.Value; // SubSource of a memory accessor may cause overflow - if comparing for less than, // modifiers should not be merged into the compare target if (_equalityModifiers.Count > 0 && (op == RequirementOperator.LessThan || op == RequirementOperator.LessThanOrEqual)) { bool hasSubSource = false; for (int i = context.Trigger.Count - 2; i >= 0; i++) { var requirementType = context.Trigger.ElementAt(i).Type; if (requirementType == RequirementType.SubSource) { hasSubSource = true; break; } else if (requirementType != RequirementType.AddSource && requirementType != RequirementType.AddHits) { break; } } if (hasSubSource) { var last = context.Trigger.Last(); context.Trigger.Remove(last); foreach (var modifier in _equalityModifiers) { if (modifier.Operation != MathematicOperation.Subtract && modifier.Operation != MathematicOperation.Add) { return(new ParseErrorExpression("Cannot normalize expression containing SubSource", left)); } context.Trigger.Add(new Requirement { Type = (modifier.Operation == MathematicOperation.Subtract) ? RequirementType.AddSource : RequirementType.SubSource, Left = new Field { Type = FieldType.Value, Value = (uint)modifier.Amount }, Operator = RequirementOperator.None, Right = new Field() }); } context.Trigger.Add(last); _equalityModifiers.Clear(); } } while (_equalityModifiers.Count > 0) { var originalValue = newValue; var modifier = _equalityModifiers.Pop(); newValue = modifier.Apply(newValue); var restoredValue = modifier.Remove(newValue); switch (op) { case RequirementOperator.Equal: if (restoredValue != originalValue) { return(new ParseErrorExpression("Result can never be true using integer math", comparison)); } break; case RequirementOperator.NotEqual: if (restoredValue != originalValue) { return(new ParseErrorExpression("Result is always true using integer math", comparison)); } break; case RequirementOperator.GreaterThanOrEqual: if (restoredValue != originalValue) { op = RequirementOperator.GreaterThan; } break; } } var requirement = context.LastRequirement; requirement.Operator = op; requirement.Right = new Field { Size = requirement.Left.Size, Type = FieldType.Value, Value = (uint)newValue }; } else { var leftModifiers = new Stack <ValueModifier>(_equalityModifiers.Reverse()); _equalityModifiers.Clear(); error = ExecuteAchievementExpression(right, scope); if (error != null) { return(error); } if (leftModifiers.Count > 0 || _equalityModifiers.Count > 0) { var rightValue = 1234567; var leftValue = rightValue; while (leftModifiers.Count > 0) { var modifier = leftModifiers.Pop(); leftValue = ValueModifier.Apply(leftValue, MathematicExpression.GetOppositeOperation(modifier.Operation), modifier.Amount); } while (_equalityModifiers.Count > 0) { var modifier = _equalityModifiers.Pop(); rightValue = ValueModifier.Apply(rightValue, MathematicExpression.GetOppositeOperation(modifier.Operation), modifier.Amount); } var diff = leftValue - rightValue; if (diff != 0) { var modifier = new Requirement(); if (diff < 0) { modifier.Left = new Field { Type = FieldType.Value, Value = (uint)(-diff) }; modifier.Type = RequirementType.SubSource; } else { modifier.Left = new Field { Type = FieldType.Value, Value = (uint)diff }; modifier.Type = RequirementType.AddSource; } ((IList <Requirement>)context.Trigger).Insert(insertIndex, modifier); } } var extraRequirement = context.LastRequirement; context.Trigger.Remove(extraRequirement); var requirement = context.LastRequirement; if (requirement != null) { requirement.Operator = op; requirement.Right = extraRequirement.Left; } } return(null); }
private ParseErrorExpression ExecuteAchievementMathematic(MathematicExpression mathematic, InterpreterScope scope) { var left = mathematic.Left; var operation = mathematic.Operation; var context = scope.GetContext <TriggerBuilderContext>(); ParseErrorExpression error; ExpressionBase right; if (!mathematic.Right.ReplaceVariables(scope, out right)) { return((ParseErrorExpression)right); } if (operation == MathematicOperation.Subtract && (right is FunctionCallExpression || right is MathematicExpression)) { // if subtracting a non-integer, swap the order to perform a SubSource var newEqualityModifiers = new Stack <ValueModifier>(); var oldEqualityModifiers = _equalityModifiers; _equalityModifiers = newEqualityModifiers; var requirements = new List <Requirement>(); var innerContext = new TriggerBuilderContext() { Trigger = requirements }; var innerScope = new InterpreterScope(scope) { Context = innerContext }; // generate the condition for the right side error = ExecuteAchievementExpression(right, innerScope); _equalityModifiers = oldEqualityModifiers; if (error != null) { return(error); } foreach (var requirement in requirements) { switch (requirement.Type) { case RequirementType.None: case RequirementType.AddSource: requirement.Type = RequirementType.SubSource; break; case RequirementType.SubSource: requirement.Type = RequirementType.AddSource; break; default: return(new ParseErrorExpression("Cannot normalize expression for negation", mathematic)); } context.Trigger.Add(requirement); } foreach (var modifier in newEqualityModifiers) { switch (modifier.Operation) { case MathematicOperation.Add: _equalityModifiers.Push(new ValueModifier(MathematicOperation.Subtract, modifier.Amount)); break; case MathematicOperation.Subtract: _equalityModifiers.Push(new ValueModifier(MathematicOperation.Add, modifier.Amount)); break; default: return(new ParseErrorExpression("Cannot normalize expression for negation", mathematic)); } } right = mathematic.Left; operation = MathematicOperation.Add; } else { // generate the condition for the first expression error = ExecuteAchievementExpression(left, scope); if (error != null) { return(error); } } var integerOperand = right as IntegerConstantExpression; if (integerOperand != null) { var oppositeOperation = MathematicExpression.GetOppositeOperation(operation); if (oppositeOperation == MathematicOperation.None) { return(new ParseErrorExpression(String.Format("Cannot normalize expression to eliminate {0}", MathematicExpression.GetOperatorType(mathematic.Operation)), mathematic)); } var priority = MathematicExpression.GetPriority(mathematic.Operation); if (priority != MathematicPriority.Add) { if (context.Trigger.Count > 1) { var previousRequirementType = context.Trigger.ElementAt(context.Trigger.Count - 2).Type; if (previousRequirementType == RequirementType.AddSource || previousRequirementType == RequirementType.SubSource) { return(new ParseErrorExpression(String.Format("Cannot normalize expression to eliminate {0}", MathematicExpression.GetOperatorType(mathematic.Operation)), mathematic)); } } if (_equalityModifiers.Any(e => MathematicExpression.GetPriority(e.Operation) != priority)) { return(new ParseErrorExpression(String.Format("Cannot normalize expression to eliminate {0}", MathematicExpression.GetOperatorType(mathematic.Operation)), mathematic)); } } _equalityModifiers.Push(new ValueModifier(oppositeOperation, integerOperand.Value)); return(null); } if (operation == MathematicOperation.Add) { foreach (var modifier in _equalityModifiers) { if (MathematicExpression.GetPriority(modifier.Operation) != MathematicPriority.Add) { return(new ParseErrorExpression(String.Format("Cannot normalize expression to eliminate {0}", MathematicExpression.GetOperatorType(MathematicExpression.GetOppositeOperation(modifier.Operation))), mathematic)); } } // adding two memory accessors - make sure previous is AddSource or SubSource if (context.LastRequirement.Type != RequirementType.SubSource) { context.LastRequirement.Type = RequirementType.AddSource; context.LastRequirement.Operator = RequirementOperator.None; context.LastRequirement.Right = new Field(); } } else { return(new ParseErrorExpression(String.Format("Cannot normalize expression to eliminate {0}", MathematicExpression.GetOperatorType(mathematic.Operation)), mathematic)); } // generate the condition for the second expression error = ExecuteAchievementExpression(right, scope); if (error != null) { error = new ParseErrorExpression(error.Message, mathematic); } return(error); }