2021-01-02 16:12:16 +01:00
|
|
|
import { RollOptions } from "./roll-data";
|
|
|
|
|
|
|
|
export function isDiceSwapNecessary(
|
|
|
|
critSuccesses: Array<number>,
|
|
|
|
otherRolls: Array<number>,
|
2021-01-04 19:38:26 +01:00
|
|
|
remainingTargetValue: number,
|
2021-01-02 16:12:16 +01:00
|
|
|
): boolean {
|
|
|
|
if (critSuccesses.length == 0 || otherRolls.length == 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const amountOfOtherRolls = otherRolls.length;
|
|
|
|
const lastDice = otherRolls[amountOfOtherRolls - 1];
|
2021-01-04 19:38:26 +01:00
|
|
|
if (lastDice <= remainingTargetValue) {
|
2021-01-02 16:12:16 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-01-04 19:38:26 +01:00
|
|
|
return lastDice + remainingTargetValue > 20;
|
2021-01-02 16:12:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
export function isSlayingDiceRepetition(opts: RollOptions): boolean {
|
|
|
|
return opts.useSlayingDice && opts.slayingDiceRepetition;
|
|
|
|
}
|
2021-01-04 19:38:26 +01:00
|
|
|
|
|
|
|
export function calculateRollResult(
|
|
|
|
assignedRollResults: Array<number>,
|
|
|
|
remainderTargetValue: number,
|
|
|
|
rollOptions: RollOptions,
|
|
|
|
): number {
|
|
|
|
const numberOfDice = assignedRollResults.length;
|
|
|
|
|
|
|
|
const maxResultPerDie: Array<number> = Array(numberOfDice).fill(20);
|
|
|
|
maxResultPerDie[numberOfDice - 1] = remainderTargetValue;
|
|
|
|
|
|
|
|
const rollsAndMaxValues = zip(assignedRollResults, maxResultPerDie);
|
|
|
|
|
|
|
|
return rollsAndMaxValues
|
|
|
|
.map(([v, m]) => {
|
|
|
|
return v <= rollOptions.maxCritSucc ? [m, m] : [v, m];
|
|
|
|
})
|
|
|
|
.filter(([v, m]) => v <= m)
|
|
|
|
.map(([v]) => v)
|
|
|
|
.reduce((a, b) => a + b);
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Move to generic utils method?
|
|
|
|
function zip<T, U>(a1: Array<T>, a2: Array<U>): Array<[T, U]> {
|
|
|
|
if (a1.length <= a2.length) {
|
|
|
|
return a1.map((e1, i) => [e1, a2[i]]);
|
|
|
|
} else {
|
|
|
|
return a2.map((e2, i) => [a1[i], e2]);
|
|
|
|
}
|
|
|
|
}
|