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);
        }
Beispiel #3
0
        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);
        }
Beispiel #4
0
        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);
        }
Beispiel #5
0
        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));
        }
Beispiel #9
0
        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));
        }
Beispiel #10
0
            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);
            }
Beispiel #11
0
            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);
            }
Beispiel #12
0
        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));
        }
Beispiel #14
0
        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);
        }
Beispiel #19
0
        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);
        }
Beispiel #20
0
        private ParseErrorExpression ExecuteAchievementFunction(FunctionCallExpression functionCall, InterpreterScope scope)
        {
            var context = scope.GetContext <TriggerBuilderContext>();

            return(context.CallFunction(functionCall, scope));
        }
Beispiel #21
0
        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);
        }
Beispiel #22
0
        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);
        }
Beispiel #23
0
        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);
        }
    }
Beispiel #24
0
        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);
        }
Beispiel #25
0
        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);
        }
Beispiel #26
0
        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);
        }