diff --git a/.bruno/Authenticated/Roll Requests/Roll Dice.bru b/.bruno/Authenticated/Roll Requests/Roll Dice.bru index 35ba6d1..2d12b1a 100644 --- a/.bruno/Authenticated/Roll Requests/Roll Dice.bru +++ b/.bruno/Authenticated/Roll Requests/Roll Dice.bru @@ -5,7 +5,7 @@ meta { } get { - url: http://localhost:8166/api/roll?user=[discord-user-id]&channel=[discord-channel-id]&rollstr=[artificer-roll-cmd]&documentation=All items below are optional. Flags do not need values.&nd=[no-details-flag]&snd=[super-no-details-flag]&hr=[hide-raw-roll-details-flag]&s=[spoiler-results-flag]&m-or-max=[max-roll-flag, cannot be used with n flag]&min=[[min-roll-flag, cannot be used with n or max]&n=[nominal-roll-flag, cannot be used with max or min flag]&gms=[csv-of-discord-user-ids-to-be-dmed-results]&o=[order-rolls, must be a or d]&c=[count-flag] + url: http://localhost:8166/api/roll?user=[discord-user-id]&channel=[discord-channel-id]&rollstr=[artificer-roll-cmd]&documentation=All items below are optional. Flags do not need values.&nd=[no-details-flag]&snd=[super-no-details-flag]&hr=[hide-raw-roll-details-flag]&s=[spoiler-results-flag]&m-or-max=[max-roll-flag, cannot be used with n flag]&min=[[min-roll-flag, cannot be used with n or max]&n=[nominal-roll-flag, cannot be used with max or min flag]&gms=[csv-of-discord-user-ids-to-be-dmed-results]&o=[order-rolls, must be a or d]&c=[count-flag]&cc=[confirm-crit-flag] body: none auth: inherit } @@ -25,4 +25,5 @@ params:query { gms: [csv-of-discord-user-ids-to-be-dmed-results] o: [order-rolls, must be a or d] c: [count-flag] + cc: [confirm-crit-flag] } diff --git a/src/artigen/artigen.ts b/src/artigen/artigen.ts index 1cca0cb..65aafab 100644 --- a/src/artigen/artigen.ts +++ b/src/artigen/artigen.ts @@ -10,6 +10,7 @@ import { loggingEnabled } from 'artigen/utils/logFlag.ts'; import { assertPrePostBalance } from 'artigen/utils/parenBalance.ts'; import { compareTotalRolls, compareTotalRollsReverse } from 'artigen/utils/sortFuncs.ts'; import { translateError } from 'artigen/utils/translateError.ts'; +import { reduceCountDetails } from 'artigen/utils/counter.ts'; // runCmd(rollRequest) // runCmd handles converting rollRequest into a computer readable format for processing, and finally executes the solving @@ -115,24 +116,7 @@ export const runCmd = (rollRequest: QueuedRoll): SolvedRoll => { returnMsg.line3 = line3; // Reduce counts to a single object - returnMsg.counts = tempCountDetails.reduce( - (acc, cnt) => ({ - total: acc.total + cnt.total, - successful: acc.successful + cnt.successful, - failed: acc.failed + cnt.failed, - rerolled: acc.rerolled + cnt.rerolled, - dropped: acc.dropped + cnt.dropped, - exploded: acc.exploded + cnt.exploded, - }), - { - total: 0, - successful: 0, - failed: 0, - rerolled: 0, - dropped: 0, - exploded: 0, - }, - ); + returnMsg.counts = reduceCountDetails(tempCountDetails); } catch (e) { // Fill in the return block const solverError = e as Error; diff --git a/src/artigen/cmdTokenizer.ts b/src/artigen/cmdTokenizer.ts index d0bd0d4..3f7c256 100644 --- a/src/artigen/cmdTokenizer.ts +++ b/src/artigen/cmdTokenizer.ts @@ -13,6 +13,7 @@ import { tokenizeMath } from 'artigen/math/mathTokenizer.ts'; import { closeInternal, internalWrapRegex, openInternal } from 'artigen/utils/escape.ts'; import { loggingEnabled } from 'artigen/utils/logFlag.ts'; import { getMatchingInternalIdx, getMatchingPostfixIdx } from 'artigen/utils/parenBalance.ts'; +import { reduceCountDetails } from 'artigen/utils/counter.ts'; // tokenizeCmd expects a string[] of items that are either config.prefix/config.postfix or some text that contains math and/or dice rolls export const tokenizeCmd = (cmd: string[], modifiers: RollModifiers, topLevel: boolean, previousResults: number[] = []): [ReturnData[], CountDetails[]] => { @@ -28,15 +29,12 @@ export const tokenizeCmd = (cmd: string[], modifiers: RollModifiers, topLevel: b const openIdx = cmd.indexOf(config.prefix); const closeIdx = getMatchingPostfixIdx(cmd, openIdx); + const currentCmd = cmd.slice(openIdx + 1, closeIdx); + loggingEnabled && log(LT.LOG, `Setting previous results: topLevel:${topLevel} ${topLevel ? returnData.map((rd) => rd.rollTotal) : previousResults}`); // Handle any nested commands - const [tempData, tempCounts] = tokenizeCmd( - cmd.slice(openIdx + 1, closeIdx), - modifiers, - false, - topLevel ? returnData.map((rd) => rd.rollTotal) : previousResults, - ); + const [tempData, tempCounts] = tokenizeCmd(currentCmd, modifiers, false, topLevel ? returnData.map((rd) => rd.rollTotal) : previousResults); const data = tempData[0]; if (topLevel) { @@ -55,6 +53,24 @@ export const tokenizeCmd = (cmd: string[], modifiers: RollModifiers, topLevel: b // Store results returnData.push(data); countDetails.push(...tempCounts); + + if (topLevel && modifiers.confirmCrit && reduceCountDetails(tempCounts).successful) { + loggingEnabled && log(LT.LOG, `ConfirmCrit on ${JSON.stringify(currentCmd)}`); + let done = false; + while (!done) { + const [ccTempData, ccTempCounts] = tokenizeCmd(currentCmd, modifiers, false, topLevel ? returnData.map((rd) => rd.rollTotal) : previousResults); + const ccData = ccTempData[0]; + ccData.rollPreFormat = '\nAuto-Confirming Crit: '; + + loggingEnabled && log(LT.LOG, `ConfirmCrit on ${JSON.stringify(currentCmd)} | Rolled again ${JSON.stringify(ccData)} ${JSON.stringify(ccTempCounts)}`); + + // Store CC results + returnData.push(ccData); + countDetails.push(...ccTempCounts); + + done = reduceCountDetails(ccTempCounts).successful === 0; + } + } } if (topLevel) { diff --git a/src/artigen/dice/dice.d.ts b/src/artigen/dice/dice.d.ts index bd098bd..fb3cea6 100644 --- a/src/artigen/dice/dice.d.ts +++ b/src/artigen/dice/dice.d.ts @@ -45,6 +45,7 @@ export interface RollModifiers { order: string; count: boolean; commaTotals: boolean; + confirmCrit: boolean; apiWarn: string; valid: boolean; error: Error; diff --git a/src/artigen/dice/getModifiers.ts b/src/artigen/dice/getModifiers.ts index fa49520..fd2aa9b 100644 --- a/src/artigen/dice/getModifiers.ts +++ b/src/artigen/dice/getModifiers.ts @@ -15,6 +15,7 @@ export const Modifiers = Object.freeze({ GM: '-gm', Order: '-o', CommaTotals: '-ct', + ConfirmCrit: '-cc', }); export const getModifiers = (args: string[]): [RollModifiers, string[]] => { @@ -31,6 +32,7 @@ export const getModifiers = (args: string[]): [RollModifiers, string[]] => { order: '', count: false, commaTotals: false, + confirmCrit: false, apiWarn: '', valid: false, error: new Error(), @@ -66,6 +68,9 @@ export const getModifiers = (args: string[]): [RollModifiers, string[]] => { case Modifiers.Nominal: modifiers.nominalRoll = true; break; + case Modifiers.ConfirmCrit: + modifiers.confirmCrit = true; + break; case Modifiers.GM: modifiers.gmRoll = true; diff --git a/src/artigen/utils/counter.ts b/src/artigen/utils/counter.ts index dae7689..c09b13e 100644 --- a/src/artigen/utils/counter.ts +++ b/src/artigen/utils/counter.ts @@ -21,3 +21,24 @@ export const rollCounter = (rollSet: RollSet[]): CountDetails => { return countDetails; }; + +export const reduceCountDetails = (counts: CountDetails[]): CountDetails => { + return counts.reduce( + (acc, cnt) => ({ + total: acc.total + cnt.total, + successful: acc.successful + cnt.successful, + failed: acc.failed + cnt.failed, + rerolled: acc.rerolled + cnt.rerolled, + dropped: acc.dropped + cnt.dropped, + exploded: acc.exploded + cnt.exploded, + }), + { + total: 0, + successful: 0, + failed: 0, + rerolled: 0, + dropped: 0, + exploded: 0, + }, + ); +}; diff --git a/src/endpoints/gets/apiRoll.ts b/src/endpoints/gets/apiRoll.ts index 27d134e..1066eff 100644 --- a/src/endpoints/gets/apiRoll.ts +++ b/src/endpoints/gets/apiRoll.ts @@ -94,6 +94,7 @@ export const apiRoll = async (query: Map, apiUserid: bigint): Pr order: query.has('o') ? query.get('o')?.toLowerCase() || '' : '', count: query.has('c'), commaTotals: query.has('ct'), + confirmCrit: query.has('cc'), apiWarn: hideWarn ? '' : apiWarning, valid: true, error: new Error(),