diff --git a/src/solver/parser.ts b/src/solver/parser.ts index 1a56127..d46e2ce 100644 --- a/src/solver/parser.ts +++ b/src/solver/parser.ts @@ -286,6 +286,9 @@ export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll case 'YouNeedAD': errorMsg = 'Formatting Error: Missing die size and count config'; break; + case 'DoubleSeparator': + errorMsg = `Formatting Error: \`${errorDetails}\` should only be specified once per roll, remove all but one and repeat roll`; + break; case 'FormattingError': errorMsg = 'Formatting Error: Cannot use Keep and Drop at the same time, remove all but one and repeat roll'; break; @@ -307,22 +310,25 @@ export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll errorMsg += 'Die Size and Die Count'; break; case 'drop': - errorMsg += 'Drop (d or dl)'; + errorMsg += 'Drop (`d` or `dl`)'; break; case 'keep': - errorMsg += 'Keep (k or kh)'; + errorMsg += 'Keep (`k` or `kh`)'; break; case 'dropHigh': - errorMsg += 'Drop Highest (dh)'; + errorMsg += 'Drop Highest (`dh`)'; break; case 'keepLow': - errorMsg += 'Keep Lowest (kl)'; + errorMsg += 'Keep Lowest (`kl`)'; break; case 'reroll': - errorMsg += 'Reroll (r)'; + errorMsg += 'Reroll (`r`)'; break; case 'critScore': - errorMsg += 'Crit Score (cs)'; + errorMsg += 'Crit Score (`cs`)'; + break; + case 'critFail': + errorMsg += 'Crit Fail (`cf`)'; break; default: errorMsg += `Unhandled - ${errorDetails}`; diff --git a/src/solver/roller.ts b/src/solver/roller.ts index 82e9d5f..ff916e7 100644 --- a/src/solver/roller.ts +++ b/src/solver/roller.ts @@ -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 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[] => { @@ -193,22 +197,38 @@ export const roll = (rollStr: string, maximizeRoll: boolean, nominalRoll: boolea switch (tSep) { case 'dl': case 'd': + if (rollConf.drop.on) { + // Ensure we do not override existing settings + throwDoubleSepError(tSep); + } // Configure Drop (Lowest) rollConf.drop.on = true; rollConf.drop.count = tNum; break; case 'kh': case 'k': + if (rollConf.keep.on) { + // Ensure we do not override existing settings + throwDoubleSepError(tSep); + } // Configure Keep (Highest) rollConf.keep.on = true; rollConf.keep.count = tNum; break; case 'dh': + if (rollConf.dropHigh.on) { + // Ensure we do not override existing settings + throwDoubleSepError(tSep); + } // Configure Drop (Highest) rollConf.dropHigh.on = true; rollConf.dropHigh.count = tNum; break; case 'kl': + if (rollConf.keepLow.on) { + // Ensure we do not override existing settings + throwDoubleSepError(tSep); + } // Configure Keep (Lowest) rollConf.keepLow.on = true; 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 if (rollConf.dieCount < 0) { 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) { 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 let dkdkCnt = 0; [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) { throw new Error('FormattingError_dk'); } + if (rollConf.drop.on && rollConf.drop.count === 0) { 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)) { 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) { throw new Error('NoRerollOnAllSides'); }