Example #1
0
        private long ApplyAdvantage(RollData data, DiceAST root, int depth)
        {
            Value     = Expression.Value;
            ValueType = Expression.ValueType;
            _values   = Expression.Values.ToList();
            var rolls = Expression.Reroll(data, root, depth + 1);

            if ((KeepType == KeepType.Advantage && Expression.Value > Value) ||
                (KeepType == KeepType.Disadvantage && Expression.Value < Value))
            {
                for (int i = 0; i < _values.Count; i++)
                {
                    _values[i] = _values[i].Drop();
                }

                Value = Expression.Value;
                _values.Add(new DieResult(SpecialDie.Add));
                _values.AddRange(Expression.Values);
            }
            else
            {
                _values.Add(new DieResult(SpecialDie.Add));
                _values.AddRange(Expression.Values.Select(d => d.Drop()));
            }

            return(rolls);
        }
Example #2
0
 private void AddFunctionNodes(FunctionTiming timing, ref DiceAST node)
 {
     foreach (var fn in Functions.Where(f => f.Timing == timing))
     {
         fn.Context.Expression = node;
         node = fn;
     }
 }
Example #3
0
        protected override long EvaluateInternal(RollData data, DiceAST root, int depth)
        {
            long rolls = NumTimes?.Evaluate(data, root, depth + 1) ?? 0;

            rolls += Roll(data, root, depth);

            return(rolls);
        }
Example #4
0
        protected override long RerollInternal(RollData data, DiceAST root, int depth)
        {
            var rolls = Expression.Reroll(data, root, depth + 1);

            DoSort();

            return(rolls);
        }
Example #5
0
        protected override long RerollInternal(RollData data, DiceAST root, int depth)
        {
            long rolls = Expression.Reroll(data, root, depth + 1);

            MarkCrits();

            return(rolls);
        }
Example #6
0
 internal RerollNode(int maxRerolls, ComparisonNode comparison, DiceAST maxRerollsExpr = null)
 {
     Comparison     = comparison;
     Expression     = null;
     MaxRerolls     = maxRerolls;
     MaxRerollsExpr = maxRerollsExpr;
     _values        = new List <DieResult>();
 }
Example #7
0
        protected override long EvaluateInternal(RollData data, DiceAST root, int depth)
        {
            long rolls = Comparison?.Evaluate(data, root, depth + 1) ?? 0;

            rolls += Expression.Evaluate(data, root, depth + 1);
            rolls += DoExplode(data);

            return(rolls);
        }
Example #8
0
        protected override long EvaluateInternal(RollData data, DiceAST root, int depth)
        {
            var rolls = Amount?.Evaluate(data, root, depth + 1) ?? 0;

            rolls += Expression.Evaluate(data, root, depth + 1);
            rolls += ApplyKeep(data, root, depth);

            return(rolls);
        }
Example #9
0
        /// <summary>
        /// Creates the RollNode subtree
        /// </summary>
        /// <returns></returns>
        internal DiceAST CreateRollNode()
        {
            DiceAST roll = Roll;

            AddFunctionNodes(FunctionTiming.First, ref roll);
            AddFunctionNodes(FunctionTiming.BeforeExplode, ref roll);
            if (Explode != null)
            {
                Explode.Expression = roll;
                roll = Explode;
            }
            AddFunctionNodes(FunctionTiming.AfterExplode, ref roll);
            AddFunctionNodes(FunctionTiming.BeforeReroll, ref roll);
            if (RerollNode != null)
            {
                RerollNode.Expression = roll;
                roll = RerollNode;
            }
            AddFunctionNodes(FunctionTiming.AfterReroll, ref roll);
            AddFunctionNodes(FunctionTiming.BeforeKeep, ref roll);
            foreach (var k in Keep)
            {
                k.Expression = roll;
                roll         = k;
            }
            AddFunctionNodes(FunctionTiming.AfterKeep, ref roll);
            AddFunctionNodes(FunctionTiming.BeforeSuccess, ref roll);
            if (Success != null)
            {
                if (Success.Success.Comparisons.Count() == 0 && Success.Failure.Comparisons.Count() > 0)
                {
                    throw new DiceException(DiceErrorCode.InvalidSuccess);
                }

                Success.Expression = roll;
                roll = Success;
            }
            AddFunctionNodes(FunctionTiming.AfterSuccess, ref roll);
            AddFunctionNodes(FunctionTiming.BeforeCrit, ref roll);
            if (Critical != null)
            {
                Critical.Expression = roll;
                roll = Critical;
            }
            AddFunctionNodes(FunctionTiming.AfterCrit, ref roll);
            AddFunctionNodes(FunctionTiming.BeforeSort, ref roll);
            if (Sort != null)
            {
                Sort.Expression = roll;
                roll            = Sort;
            }
            AddFunctionNodes(FunctionTiming.AfterSort, ref roll);
            AddFunctionNodes(FunctionTiming.Last, ref roll);

            return(roll);
        }
Example #10
0
        protected override long EvaluateInternal(RollData data, DiceAST root, int depth)
        {
            long rolls = Expression.Evaluate(data, root, depth + 1);

            rolls += Critical?.Evaluate(data, root, depth + 1) ?? 0;
            rolls += Fumble?.Evaluate(data, root, depth + 1) ?? 0;
            MarkCrits();

            return(rolls);
        }
Example #11
0
        protected override long EvaluateInternal(RollData data, DiceAST root, int depth)
        {
            var rolls = MaxRerollsExpr?.Evaluate(data, root, depth + 1) ?? 0;

            rolls += Comparison.Evaluate(data, root, depth + 1);
            rolls += Expression.Evaluate(data, root, depth + 1);
            rolls += MaybeReroll(data, root, depth);

            return(rolls);
        }
Example #12
0
        protected override long RerollInternal(RollData data, DiceAST root, int depth)
        {
            long rolls = 0;

            foreach (var c in Comparisons)
            {
                rolls += c.expr.Reroll(data, root, depth);
            }

            return(rolls);
        }
Example #13
0
        internal GroupNode(DiceAST numTimes, List <DiceAST> exprs)
        {
            NumTimes     = numTimes;
            _expressions = exprs ?? throw new ArgumentNullException(nameof(exprs));
            _values      = new List <DieResult>();

            if (exprs.Count == 0)
            {
                throw new ArgumentException("A dice group must contain at least one expression", nameof(exprs));
            }
        }
Example #14
0
        internal RollNode(RollType rollType, DiceAST numDice, DiceAST numSides)
        {
            RollType = rollType;
            _values  = new List <DieResult>();
            NumDice  = numDice ?? throw new ArgumentNullException(nameof(numDice));
            NumSides = numSides;

            if (numSides == null && rollType != RollType.Fudge)
            {
                throw new ArgumentNullException(nameof(numSides));
            }
        }
Example #15
0
        internal ComparisonNode(CompareOp operation, DiceAST expression)
        {
            if (expression == null)
            {
                throw new ArgumentNullException(nameof(expression));
            }

            _comparisons = new List <Comparison>()
            {
                new Comparison(operation, expression)
            };
        }
Example #16
0
        protected override long RerollInternal(RollData data, DiceAST root, int depth)
        {
            if (Success == null)
            {
                throw new DiceException(DiceErrorCode.InvalidSuccess);
            }

            long rolls = Expression.Reroll(data, root, depth + 1);

            CountSuccesses();

            return(rolls);
        }
Example #17
0
        internal MathNode(MathOp operation, DiceAST left, DiceAST right)
        {
            Operation = operation;
            Left      = left;
            Right     = right ?? throw new ArgumentNullException(nameof(right));

            if (!operation.IsUnary() && left == null)
            {
                throw new ArgumentNullException(nameof(left));
            }

            _values = new List <DieResult>();
        }
Example #18
0
        protected override long RerollInternal(RollData data, DiceAST root, int depth)
        {
            long rolls = Context.Expression?.Reroll(data, root, depth + 1) ?? 0;

            foreach (var arg in Context.Arguments)
            {
                rolls += arg.Reroll(data, root, depth + 1);
            }

            CallFunction();

            return(rolls);
        }
Example #19
0
        protected override long EvaluateInternal(RollData data, DiceAST root, int depth)
        {
            // this doesn't increase depth as there is no actual logic that a ComparisonNode itself performs
            // (in other words, the Expression can be viewed as the ComparisonNode's evaluation)
            long rolls = 0;

            Value = 0;

            foreach (var c in Comparisons)
            {
                rolls += c.expr.Evaluate(data, root, depth);
            }

            return(rolls);
        }
Example #20
0
        protected override long EvaluateInternal(RollData data, DiceAST root, int depth)
        {
            if (Success == null)
            {
                throw new DiceException(DiceErrorCode.InvalidSuccess);
            }

            long rolls = Expression.Evaluate(data, root, depth + 1);

            rolls += Success.Evaluate(data, root, depth + 1);
            rolls += Failure?.Evaluate(data, root, depth + 1) ?? 0;

            CountSuccesses();

            return(rolls);
        }
Example #21
0
        protected override long EvaluateInternal(RollData data, DiceAST root, int depth)
        {
            if (data.MacroRegistry.Contains(Context.Name))
            {
                data.MacroRegistry.Get(Context.Name).callback(Context);
            }
            else if (data.Config.MacroRegistry.Contains(Context.Name))
            {
                data.Config.MacroRegistry.Get(Context.Name).callback(Context);
            }

            data.MacroRegistry.GlobalCallbacks?.Invoke(Context);
            data.Config.MacroRegistry.GlobalCallbacks?.Invoke(Context);

            Value     = Context.Value;
            ValueType = Context.ValueType;
            data.InternalContext.AllMacros.Add(Value);

            if (Context.Value == Decimal.MinValue)
            {
                throw new DiceException(DiceErrorCode.InvalidMacro);
            }

            _values.Clear();
            if (Context.Values != null)
            {
                _values.AddRange(Context.Values);
            }

            if (_values.Count == 0)
            {
                _values.Add(new DieResult()
                {
                    DieType  = DieType.Literal,
                    NumSides = 0,
                    Value    = Context.Value,
                    Flags    = DieFlags.Macro
                });
            }

            return(0);
        }
Example #22
0
        internal KeepNode(KeepType keepType, DiceAST amount)
        {
            KeepType   = keepType;
            Expression = null;
            _values    = new List <DieResult>();

            if (keepType == KeepType.Advantage || keepType == KeepType.Disadvantage)
            {
                if (amount != null)
                {
                    throw new ArgumentException("amount must be null if keep type is advantage or disadvantage");
                }

                Amount = null;
            }
            else
            {
                Amount = amount ?? throw new ArgumentNullException(nameof(amount));
            }
        }
Example #23
0
        /// <summary>
        /// Re-do the roll without re-evaluating the entire subtree again
        /// </summary>
        /// <param name="data">Roller config</param>
        /// <param name="root">AST root</param>
        /// <param name="depth">Recursion depth</param>
        /// <returns>Number of dice rolls performed</returns>
        protected internal long Reroll(RollData data, DiceAST root, int depth)
        {
            if (!Evaluated)
            {
                return(Evaluate(data, root, depth));
            }

            if (depth > data.Config.MaxRecursionDepth)
            {
                throw new DiceException(DiceErrorCode.RecursionDepthExceeded, data.Config.MaxRecursionDepth);
            }

            long rolls = RerollInternal(data, root, depth);

            if (rolls > data.Config.MaxDice)
            {
                throw new DiceException(DiceErrorCode.TooManyDice, data.Config.MaxDice);
            }

            return(rolls);
        }
Example #24
0
        /// <summary>
        /// Evaluates the node, causing it to store its result in Value.
        /// </summary>
        /// <param name="data">Configuration of the roller</param>
        /// <param name="root">Root of the AST</param>
        /// <param name="depth">Current recursion depth</param>
        /// <returns>Total number of rolls taken to evaluate this subtree</returns>
        protected internal long Evaluate(RollData data, DiceAST root, int depth)
        {
            if (this == root)
            {
                data.InternalContext = new InternalContext();
            }

            if (depth > data.Config.MaxRecursionDepth)
            {
                throw new DiceException(DiceErrorCode.RecursionDepthExceeded, data.Config.MaxRecursionDepth);
            }

            long rolls = EvaluateInternal(data, root, depth);

            if (rolls > data.Config.MaxDice)
            {
                throw new DiceException(DiceErrorCode.TooManyDice, data.Config.MaxDice);
            }

            Evaluated = true;

            return(rolls);
        }
Example #25
0
 protected override long EvaluateInternal(RollData data, DiceAST root, int depth)
 {
     return(0);
 }
Example #26
0
 protected override long RerollInternal(RollData data, DiceAST root, int depth)
 {
     return(Roll(data, root, depth));
 }
Example #27
0
 protected abstract long RerollInternal(RollData data, DiceAST root, int depth);
Example #28
0
 protected override long RerollInternal(RollData data, DiceAST root, int depth)
 {
     throw new InvalidOperationException("This node should not exist in an AST");
 }
Example #29
0
        internal long Roll(RollData data, DiceAST root, int depth)
        {
            long   rolls     = 0;
            ushort numTimes  = (ushort)(NumTimes?.Value ?? 1);
            bool   haveTotal = false;
            bool   haveRoll  = false;

            Value = 0;
            _values.Clear();

            if (numTimes == 0)
            {
                _values.Add(new DieResult()
                {
                    DieType  = DieType.Literal,
                    NumSides = 0,
                    Value    = 0,
                    Flags    = DieFlags.Extra
                });

                return(0);
            }

            for (ushort run = 0; run < numTimes; run++)
            {
                _values.MaybeAddPlus();
                _values.Add(new DieResult(SpecialDie.OpenParen));

                foreach (var ast in Expressions)
                {
                    // we want certain variables "fixed" throughout each iteration
                    // for example, in the group 2{(1d6)d8}, we want to roll 1d6 once and then treat it as if the
                    // group was 2{3d8} (or whatever the 1d6 rolled); in other words, we don't re-evaluate the 1d6
                    // every iteration. Note that Reroll() calls Evaluate() if the expr wasn't already evaluated.
                    rolls += ast.Reroll(data, root, depth + 1);
                    _values.MaybeAddPlus();

                    // Add a node containing the aggregate result of this expression
                    _values.Add(new DieResult()
                    {
                        DieType  = DieType.Group,
                        NumSides = 0,
                        Value    = ast.Value,
                        // maintain any crit/fumble flags from the underlying dice, combining them together
                        Flags = ast.Values
                                .Where(d => d.DieType != DieType.Special && !d.Flags.HasFlag(DieFlags.Dropped))
                                .Select(d => d.Flags & (DieFlags.Critical | DieFlags.Fumble))
                                .Aggregate((d1, d2) => d1 | d2),
                        Data = data.InternalContext.AddGroupExpression(ast)
                    });

                    Value += ast.Value;
                    // we make our final ValueType successes if *all* underlying expressions in this group which actually contain rolls are success types
                    // (and if there are actually rolls)
                    if (ast.Values.Any(d => d.DieType.IsRoll()))
                    {
                        haveRoll = true;

                        if (ast.ValueType == ResultType.Total)
                        {
                            haveTotal = true;
                        }
                    }
                }

                _values.Add(new DieResult(SpecialDie.CloseParen));
            }

            if (!haveRoll || haveTotal)
            {
                ValueType = ResultType.Total;
            }
            else
            {
                ValueType = ResultType.Successes;
            }

            return(rolls);
        }
Example #30
0
 protected abstract long EvaluateInternal(RollData data, DiceAST root, int depth);