87 lines
2.6 KiB
C#
87 lines
2.6 KiB
C#
using RpgRoller.Contracts;
|
|
using RpgRoller.Domain;
|
|
|
|
namespace RpgRoller.Services;
|
|
|
|
public sealed class D6RollEngine(IDiceRoller diceRoller)
|
|
{
|
|
public (int Total, string Breakdown, IReadOnlyList<RollDieResult> Dice) Roll(DiceExpression expression, int wildDice, bool allowFumble)
|
|
{
|
|
var initialDice = expression.DiceCount;
|
|
var currentDice = initialDice;
|
|
var pendingExplodingDice = 0;
|
|
var pendingFumbles = 0;
|
|
var dieResults = new List<RollDieResult>(initialDice);
|
|
|
|
for (var i = 0; i < currentDice; i += 1)
|
|
{
|
|
var roll = diceRoller.Roll(expression.Sides);
|
|
var isWild = i < wildDice;
|
|
var isCrit = false;
|
|
var isFumble = false;
|
|
var isAdded = false;
|
|
|
|
if (isWild)
|
|
{
|
|
if (roll == expression.Sides)
|
|
{
|
|
pendingExplodingDice += 1;
|
|
currentDice += 1;
|
|
isCrit = true;
|
|
}
|
|
else if (allowFumble && roll == 1)
|
|
{
|
|
pendingFumbles += 1;
|
|
isFumble = true;
|
|
}
|
|
}
|
|
|
|
if (pendingExplodingDice > 0 && i >= initialDice)
|
|
{
|
|
pendingExplodingDice -= 1;
|
|
isAdded = true;
|
|
if (roll == expression.Sides)
|
|
{
|
|
pendingExplodingDice += 1;
|
|
currentDice += 1;
|
|
}
|
|
}
|
|
|
|
dieResults.Add(new(roll, isCrit, isFumble, isWild, false, isAdded));
|
|
}
|
|
|
|
for (var roll = expression.Sides; roll >= 1 && pendingFumbles > 0; roll -= 1)
|
|
for (var i = currentDice - 1; i >= 0 && pendingFumbles > 0; i -= 1)
|
|
{
|
|
if (dieResults[i].Roll != roll)
|
|
continue;
|
|
|
|
dieResults[i] = dieResults[i] with
|
|
{
|
|
Removed = true,
|
|
Added = false,
|
|
Crit = false,
|
|
Fumble = false
|
|
};
|
|
pendingFumbles -= 1;
|
|
}
|
|
|
|
var total = expression.Modifier;
|
|
var includedDice = new List<int>(dieResults.Count);
|
|
foreach (var die in dieResults)
|
|
{
|
|
if (die.Fumble)
|
|
{
|
|
total += 1;
|
|
includedDice.Add(1);
|
|
}
|
|
else if (!die.Removed)
|
|
{
|
|
total += die.Roll;
|
|
includedDice.Add(die.Roll);
|
|
}
|
|
}
|
|
|
|
return (total, RollBreakdownFormatter.BuildBreakdown(includedDice, expression.Modifier, total), dieResults);
|
|
}
|
|
} |