private ParseErrorExpression ExecuteAchievementClause(ExpressionBase expression, InterpreterScope scope) { var error = ExecuteAchievementExpression(expression, scope); if (error != null) { return(error); } var context = scope.GetContext <TriggerBuilderContext>(); if (context.LastRequirement == null) { // no requirements generated return(new ParseErrorExpression("Incomplete trigger condition", expression)); } if (context.LastRequirement.Operator == RequirementOperator.None && scope.GetContext <ValueBuilderContext>() == null) { // final condition is a combining condition or an incomplete comparison return(new ParseErrorExpression("Incomplete trigger condition", expression)); } return(null); }
public override bool Evaluate(InterpreterScope scope, out ExpressionBase result) { var context = scope.GetContext <AchievementScriptContext>(); if (context == null) { result = new ParseErrorExpression(Name.Name + " has no meaning outside of an achievement script"); return(false); } scope = new InterpreterScope(scope) { Context = new RichPresenceDisplayContext { RichPresence = context.RichPresence, } }; if (!base.Evaluate(scope, out result)) { return(false); } var displayString = ((StringConstantExpression)result).Value; if (!SetDisplayString(context.RichPresence, displayString, scope, out result)) { return(false); } return(true); }
public override bool Evaluate(InterpreterScope scope, out ExpressionBase result) { var stringExpression = GetStringParameter(scope, "format_string", out result); if (stringExpression == null) { return(false); } var context = scope.GetContext <AchievementScriptContext>(); Debug.Assert(context != null); scope = new InterpreterScope(scope); scope.Context = new RichPresenceDisplayContext { RichPresence = context.RichPresence }; string displayString; result = ProcessRichPresenceDisplay(stringExpression, scope, out displayString); if (result != null) { return(false); } if (!SetDisplayString(context.RichPresence, displayString, scope, out result)) { return(false); } return(true); }
public override bool BuildMacro(RichPresenceDisplayFunction.RichPresenceDisplayContext context, InterpreterScope scope, out ExpressionBase result) { var macro = GetStringParameter(scope, "macro", out result); if (macro == null) { return(false); } var expression = GetParameter(scope, "expression", out result); if (expression == null) { return(false); } var value = TriggerBuilderContext.GetValueString(expression, scope, out result); if (value == null) { return(false); } var functionCall = scope.GetContext <FunctionCallExpression>(); var valueFormat = GetValueFormat(macro.Value); context.RichPresence.AddValueField(functionCall, macro.Value, valueFormat); result = new StringConstantExpression(String.Format("@{0}({1})", macro.Value, value)); return(true); }
protected virtual bool SetDisplayString(RichPresenceBuilder richPresence, string displayString, InterpreterScope scope, out ExpressionBase result) { result = null; richPresence.DisplayString = displayString; var functionCall = scope.GetContext <FunctionCallExpression>(); if (functionCall != null && functionCall.FunctionName.Name == this.Name.Name) { richPresence.Line = functionCall.Line; } return(true); }
public override bool Evaluate(InterpreterScope scope, out ExpressionBase result) { var context = scope.GetContext <RichPresenceDisplayContext>(); if (context == null) { result = new ParseErrorExpression(Name.Name + " has no meaning outside of a rich_presence_display call"); return(false); } return(BuildMacro(context, scope, out result)); }
public override bool BuildMacro(RichPresenceDisplayFunction.RichPresenceDisplayContext context, InterpreterScope scope, out ExpressionBase result) { var name = GetStringParameter(scope, "name", out result); if (name == null) { return(false); } var expression = GetParameter(scope, "expression", out result); if (expression == null) { return(false); } var dictionary = GetDictionaryParameter(scope, "dictionary", out result); if (dictionary == null) { return(false); } var fallback = GetStringParameter(scope, "fallback", out result); if (fallback == null) { return(false); } var value = TriggerBuilderContext.GetValueString(expression, scope, out result); if (value == null) { return(false); } var functionCall = scope.GetContext <FunctionCallExpression>(); var error = context.RichPresence.AddLookupField(functionCall, name.Value, dictionary, fallback); if (error != null) { result = error; return(false); } result = new StringConstantExpression(String.Format("@{0}({1})", name.Value, value)); return(true); }
private ParseErrorExpression ExecuteAchievementExpression(ExpressionBase expression, InterpreterScope scope) { ExpressionBase operand; switch (expression.Type) { case ExpressionType.FunctionCall: return(ExecuteAchievementFunction((FunctionCallExpression)expression, scope)); case ExpressionType.Conditional: return(ExecuteAchievementConditional((ConditionalExpression)expression, scope)); case ExpressionType.Comparison: return(ExecuteAchievementComparison((ComparisonExpression)expression, scope)); case ExpressionType.Mathematic: return(ExecuteAchievementMathematic((MathematicExpression)expression, scope)); case ExpressionType.Variable: if (!expression.ReplaceVariables(scope, out operand)) { return(new ParseErrorExpression(operand, expression)); } if (expression is FunctionReferenceExpression) { return(new ParseErrorExpression("Function used like a variable", expression)); } return(ExecuteAchievementExpression(operand, scope)); case ExpressionType.BooleanConstant: { var context = scope.GetContext <TriggerBuilderContext>(); if (((BooleanConstantExpression)expression).Value == true) { context.Trigger.Add(AlwaysTrueFunction.CreateAlwaysTrueRequirement()); } else { context.Trigger.Add(AlwaysFalseFunction.CreateAlwaysFalseRequirement()); } return(null); } } return(new ParseErrorExpression("Cannot generate trigger from " + expression.Type, expression)); }
public override bool ReplaceVariables(InterpreterScope scope, out ExpressionBase result) { var tally = scope.GetContext <TallyFunction>(); // explicitly in tally clause if (tally == null) { var assignment = scope.GetInterpreterContext <AssignmentExpression>(); // in generic assignment clause - may be used byte rich_presence_display - will determine later if (assignment == null) { result = new ParseErrorExpression(Name.Name + " has no meaning outside of a tally call"); return(false); } } return(base.ReplaceVariables(scope, out result)); }
protected bool IsInRichPresenceDisplayClause(InterpreterScope scope, out ExpressionBase result) { var richPresence = scope.GetContext <RichPresenceDisplayContext>(); // explicitly in rich_presence_display clause if (richPresence == null) { var assignment = scope.GetInterpreterContext <AssignmentExpression>(); // in generic assignment clause - may be used byte rich_presence_display - will determine later if (assignment == null) { result = new ParseErrorExpression(Name.Name + " has no meaning outside of a rich_presence_display call"); return(false); } } result = null; return(true); }
protected bool IsInTriggerClause(InterpreterScope scope, out ExpressionBase result) { var triggerContext = scope.GetContext <TriggerBuilderContext>(); if (triggerContext == null) // explicitly in trigger clause { var assignment = scope.GetInterpreterContext <AssignmentExpression>(); if (assignment == null) // in generic assignment clause - may be used in a trigger - will determine later { result = new ParseErrorExpression(Name.Name + " has no meaning outside of a trigger clause"); return(false); } } result = null; return(true); }
private ParseErrorExpression ExecuteAchievementConditional(ConditionalExpression condition, InterpreterScope scope) { ParseErrorExpression error; var context = scope.GetContext <TriggerBuilderContext>(); switch (condition.Operation) { case ConditionalOperation.Not: return(new ParseErrorExpression("! operator should have been normalized out", condition)); case ConditionalOperation.And: error = ExecuteAchievementExpression(condition.Left, scope); if (error != null) { return(error); } error = ExecuteAchievementExpression(condition.Right, scope); if (error != null) { return(error); } return(null); case ConditionalOperation.Or: BeginAlt(context); error = ExecuteAchievementExpression(condition.Left, scope); if (error != null) { return(error); } BeginAlt(context); error = ExecuteAchievementExpression(condition.Right, scope); if (error != null) { return(error); } return(null); } return(new ParseErrorExpression("Unsupported conditional", condition)); }
private ParseErrorExpression ExecuteAchievementFunction(FunctionCallExpression functionCall, InterpreterScope scope) { ExpressionBase evaluated; if (!functionCall.ReplaceVariables(scope, out evaluated)) { return((ParseErrorExpression)evaluated); } functionCall = evaluated as FunctionCallExpression; if (functionCall != null) { var context = scope.GetContext <TriggerBuilderContext>(); return(context.CallFunction(functionCall, scope)); } return(ExecuteAchievementExpression(evaluated, scope)); }
public override bool ReplaceVariables(InterpreterScope scope, out ExpressionBase result) { var comparison = GetParameter(scope, "comparison", out result); if (comparison == null) { return(false); } // trigger_when(A || B) => trigger_when(A) || trigger_when(B) // trigger_when(A && B) => trigger_when(A) && trigger_when(B) var condition = comparison as ConditionalExpression; if (condition != null && !condition.IsLogicalUnit) { if (condition.Operation == ConditionalOperation.Or) { // OR within an AND must be kept as an OrNext if (scope.GetContext <TriggerWhenFunction>() != null) { condition.IsLogicalUnit = true; return(base.ReplaceVariables(scope, out result)); } return(SplitConditions(scope, condition, ConditionalOperation.Or, out result)); } if (condition.Operation == ConditionalOperation.And) { var newScope = new InterpreterScope(scope) { Context = this }; return(SplitConditions(newScope, condition, ConditionalOperation.And, out result)); } } return(base.ReplaceVariables(scope, out result)); }
/// <summary> /// Populates an achievement from an expression. /// </summary> /// <param name="achievement">The achievement to populate.</param> /// <param name="expression">The expression to populate from.</param> /// <param name="scope">The scope.</param> /// <param name="result">[out] The error if not successful.</param> /// <returns><c>true</c> if successful, <c>false</c> if not.</returns> public static bool ProcessAchievementConditions(ScriptInterpreterAchievementBuilder achievement, ExpressionBase expression, InterpreterScope scope, out ExpressionBase result) { ParseErrorExpression parseError; if (!achievement.PopulateFromExpression(expression, scope, out parseError)) { result = parseError; return(false); } // only optimize at the outermost level if (ReferenceEquals(scope.GetOutermostContext <TriggerBuilderContext>(), scope.GetContext <TriggerBuilderContext>())) { var message = achievement.Optimize(); if (message != null) { result = new ParseErrorExpression(message, expression); return(false); } } result = null; return(true); }
private ParseErrorExpression ExecuteAchievementComparison(ComparisonExpression comparison, InterpreterScope scope) { var left = comparison.Left; var right = comparison.Right; if (left.Type == ExpressionType.Comparison || right.Type == ExpressionType.Comparison) { return(new ParseErrorExpression("comparison did not evaluate to a valid comparison", comparison)); } var context = scope.GetContext <TriggerBuilderContext>(); var op = GetRequirementOperator(comparison.Operation); var leftField = CreateFieldFromExpression(left); if (leftField.Type != FieldType.None) { var rightField = CreateFieldFromExpression(right); if (rightField.Type != FieldType.None) { // comparing two constants, convert to always_true or always_false var requirement = new Requirement { Left = leftField, Operator = op, Right = rightField, }; if (requirement.Evaluate() == true) { 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 = Requirement.GetReversedRequirementOperator(op); } var error = ExecuteAchievementExpression(left, scope); if (error != null) { return(ParseErrorExpression.WrapError(error, "Invalid value", left)); } if (right.Type == ExpressionType.IntegerConstant || right.Type == ExpressionType.FloatConstant) { var lastRequirement = context.LastRequirement; if (lastRequirement.Operator != RequirementOperator.None) { // try to rearrange so the last requirement doesn't have a modifier for (int i = context.Trigger.Count - 2; i >= 0; --i) { var requirement = context.Trigger.ElementAt(i); if (requirement.Type == RequirementType.AddSource && requirement.Operator == RequirementOperator.None) { context.Trigger.Remove(lastRequirement); context.Trigger.Add(new Requirement { Left = requirement.Left }); requirement.Left = lastRequirement.Left; requirement.Operator = lastRequirement.Operator; requirement.Right = lastRequirement.Right; lastRequirement = context.LastRequirement; break; } if (requirement.Type != RequirementType.AddSource && requirement.Type != RequirementType.SubSource && requirement.Type != RequirementType.AddAddress) { break; } } if (lastRequirement.Operator != RequirementOperator.None) { // last requirement still has a modifier, have to add a dummy condition. lastRequirement.Type = RequirementType.AddSource; context.Trigger.Add(new Requirement { Left = new Field { Size = lastRequirement.Left.Size, Type = FieldType.Value, Value = 0 } }); lastRequirement = context.LastRequirement; } } lastRequirement.Operator = op; lastRequirement.Right = CreateFieldFromExpression(right); } else { error = ExecuteAchievementExpression(right, scope); if (error != null) { return(ParseErrorExpression.WrapError(error, "Invalid value", right)); } var extraRequirement = context.LastRequirement; ((IList <Requirement>)context.Trigger).RemoveAt(context.Trigger.Count - 1); var requirement = context.LastRequirement; if (requirement != null) { if (requirement.Type == RequirementType.AddAddress) { return(HandleAddAddressComparison(comparison, (IList <Requirement>)context.Trigger, op, extraRequirement)); } else if (context.Trigger.Count > 1 && context.Trigger.ElementAt(context.Trigger.Count - 2).Type == RequirementType.AddAddress) { // if left side is an AddAddress chain, but right side is a not, we have to keep the // dummy condition to prevent the AddAddress from modifying the memory address on the // right side. integers are handled above. requirement.Type = RequirementType.AddSource; extraRequirement.Right = extraRequirement.Left; extraRequirement.Left = new Field { Type = FieldType.Value, Value = 0 }; extraRequirement.Operator = op; context.Trigger.Add(extraRequirement); } else if (requirement.Operator != RequirementOperator.None) { // if left side is a complex expression (a * 4), add a new dummy condition for the comparison requirement.Type = RequirementType.AddSource; extraRequirement.Right = extraRequirement.Left; extraRequirement.Left = new Field { Type = FieldType.Value, Value = 0 }; extraRequirement.Operator = op; context.Trigger.Add(extraRequirement); } else { // no AddAddress on either side, just merge the conditions requirement.Operator = op; requirement.Right = extraRequirement.Left; } } } return(null); }
private ParseErrorExpression ExecuteAchievementConditional(ConditionalExpression condition, InterpreterScope scope) { return(ExecuteAchievementConditional(condition, scope, scope.GetContext <TriggerBuilderContext>())); }
private ParseErrorExpression ExecuteAchievementMathematic(MathematicExpression mathematic, InterpreterScope scope) { ParseErrorExpression error; var context = scope.GetContext <TriggerBuilderContext>(); var operation = mathematic.Operation; switch (operation) { case MathematicOperation.Add: case MathematicOperation.Subtract: break; case MathematicOperation.Multiply: case MathematicOperation.Divide: case MathematicOperation.BitwiseAnd: // generate the condition for the right side Field operand = CreateFieldFromExpression(mathematic.Right); if (operand.Type == FieldType.None) { var requirements = new List <Requirement>(); var innerContext = new TriggerBuilderContext() { Trigger = requirements }; var innerScope = new InterpreterScope(scope) { Context = innerContext }; error = ExecuteAchievementExpression(mathematic.Right, innerScope); if (error != null) { return(error); } if (requirements.Count > 1) { return(new ParseErrorExpression("Multiplication by complex value not supported", mathematic)); } operand = requirements[0].Left; } // generate the conditions for the left side error = ExecuteAchievementExpression(mathematic.Left, scope); if (error != null) { return(error); } if (context.LastRequirement.Operator != RequirementOperator.None) { return(new ParseErrorExpression("Cannot generate condition using both " + context.LastRequirement.Operator + " and " + operation)); } switch (operation) { case MathematicOperation.Multiply: context.LastRequirement.Operator = RequirementOperator.Multiply; break; case MathematicOperation.Divide: context.LastRequirement.Operator = RequirementOperator.Divide; break; case MathematicOperation.BitwiseAnd: context.LastRequirement.Operator = RequirementOperator.BitwiseAnd; break; } context.LastRequirement.Right = operand; return(null); default: return(new ParseErrorExpression("Cannot normalize expression to eliminate " + MathematicExpression.GetOperatorType(operation), mathematic)); } var left = mathematic.Left; ExpressionBase right; if (!mathematic.Right.ReplaceVariables(scope, out right)) { return((ParseErrorExpression)right); } Field field = CreateFieldFromExpression(right); if (field.Type != FieldType.None) { context.Trigger.Add(new Requirement { Type = (operation == MathematicOperation.Add) ? RequirementType.AddSource : RequirementType.SubSource, Left = field, Operator = RequirementOperator.None, Right = new Field() }); } if (operation == MathematicOperation.Subtract && (right is FunctionCallExpression || right is MathematicExpression)) { // if subtracting a non-integer, swap the order to perform a SubSource 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); 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; case RequirementType.AddAddress: // AddAddress is allowed as long as it's not the last requirement if (ReferenceEquals(requirement, requirements.Last())) { return(new ParseErrorExpression("Cannot normalize expression for negation", mathematic)); } break; default: return(new ParseErrorExpression("Cannot normalize expression for negation", mathematic)); } context.Trigger.Add(requirement); } right = mathematic.Left; operation = MathematicOperation.Add; } else { // generate the condition for the first expression error = ExecuteAchievementExpression(left, scope); if (error != null) { return(error); } } if (field.Type != FieldType.None) { return(null); } if (operation == MathematicOperation.Add) { // adding two memory accessors - make sure previous is AddSource or SubSource var lastRequirement = context.LastRequirement; if (lastRequirement.Type != RequirementType.SubSource) { lastRequirement.Type = RequirementType.AddSource; if (lastRequirement.IsComparison) { lastRequirement.Operator = RequirementOperator.None; lastRequirement.Right = new Field(); } } } else { return(new ParseErrorExpression(String.Format("Cannot normalize expression to eliminate {0}", MathematicExpression.GetOperatorType(mathematic.Operation)), mathematic)); } field = CreateFieldFromExpression(right); if (field.Type != FieldType.None) { context.Trigger.Add(new Requirement { Type = RequirementType.None, Left = field, Operator = RequirementOperator.None, Right = new Field() }); return(null); } // generate the condition for the second expression error = ExecuteAchievementExpression(right, scope); if (error != null) { error = new ParseErrorExpression(error.Message, mathematic); } // make sure the mathematic expression doesn't result in a comparison { var lastRequirement = context.LastRequirement; if (lastRequirement.IsComparison) { lastRequirement.Operator = RequirementOperator.None; lastRequirement.Right = new Field(); } } return(error); }
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 ExecuteAchievementFunction(FunctionCallExpression functionCall, InterpreterScope scope) { var context = scope.GetContext <TriggerBuilderContext>(); return(context.CallFunction(functionCall, scope)); }
public override bool Evaluate(InterpreterScope scope, out ExpressionBase result) { var leaderboard = new Leaderboard(); var stringExpression = GetStringParameter(scope, "title", out result); if (stringExpression == null) { return(false); } leaderboard.Title = stringExpression.Value; stringExpression = GetStringParameter(scope, "description", out result); if (stringExpression == null) { return(false); } leaderboard.Description = stringExpression.Value; leaderboard.Start = ProcessTrigger(scope, "start", out result); if (leaderboard.Start == null) { return(false); } leaderboard.Cancel = ProcessTrigger(scope, "cancel", out result); if (leaderboard.Cancel == null) { return(false); } leaderboard.Submit = ProcessTrigger(scope, "submit", out result); if (leaderboard.Submit == null) { return(false); } leaderboard.Value = ProcessValue(scope, "value", out result); if (leaderboard.Value == null) { return(false); } var format = GetStringParameter(scope, "format", out result); if (format == null) { return(false); } leaderboard.Format = Leaderboard.ParseFormat(format.Value); if (leaderboard.Format == ValueFormat.None) { result = new ParseErrorExpression(format.Value + " is not a supported leaderboard format", format); return(false); } var functionCall = scope.GetContext <FunctionCallExpression>(); if (functionCall != null && functionCall.FunctionName.Name == this.Name.Name) { leaderboard.SourceLine = functionCall.Line; } var context = scope.GetContext <AchievementScriptContext>(); Debug.Assert(context != null); context.Leaderboards.Add(leaderboard); return(true); }
private ParseErrorExpression ProcessRichPresenceDisplay(StringConstantExpression stringExpression, InterpreterScope scope, out string displayString) { displayString = null; ExpressionBase result; var varargs = GetParameter(scope, "varargs", out result) as ArrayExpression; if (varargs == null) { var error = result as ParseErrorExpression; if (error == null) { error = new ParseErrorExpression("unexpected varargs", stringExpression); } return(error); } var context = scope.GetContext <RichPresenceDisplayContext>(); var builder = context.DisplayString; var tokenizer = new PositionalTokenizer(Tokenizer.CreateTokenizer(stringExpression.Value)); while (tokenizer.NextChar != '\0') { var token = tokenizer.ReadTo('{'); builder.Append(token.ToString()); if (tokenizer.NextChar == '\0') { break; } var positionalTokenColumn = tokenizer.Column; tokenizer.Advance(); var index = tokenizer.ReadNumber(); if (tokenizer.NextChar != '}') { return(new ParseErrorExpression("Invalid positional token", stringExpression.Line, stringExpression.Column + positionalTokenColumn, stringExpression.Line, stringExpression.Column + tokenizer.Column - 1)); } tokenizer.Advance(); var parameterIndex = Int32.Parse(index.ToString()); if (parameterIndex >= varargs.Entries.Count) { return(new ParseErrorExpression("Invalid parameter index: " + parameterIndex, stringExpression.Line, stringExpression.Column + positionalTokenColumn, stringExpression.Line, stringExpression.Column + tokenizer.Column - 1)); } var functionCall = varargs.Entries[parameterIndex] as FunctionCallExpression; var functionDefinition = scope.GetFunction(functionCall.FunctionName.Name); if (functionDefinition == null) { return(new ParseErrorExpression("Unknown function: " + functionCall.FunctionName.Name)); } var richPresenceFunction = functionDefinition as FunctionDefinition; if (richPresenceFunction == null) { return(new ParseErrorExpression(functionCall.FunctionName.Name + " cannot be called as a rich_presence_display parameter", functionCall)); } var error = richPresenceFunction.BuildMacro(context, scope, functionCall); if (error != null) { return(new ParseErrorExpression(error, functionCall)); } } displayString = builder.ToString(); return(null); }
public override ParseErrorExpression BuildTrigger(TriggerBuilderContext context, InterpreterScope scope, FunctionCallExpression functionCall) { var error = base.BuildTrigger(context, scope, functionCall); if (error != null) { return(error); } ExpressionBase result; var format = functionCall.Parameters.ElementAt(2); if (!format.ReplaceVariables(scope, out result)) { return((ParseErrorExpression)result); } StringConstantExpression formatStr = result as StringConstantExpression; if (formatStr == null) { return(new ParseErrorExpression("format is not a string", format)); } if (formatStr.Value != "raw") { if (scope.GetContext <ValueBuilderContext>() != null) { return(new ParseErrorExpression("Value fields only support raw measured values", format)); } if (formatStr.Value == "percent") { context.LastRequirement.Type = RequirementType.MeasuredPercent; } else { return(new ParseErrorExpression("Unknown format: " + formatStr.Value, format)); } } var when = functionCall.Parameters.ElementAt(1); var builder = new ScriptInterpreterAchievementBuilder(); if (!TriggerBuilderContext.ProcessAchievementConditions(builder, when, scope, out result)) { return new ParseErrorExpression("when did not evaluate to a valid comparison", when) { InnerError = (ParseErrorExpression)result } } ; error = builder.CollapseForSubClause(); if (error != null) { return(error); } if (builder.CoreRequirements.Count != 1 || builder.CoreRequirements.First().Evaluate() != true) { foreach (var requirement in builder.CoreRequirements) { if (requirement.Type == RequirementType.None || requirement.Type == RequirementType.AndNext) { requirement.Type = RequirementType.MeasuredIf; } context.Trigger.Add(requirement); } } 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); }
public override bool Evaluate(InterpreterScope scope, out ExpressionBase result) { var achievement = new ScriptInterpreterAchievementBuilder(); var stringExpression = GetStringParameter(scope, "title", out result); if (stringExpression == null) { return(false); } achievement.Title = stringExpression.Value; stringExpression = GetStringParameter(scope, "description", out result); if (stringExpression == null) { return(false); } achievement.Description = stringExpression.Value; stringExpression = GetStringParameter(scope, "badge", out result); if (stringExpression == null) { return(false); } achievement.BadgeName = stringExpression.Value; var integerExpression = GetIntegerParameter(scope, "points", out result); if (integerExpression == null) { return(false); } achievement.Points = integerExpression.Value; integerExpression = GetIntegerParameter(scope, "id", out result); if (integerExpression == null) { return(false); } achievement.Id = integerExpression.Value; var trigger = GetParameter(scope, "trigger", out result); if (trigger == null) { return(false); } if (!TriggerBuilderContext.ProcessAchievementConditions(achievement, trigger, scope, out result)) { if (result.Location.Start != trigger.Location.Start || result.Location.End != trigger.Location.End) { var error = (ParseErrorExpression)result; result = new ParseErrorExpression(error.Message, trigger) { InnerError = error }; } return(false); } var newAchievement = achievement.ToAchievement(); var functionCall = scope.GetOutermostContext <FunctionCallExpression>(); if (functionCall != null) { newAchievement.SourceLine = functionCall.Location.Start.Line; } var context = scope.GetContext <AchievementScriptContext>(); Debug.Assert(context != null); context.Achievements.Add(newAchievement); return(true); }
internal bool Run(ExpressionGroupCollection expressionGroups, IScriptInterpreterCallback callback) { AchievementScriptContext scriptContext = null; InterpreterScope scope = expressionGroups.Scope; if (scope != null) { scriptContext = scope.GetContext <AchievementScriptContext>(); } if (scriptContext == null) { scriptContext = new AchievementScriptContext(); scope = new InterpreterScope(expressionGroups.Scope ?? GetGlobalScope()) { Context = scriptContext }; } expressionGroups.ResetErrors(); bool result = true; foreach (var expressionGroup in expressionGroups.Groups) { if (expressionGroup.NeedsEvaluated) { if (scriptContext.Achievements == null) { scriptContext.Achievements = new List <Achievement>(); } if (scriptContext.Leaderboards == null) { scriptContext.Leaderboards = new List <Leaderboard>(); } if (scriptContext.RichPresence == null) { scriptContext.RichPresence = new RichPresenceBuilder(); } if (!Evaluate(expressionGroup.Expressions, scope, callback)) { var error = Error; if (error != null) { expressionGroups.AddEvaluationError(error); } result = false; } if (scriptContext.Achievements.Count > 0) { expressionGroup.GeneratedAchievements = scriptContext.Achievements; scriptContext.Achievements = null; } else if (expressionGroup.GeneratedAchievements != null) { expressionGroup.GeneratedAchievements = null; } if (scriptContext.Leaderboards.Count > 0) { expressionGroup.GeneratedLeaderboards = scriptContext.Leaderboards; scriptContext.Leaderboards = null; } else if (expressionGroup.GeneratedLeaderboards != null) { expressionGroup.GeneratedLeaderboards = null; } if (!scriptContext.RichPresence.IsEmpty) { expressionGroup.GeneratedRichPresence = scriptContext.RichPresence; scriptContext.RichPresence = null; } expressionGroup.MarkEvaluated(); } } if (!ReferenceEquals(scope, expressionGroups.Scope)) { if (scope.FunctionCount > 0 || scope.VariableCount > 0) { if (expressionGroups.Scope != null) { expressionGroups.Scope.Merge(scope); } else { expressionGroups.Scope = scope; } } } _achievements.Clear(); _leaderboards.Clear(); _richPresence.Clear(); foreach (var expressionGroup in expressionGroups.Groups) { if (expressionGroup.GeneratedAchievements != null) { _achievements.AddRange(expressionGroup.GeneratedAchievements); } if (expressionGroup.GeneratedLeaderboards != null) { _leaderboards.AddRange(expressionGroup.GeneratedLeaderboards); } if (expressionGroup.GeneratedRichPresence != null) { var error = _richPresence.Merge(expressionGroup.GeneratedRichPresence); if (error != null) { expressionGroups.AddEvaluationError(error); result = false; } } } double minimumVersion = 0.30; foreach (var achievement in _achievements) { var achievementMinimumVersion = AchievementBuilder.GetMinimumVersion(achievement); if (achievementMinimumVersion > minimumVersion) { minimumVersion = achievementMinimumVersion; } } foreach (var leaderboard in _leaderboards) { var leaderboardMinimumVersion = AchievementBuilder.GetMinimumVersion(leaderboard); if (leaderboardMinimumVersion > minimumVersion) { minimumVersion = leaderboardMinimumVersion; } } _richPresence.DisableLookupCollapsing = (minimumVersion < 0.79); _richPresence.DisableBuiltInMacros = (minimumVersion < 0.80); if (!String.IsNullOrEmpty(_richPresence.DisplayString)) { RichPresence = _richPresence.ToString(); RichPresenceLine = _richPresence.Line; } if (Error == null) { Error = expressionGroups.Errors.FirstOrDefault(); } return(result); }