Exemple #1
0
        protected ParseErrorExpression BuildTriggerCondition(TriggerBuilderContext context, InterpreterScope scope, ExpressionBase condition)
        {
            var            builder = new ScriptInterpreterAchievementBuilder();
            ExpressionBase result;

            if (!TriggerBuilderContext.ProcessAchievementConditions(builder, condition, scope, out result))
            {
                return((ParseErrorExpression)result);
            }

            if (builder.AlternateRequirements.Count > 0)
            {
                return(new ParseErrorExpression(Name.Name + " does not support ||'d conditions", condition));
            }

            if (builder.CoreRequirements.Count > 1)
            {
                var last = builder.CoreRequirements.Last();
                foreach (var requirement in builder.CoreRequirements)
                {
                    if (requirement.Type == RequirementType.None && !ReferenceEquals(requirement, last))
                    {
                        requirement.Type = RequirementType.AndNext;
                    }
                }
            }

            ParseErrorExpression error = ValidateSingleCondition(builder.CoreRequirements);

            if (error != null)
            {
                return(new ParseErrorExpression(error, condition));
            }

            error = ModifyRequirements(builder);
            if (error != null)
            {
                return(error);
            }

            foreach (var requirement in builder.CoreRequirements)
            {
                context.Trigger.Add(requirement);
            }

            return(null);
        }
Exemple #2
0
        protected ParseErrorExpression BuildTriggerCondition(TriggerBuilderContext context, InterpreterScope scope, ExpressionBase condition)
        {
            var            builder = new ScriptInterpreterAchievementBuilder();
            ExpressionBase result;

            if (!TriggerBuilderContext.ProcessAchievementConditions(builder, condition, scope, out result))
            {
                switch (condition.Type)
                {
                case ExpressionType.Conditional:
                case ExpressionType.Comparison:
                    // allowed constructs should only report the inner error
                    return((ParseErrorExpression)result);

                default:
                    // non-allowed construct
                    return(new ParseErrorExpression("comparison did not evaluate to a valid comparison", condition)
                    {
                        InnerError = (ParseErrorExpression)result
                    });
                }
            }

            var error = builder.CollapseForSubClause();

            if (error != null)
            {
                return(new ParseErrorExpression(error.Message, condition));
            }

            error = ModifyRequirements(builder);
            if (error != null)
            {
                return(new ParseErrorExpression(error.Message, condition));
            }

            foreach (var requirement in builder.CoreRequirements)
            {
                context.Trigger.Add(requirement);
            }

            return(null);
        }
Exemple #3
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);
        }
Exemple #4
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);
        }
    }
Exemple #5
0
        private ParseErrorExpression EvaluateAddHits(TriggerBuilderContext context, InterpreterScope scope, ConditionalExpression condition)
        {
            ExpressionBase result;

            var builder = new ScriptInterpreterAchievementBuilder();

            builder.CoreRequirements.Add(new Requirement()); // empty core requirement required for optimize call, we'll ignore it
            if (!TriggerBuilderContext.ProcessAchievementConditions(builder, condition, scope, out result))
            {
                return((ParseErrorExpression)result);
            }

            var requirements = new List <Requirement>();

            foreach (var altGroup in builder.AlternateRequirements)
            {
                var error = ValidateSingleCondition(altGroup);
                if (error != null)
                {
                    return(error);
                }

                var requirement = altGroup.First();
                if (requirement.Type != RequirementType.None)
                {
                    return(new ParseErrorExpression("modifier not allowed in multi-condition repeated clause"));
                }

                requirements.Add(requirement);
            }

            // the last item cannot have its own HitCount as it will hold the HitCount for the group.
            // if necessary, find one without a HitCount and make it the last.
            int index = requirements.Count - 1;

            if (requirements[index].HitCount > 0)
            {
                do
                {
                    index--;
                } while (index >= 0 && requirements[index].HitCount > 0);

                if (index == -1)
                {
                    // all requirements had HitCount limits, add a dummy item that's never true for the total HitCount
                    requirements.Add(new Requirement
                    {
                        Left = new Field {
                            Type = FieldType.Value, Value = 1
                        },
                        Operator = RequirementOperator.Equal,
                        Right    = new Field {
                            Type = FieldType.Value, Value = 0
                        }
                    });
                }
                else
                {
                    // found a requirement without a HitCount limit, move it to the last spot for the total HitCount
                    var requirement = requirements[index];
                    requirements.RemoveAt(index);
                    requirements.Add(requirement);
                }
            }

            // everything but the last becomes an AddHits
            for (int i = 0; i < requirements.Count - 1; i++)
            {
                requirements[i].Type = RequirementType.AddHits;
            }

            // load the requirements into the trigger
            foreach (var requirement in requirements)
            {
                context.Trigger.Add(requirement);
            }

            return(null);
        }
Exemple #6
0
        protected ParseErrorExpression EvaluateCondition(TriggerBuilderContext context, InterpreterScope scope, ConditionalExpression condition)
        {
            ExpressionBase result;

            var builder = new ScriptInterpreterAchievementBuilder();

            if (!TriggerBuilderContext.ProcessAchievementConditions(builder, condition, scope, out result))
            {
                return((ParseErrorExpression)result);
            }

            if (builder.CoreRequirements.Any())
            {
                var error = ProcessOrNextSubClause(builder.CoreRequirements);
                if (error != null)
                {
                    return(error);
                }

                if (builder.AlternateRequirements.Count == 0)
                {
                    // everything was converted to an OrNext. convert the last back
                    builder.CoreRequirements.Last().Type = RequirementType.None;

                    // one of the alts was entirely promoted to Core. We only need to check for that.
                    foreach (var clause in builder.CoreRequirements)
                    {
                        context.Trigger.Add(clause);
                    }

                    return(null);
                }

                // core requirements have to be injected into each subclause as a series of AndNext's
                builder.CoreRequirements.Last().Type = RequirementType.AndNext;
            }

            var requirements = new List <ICollection <Requirement> >();

            foreach (var altGroup in builder.AlternateRequirements)
            {
                var error = ProcessOrNextSubClause(altGroup);
                if (error != null)
                {
                    return(error);
                }

                if (builder.CoreRequirements.Any())
                {
                    var merged = new List <Requirement>(builder.CoreRequirements);
                    merged.AddRange(altGroup);
                    requirements.Add(merged);
                }
                else
                {
                    requirements.Add(altGroup);
                }
            }

            // the last item cannot have its own HitCount as it will hold the HitCount for the group.
            // if necessary, find one without a HitCount and make it the last.
            int index = requirements.Count - 1;

            if (requirements[index].Last().HitCount > 0)
            {
                do
                {
                    index--;
                } while (index >= 0 && requirements[index].Last().HitCount > 0);

                if (index == -1)
                {
                    // all requirements had HitCount limits, add a dummy item that's never true for the total HitCount
                    requirements.Add(new Requirement[] { AlwaysFalseFunction.CreateAlwaysFalseRequirement() });
                }
                else
                {
                    // found a requirement without a HitCount limit, move it to the last spot for the total HitCount
                    var requirement = requirements[index];
                    requirements.RemoveAt(index);
                    requirements.Add(requirement);
                }
            }

            // if we can guarantee the individual requirements won't be true in the same frame, we can use AddHits
            // instead of OrNext to improve compatibility with older versions of RetroArch
            if (CanUseAddHits(requirements))
            {
                foreach (var requirement in requirements)
                {
                    foreach (var cond in requirement)
                    {
                        if (cond.Type == RequirementType.OrNext)
                        {
                            cond.Type = RequirementType.AddHits;
                        }
                    }
                }
            }
            else
            {
                // an AndNext in the first clause is acceptable, but once we see the first
                // OrNext, each clause must be a single logical condition as AndNext has the
                // same priority as OrNext and will not be processed first.
                for (int i = 1; i < requirements.Count; ++i)
                {
                    if (requirements[i].Any(r => r.Type == RequirementType.AndNext))
                    {
                        return(new ParseErrorExpression("Cannot join multiple AndNext chains with OrNext"));
                    }
                }
            }

            // everything was converted to an OrNext. convert the last back
            requirements.Last().Last().Type = RequirementType.None;

            // load the requirements into the trigger
            foreach (var requirement in requirements)
            {
                foreach (var clause in requirement)
                {
                    context.Trigger.Add(clause);
                }
            }

            return(null);
        }
Exemple #7
0
        public override ParseErrorExpression BuildTrigger(TriggerBuilderContext context, InterpreterScope scope, FunctionCallExpression functionCall)
        {
            var until = functionCall.Parameters.ElementAt(1);

            // build the reset next clause
            var            builder = new ScriptInterpreterAchievementBuilder();
            ExpressionBase result;

            if (!TriggerBuilderContext.ProcessAchievementConditions(builder, until, scope, out result))
            {
                return new ParseErrorExpression("until did not evaluate to a valid comparison", until)
                       {
                           InnerError = (ParseErrorExpression)result
                       }
            }
            ;

            var error = builder.CollapseForSubClause();

            if (error != null)
            {
                return(new ParseErrorExpression(error.Message, until));
            }

            var resetNextClause = new List <Requirement>();

            if (builder.CoreRequirements.Count > 0 && builder.CoreRequirements.First().Evaluate() != false)
            {
                foreach (var requirement in builder.CoreRequirements)
                {
                    if (requirement.Type == RequirementType.None)
                    {
                        requirement.Type = RequirementType.AndNext;
                    }

                    resetNextClause.Add(requirement);
                }

                resetNextClause.Last().Type = RequirementType.ResetNextIf;
            }

            // build the when clause
            var whenContext = new TriggerBuilderContext {
                Trigger = new List <Requirement>()
            };

            error = base.BuildTrigger(whenContext, scope, functionCall);
            if (error != null)
            {
                return(error);
            }

            // 'reset next' clause first
            foreach (var resetRequirement in resetNextClause)
            {
                context.Trigger.Add(resetRequirement);
            }

            // then 'when' clause. make sure to insert the 'reset next' clause after each addhits/subhits
            // as they break up the 'reset next' scope.
            foreach (var whenRequirement in whenContext.Trigger)
            {
                context.Trigger.Add(whenRequirement);

                switch (whenRequirement.Type)
                {
                case RequirementType.AddHits:
                case RequirementType.SubHits:
                    foreach (var resetRequirement in resetNextClause)
                    {
                        context.Trigger.Add(resetRequirement);
                    }
                    break;

                default:
                    break;
                }
            }

            // disable_when is a pause lock - if a hitcount was not specified assume the first hit is enough
            if (context.LastRequirement.HitCount == 0)
            {
                context.LastRequirement.HitCount = 1;
            }

            return(null);
        }
    }