diff --git a/spec/support/ds4rolls/executor.spec.ts b/spec/support/ds4rolls/executor.spec.ts index e28447c9..1ab1bda4 100644 --- a/spec/support/ds4rolls/executor.spec.ts +++ b/spec/support/ds4rolls/executor.spec.ts @@ -314,13 +314,12 @@ describe("DS4 Rolls with multiple and slaying dice, first throw", () => { }); }); -// TODO: Implement & reactivate -xdescribe("DS4 Rolls with multiple and slaying dice, recurrent throw", () => { +describe("DS4 Rolls with multiple and slaying dice, recurrent throw", () => { it("Should regularly succeed with the first roll being a `20`", () => { const rollProvider = mockMultipleThrows([20, 2, 19]); - expect(rollCheckMultipleDice(48, {}, rollProvider)).toEqual( - new RollResult(40, RollResultStatus.CRITICAL_FAILURE, [20, 2, 19]), + expect(rollCheckMultipleDice(48, { useSlayingDice: true, slayingDiceRepetition: true }, rollProvider)).toEqual( + new RollResult(41, RollResultStatus.SUCCESS, [20, 19, 2]), ); }); }); diff --git a/src/module/rolls/roll-executor.ts b/src/module/rolls/roll-executor.ts index e9df9a9b..233950ea 100644 --- a/src/module/rolls/roll-executor.ts +++ b/src/module/rolls/roll-executor.ts @@ -33,24 +33,7 @@ export function rollCheckSingleDie( } } -export function rollCheckMultipleDice( - testValue: number, - rollOptions: Partial, - provider: RollProvider = new DS4RollProvider(), -): RollResult { - const usedOptions = new DefaultRollOptions().mergeWith(rollOptions); - const finalCheck = testValue % 20; - const numberOfDice = Math.ceil(testValue / 20); - - const dice = provider.getNextRolls(numberOfDice); - - const firstResult = dice[0]; - - // Slaying Dice require a different handling. - if (firstResult >= usedOptions.minCritFail && !isSlayingDiceRepetition(usedOptions)) { - return new RollResult(0, RollResultStatus.CRITICAL_FAILURE, dice); - } - +function separateCriticalHits(dice: Array, usedOptions: RollOptions): [Array, Array] { const partitionCallback = (prev: [Array, Array], cur: number) => { if (cur <= usedOptions.maxCritSucc) { prev[0].push(cur); @@ -64,6 +47,30 @@ export function rollCheckMultipleDice( .reduce(partitionCallback, [[], []]) .map((a) => a.sort((r1, r2) => r2 - r1)); + return [critSuccesses, otherRolls]; +} + +export function rollCheckMultipleDice( + testValue: number, + rollOptions: Partial, + provider: RollProvider = new DS4RollProvider(), +): RollResult { + const usedOptions = new DefaultRollOptions().mergeWith(rollOptions); + const finalCheck = testValue % 20; + const numberOfDice = Math.ceil(testValue / 20); + + const dice = provider.getNextRolls(numberOfDice); + + const firstResult = dice[0]; + const slayingDiceRepetition = isSlayingDiceRepetition(usedOptions); + + // Slaying Dice require a different handling. + if (firstResult >= usedOptions.minCritFail && !slayingDiceRepetition) { + return new RollResult(0, RollResultStatus.CRITICAL_FAILURE, dice); + } + + const [critSuccesses, otherRolls] = separateCriticalHits(dice, usedOptions); + const swapLastWithCrit: boolean = isDiceSwapNecessary(critSuccesses, otherRolls, finalCheck); let sortedRollResults: Array; @@ -96,5 +103,9 @@ export function rollCheckMultipleDice( }) .reduce((a, b) => a + b); - return new RollResult(evaluationResult, RollResultStatus.SUCCESS, sortedRollResults); + if (usedOptions.useSlayingDice && firstResult <= usedOptions.maxCritSucc) { + return new RollResult(evaluationResult, RollResultStatus.CRITICAL_SUCCESS, sortedRollResults); + } else { + return new RollResult(evaluationResult, RollResultStatus.SUCCESS, sortedRollResults); + } }