further improve roll safety, no doubling up on drop/keep

This commit is contained in:
Ean Milligan 2025-04-27 04:46:02 -04:00
parent e69806c443
commit 2f2a8f67e0
2 changed files with 42 additions and 13 deletions

View File

@ -286,6 +286,9 @@ export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll
case 'YouNeedAD': case 'YouNeedAD':
errorMsg = 'Formatting Error: Missing die size and count config'; errorMsg = 'Formatting Error: Missing die size and count config';
break; break;
case 'DoubleSeparator':
errorMsg = `Formatting Error: \`${errorDetails}\` should only be specified once per roll, remove all but one and repeat roll`;
break;
case 'FormattingError': case 'FormattingError':
errorMsg = 'Formatting Error: Cannot use Keep and Drop at the same time, remove all but one and repeat roll'; errorMsg = 'Formatting Error: Cannot use Keep and Drop at the same time, remove all but one and repeat roll';
break; break;
@ -307,22 +310,25 @@ export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll
errorMsg += 'Die Size and Die Count'; errorMsg += 'Die Size and Die Count';
break; break;
case 'drop': case 'drop':
errorMsg += 'Drop (d or dl)'; errorMsg += 'Drop (`d` or `dl`)';
break; break;
case 'keep': case 'keep':
errorMsg += 'Keep (k or kh)'; errorMsg += 'Keep (`k` or `kh`)';
break; break;
case 'dropHigh': case 'dropHigh':
errorMsg += 'Drop Highest (dh)'; errorMsg += 'Drop Highest (`dh`)';
break; break;
case 'keepLow': case 'keepLow':
errorMsg += 'Keep Lowest (kl)'; errorMsg += 'Keep Lowest (`kl`)';
break; break;
case 'reroll': case 'reroll':
errorMsg += 'Reroll (r)'; errorMsg += 'Reroll (`r`)';
break; break;
case 'critScore': case 'critScore':
errorMsg += 'Crit Score (cs)'; errorMsg += 'Crit Score (`cs`)';
break;
case 'critFail':
errorMsg += 'Crit Fail (`cf`)';
break; break;
default: default:
errorMsg += `Unhandled - ${errorDetails}`; errorMsg += `Unhandled - ${errorDetails}`;

View File

@ -16,6 +16,10 @@ const loopCountCheck = (loopCount: number): void => {
} }
}; };
const throwDoubleSepError = (sep: string): void => {
throw new Error(`DoubleSeparator_${sep}`);
};
// roll(rollStr, maximizeRoll, nominalRoll) returns RollSet // roll(rollStr, maximizeRoll, nominalRoll) returns RollSet
// roll parses and executes the rollStr, if needed it will also make the roll the maximum or average // roll parses and executes the rollStr, if needed it will also make the roll the maximum or average
export const roll = (rollStr: string, maximizeRoll: boolean, nominalRoll: boolean): RollSet[] => { export const roll = (rollStr: string, maximizeRoll: boolean, nominalRoll: boolean): RollSet[] => {
@ -193,22 +197,38 @@ export const roll = (rollStr: string, maximizeRoll: boolean, nominalRoll: boolea
switch (tSep) { switch (tSep) {
case 'dl': case 'dl':
case 'd': case 'd':
if (rollConf.drop.on) {
// Ensure we do not override existing settings
throwDoubleSepError(tSep);
}
// Configure Drop (Lowest) // Configure Drop (Lowest)
rollConf.drop.on = true; rollConf.drop.on = true;
rollConf.drop.count = tNum; rollConf.drop.count = tNum;
break; break;
case 'kh': case 'kh':
case 'k': case 'k':
if (rollConf.keep.on) {
// Ensure we do not override existing settings
throwDoubleSepError(tSep);
}
// Configure Keep (Highest) // Configure Keep (Highest)
rollConf.keep.on = true; rollConf.keep.on = true;
rollConf.keep.count = tNum; rollConf.keep.count = tNum;
break; break;
case 'dh': case 'dh':
if (rollConf.dropHigh.on) {
// Ensure we do not override existing settings
throwDoubleSepError(tSep);
}
// Configure Drop (Highest) // Configure Drop (Highest)
rollConf.dropHigh.on = true; rollConf.dropHigh.on = true;
rollConf.dropHigh.count = tNum; rollConf.dropHigh.count = tNum;
break; break;
case 'kl': case 'kl':
if (rollConf.keepLow.on) {
// Ensure we do not override existing settings
throwDoubleSepError(tSep);
}
// Configure Keep (Lowest) // Configure Keep (Lowest)
rollConf.keepLow.on = true; rollConf.keepLow.on = true;
rollConf.keepLow.count = tNum; rollConf.keepLow.count = tNum;
@ -381,13 +401,6 @@ export const roll = (rollStr: string, maximizeRoll: boolean, nominalRoll: boolea
} }
} }
// Filter rollConf num lists to only include valid numbers
const validNumFilter = (curNum: number) => curNum <= rollConf.dieSize && curNum > 0;
rollConf.reroll.nums = rollConf.reroll.nums.filter(validNumFilter);
rollConf.critScore.range = rollConf.critScore.range.filter(validNumFilter);
rollConf.critFail.range = rollConf.critFail.range.filter(validNumFilter);
rollConf.exploding.nums = rollConf.exploding.nums.filter(validNumFilter);
// Verify the parse, throwing errors for every invalid config // Verify the parse, throwing errors for every invalid config
if (rollConf.dieCount < 0) { if (rollConf.dieCount < 0) {
throw new Error('NoZerosAllowed_base'); throw new Error('NoZerosAllowed_base');
@ -395,6 +408,7 @@ export const roll = (rollStr: string, maximizeRoll: boolean, nominalRoll: boolea
if (rollConf.dieCount === 0 || rollConf.dieSize === 0) { if (rollConf.dieCount === 0 || rollConf.dieSize === 0) {
throw new Error('NoZerosAllowed_base'); throw new Error('NoZerosAllowed_base');
} }
// Since only one drop or keep option can be active, count how many are active to throw the right error // Since only one drop or keep option can be active, count how many are active to throw the right error
let dkdkCnt = 0; let dkdkCnt = 0;
[rollConf.drop.on, rollConf.keep.on, rollConf.dropHigh.on, rollConf.keepLow.on].forEach((e) => { [rollConf.drop.on, rollConf.keep.on, rollConf.dropHigh.on, rollConf.keepLow.on].forEach((e) => {
@ -406,6 +420,7 @@ export const roll = (rollStr: string, maximizeRoll: boolean, nominalRoll: boolea
if (dkdkCnt > 1) { if (dkdkCnt > 1) {
throw new Error('FormattingError_dk'); throw new Error('FormattingError_dk');
} }
if (rollConf.drop.on && rollConf.drop.count === 0) { if (rollConf.drop.on && rollConf.drop.count === 0) {
throw new Error('NoZerosAllowed_drop'); throw new Error('NoZerosAllowed_drop');
} }
@ -421,6 +436,14 @@ export const roll = (rollStr: string, maximizeRoll: boolean, nominalRoll: boolea
if (rollConf.reroll.on && rollConf.reroll.nums.includes(0)) { if (rollConf.reroll.on && rollConf.reroll.nums.includes(0)) {
throw new Error('NoZerosAllowed_reroll'); throw new Error('NoZerosAllowed_reroll');
} }
// Filter rollConf num lists to only include valid numbers
const validNumFilter = (curNum: number) => curNum <= rollConf.dieSize && curNum > 0;
rollConf.reroll.nums = rollConf.reroll.nums.filter(validNumFilter);
rollConf.critScore.range = rollConf.critScore.range.filter(validNumFilter);
rollConf.critFail.range = rollConf.critFail.range.filter(validNumFilter);
rollConf.exploding.nums = rollConf.exploding.nums.filter(validNumFilter);
if (rollConf.reroll.on && rollConf.reroll.nums.length === rollConf.dieSize) { if (rollConf.reroll.on && rollConf.reroll.nums.length === rollConf.dieSize) {
throw new Error('NoRerollOnAllSides'); throw new Error('NoRerollOnAllSides');
} }