Пример #1
0
        internal static DieResult DoRoll(RollData data, RollType rollType, int numSides, DieFlags flags = 0)
        {
            if (numSides < 1 || numSides > data.Config.MaxSides)
            {
                throw new DiceException(DiceErrorCode.BadSides, data.Config.MaxSides);
            }

            if (data.Config.NormalSidesOnly && !_normalSides.Contains(numSides))
            {
                throw new DiceException(DiceErrorCode.WrongSides);
            }

            byte[]  roll = new byte[4];
            uint    sides = (uint)numSides;
            int     min, max;
            uint    rollValue;
            int     rollAmt;
            DieType dt;

            switch (rollType)
            {
            case RollType.Normal:
                dt  = DieType.Normal;
                min = 1;
                max = numSides;
                break;

            case RollType.Fudge:
                dt = DieType.Fudge;
                // fudge dice go from -sides to sides, so we need to double
                // numSides and include an extra side for a "0" value as well.
                sides = (sides * 2) + 1;
                min   = -numSides;
                max   = numSides;
                break;

            default:
                throw new InvalidOperationException("Unknown RollType");
            }

            if (data.Config.RollDie != null)
            {
                rollAmt = data.Config.RollDie(min, max);
                if (rollAmt < min || rollAmt > max)
                {
                    throw new InvalidOperationException("RollerConfig.RollDie returned a value not within the expected range.");
                }

                // convert rollValue into the 0-based number for serialization
                switch (rollType)
                {
                case RollType.Normal:
                    rollValue = (uint)(rollAmt - 1);
                    break;

                case RollType.Fudge:
                    rollValue = (uint)(rollAmt + numSides);
                    break;

                default:
                    throw new InvalidOperationException("Unknown RollType");
                }
            }
            else
            {
                do
                {
                    if (data.Config.GetRandomBytes != null)
                    {
                        data.Config.GetRandomBytes(roll);
                    }
                    else
                    {
                        _rand.GetBytes(roll);
                    }
                } while (!IsFairRoll(roll, sides));

                // rollAmt is a number from 0 to sides-1, need to convert to a proper number
                rollValue = BitConverter.ToUInt32(roll, 0) % sides;
                rollAmt   = (int)rollValue;

                switch (rollType)
                {
                case RollType.Normal:
                    // change from 0 to sides-1 into 1 to sides
                    rollAmt++;
                    break;

                case RollType.Fudge:
                    // normalize back into -numSides to +numSides
                    rollAmt -= ((int)sides - 1) / 2;
                    break;

                default:
                    throw new InvalidOperationException("Unknown RollType");
                }
            }

            data.InternalContext.AllRolls.Add(rollValue);

            // finally, mark if this was a critical or fumble. This may be overridden later by a CritNode.
            if (rollAmt == min)
            {
                flags |= DieFlags.Fumble;
            }

            if (rollAmt == max)
            {
                flags |= DieFlags.Critical;
            }

            return(new DieResult()
            {
                DieType = dt,
                NumSides = numSides,
                Value = rollAmt,
                Flags = flags
            });
        }
Пример #2
0
        private void MarkCrits()
        {
            Value     = Expression.Value;
            ValueType = Expression.ValueType;
            _values.Clear();
            DieFlags mask = 0;

            if (Critical != null)
            {
                mask |= DieFlags.Critical;
            }

            if (Fumble != null)
            {
                mask |= DieFlags.Fumble;
            }

            foreach (var die in Expression.Values)
            {
                DieFlags flags = 0;

                if (die.DieType == DieType.Special || die.DieType == DieType.Group)
                {
                    // we don't skip over dropped dice here since we DO still want to
                    // mark them as criticals/fumbles as needed.
                    _values.Add(die);
                    continue;
                }

                if (Critical?.Compare(die.Value) == true)
                {
                    flags |= DieFlags.Critical;

                    // if tracking successes; a critical success is worth 2 successes
                    if (ValueType == ResultType.Successes)
                    {
                        // just in case the die wasn't already marked as a success
                        if ((die.Flags & DieFlags.Success) == 0)
                        {
                            flags |= DieFlags.Success;
                            Value++;
                        }

                        Value++;
                    }
                }

                if (Fumble?.Compare(die.Value) == true)
                {
                    flags |= DieFlags.Fumble;

                    // if tracking failures; a critical failure is worth -2 successes
                    if (ValueType == ResultType.Successes)
                    {
                        // just in case the die wasn't already marked as a failure
                        if ((die.Flags & DieFlags.Failure) == 0)
                        {
                            flags |= DieFlags.Failure;
                            Value--;
                        }

                        Value--;
                    }
                }

                _values.Add(new DieResult()
                {
                    DieType  = die.DieType,
                    NumSides = die.NumSides,
                    Value    = die.Value,
                    // strip any existing crit/fumble flag off and use ours,
                    // assuming a comparison was defined for it.
                    // (we may have an existing flag if the die rolled min or max value)
                    Flags = (die.Flags & ~mask) | flags
                });
            }
        }