Implement dice matching option
This commit is contained in:
parent
791dd3a626
commit
0e009441ca
|
@ -106,6 +106,10 @@ The Artificer comes with a few supplemental commands to the main rolling command
|
|||
| !!=u | Optional | Yes | compounding explosion, rolls one dy for each die that lands on u, but adds the resulting explosion to the die that caused this explosion |
|
||||
| !!>u | Optional | Yes | compounding explosion, rolls one dy for each die that lands on u or greater, but adds the resulting explosion to the die that caused this explosion |
|
||||
| !!<u | Optional | Yes | compounding explosion, rolls one dy for each die that lands on u or under, but adds the resulting explosion to the die that caused this explosion |
|
||||
| m | Optional | No | matching dice, adds labels to any dice that match |
|
||||
| mz | Optional | No | matching dice, adds labels to any dice that have z or more matches |
|
||||
| mt | Optional | No | matching dice, adds labels to any dice that match, changes result to be the count of labels added |
|
||||
| mtz | Optional | No | matching dice, adds labels to any dice that have z or more matches, changes result to be the count of labels added |
|
||||
|
||||
* If the parameter is Required, it must be provided at all times.
|
||||
* If the parameter is Repeatable, it may occur multiple times in the roll configuration.
|
||||
|
|
|
@ -15,6 +15,7 @@ export interface RollSet {
|
|||
critHit: boolean;
|
||||
critFail: boolean;
|
||||
isComplex: boolean;
|
||||
matchLabel: string;
|
||||
}
|
||||
|
||||
// CountDetails is the object holding the count data for creating the Count Embed
|
||||
|
@ -102,4 +103,14 @@ export interface RollConf {
|
|||
penetrating: boolean;
|
||||
nums: number[];
|
||||
};
|
||||
match: {
|
||||
on: boolean;
|
||||
minCount: number;
|
||||
returnTotal: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export interface SumOverride {
|
||||
on: boolean;
|
||||
value: number;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { log, LogTypes as LT } from '@Log4Deno';
|
||||
|
||||
import { RollModifiers, RollSet } from 'artigen/dice/dice.d.ts';
|
||||
import { RollModifiers, RollSet, SumOverride } from 'artigen/dice/dice.d.ts';
|
||||
import { genFateRoll, genRoll } from 'artigen/dice/randomRoll.ts';
|
||||
import { getRollConf } from 'artigen/dice/getRollConf.ts';
|
||||
|
||||
|
@ -8,10 +8,11 @@ import { loggingEnabled } from 'artigen/utils/logFlag.ts';
|
|||
import { compareOrigIdx, compareRolls } from 'artigen/utils/sortFuncs.ts';
|
||||
|
||||
import { getLoopCount, loopCountCheck } from 'artigen/managers/loopManager.ts';
|
||||
import { generateRollVals } from 'artigen/utils/rollValCounter.ts';
|
||||
|
||||
// roll(rollStr, modifiers) returns RollSet
|
||||
// roll parses and executes the rollStr
|
||||
export const executeRoll = (rollStr: string, modifiers: RollModifiers): RollSet[] => {
|
||||
export const executeRoll = (rollStr: string, modifiers: RollModifiers): [RollSet[], SumOverride] => {
|
||||
/* Roll Capabilities
|
||||
* Deciphers and rolls a single dice roll set
|
||||
*
|
||||
|
@ -67,6 +68,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): RollSet[
|
|||
rollConf.critScore.on ||
|
||||
rollConf.critFail.on ||
|
||||
rollConf.exploding.on,
|
||||
matchLabel: '',
|
||||
});
|
||||
|
||||
// Initial rolling, not handling reroll or exploding here
|
||||
|
@ -307,18 +309,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): RollSet[
|
|||
|
||||
// Handle OVA dropping/keeping
|
||||
if (rollConf.type === 'ova') {
|
||||
// Make "empty" vals array to easily sum up which die value is the greatest
|
||||
const rollVals: Array<number> = new Array(rollConf.dieSize).fill(0);
|
||||
|
||||
// Sum up all rolls
|
||||
for (const ovaRoll of rollSet) {
|
||||
loopCountCheck();
|
||||
|
||||
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | incrementing rollVals for ${JSON.stringify(ovaRoll)}`);
|
||||
if (!ovaRoll.dropped && !ovaRoll.rerolled) {
|
||||
rollVals[ovaRoll.roll - 1] += ovaRoll.roll;
|
||||
}
|
||||
}
|
||||
const rollVals: Array<number> = generateRollVals(rollConf, rollSet, rollStr, false);
|
||||
|
||||
// Find max value, using lastIndexOf to use the greatest die size max in case of duplicate maximums
|
||||
const maxRoll = rollVals.lastIndexOf(Math.max(...rollVals)) + 1;
|
||||
|
@ -337,5 +328,47 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): RollSet[
|
|||
}
|
||||
}
|
||||
|
||||
return rollSet;
|
||||
const sumOverride: SumOverride = {
|
||||
on: rollConf.match.returnTotal,
|
||||
value: 0,
|
||||
};
|
||||
if (rollConf.match.on) {
|
||||
const rollVals: Array<number> = generateRollVals(rollConf, rollSet, rollStr, true).map((count) => (count >= rollConf.match.minCount ? count : 0));
|
||||
const labels = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
let labelIdx = 0;
|
||||
const rollLabels: Array<string> = rollVals.map((count) => {
|
||||
loopCountCheck();
|
||||
|
||||
if (labelIdx >= labels.length) {
|
||||
throw new Error(`TooManyLabels_${labels.length}`);
|
||||
}
|
||||
|
||||
if (count) {
|
||||
return labels[labelIdx++];
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | current match state: ${rollVals} | ${rollLabels}`);
|
||||
|
||||
// Apply labels
|
||||
for (const roll of rollSet) {
|
||||
loopCountCheck();
|
||||
|
||||
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | trying to add a label to ${JSON.stringify(roll)}`);
|
||||
if (rollLabels[roll.roll - 1]) {
|
||||
roll.matchLabel = rollLabels[roll.roll - 1];
|
||||
} else if (rollConf.match.returnTotal) {
|
||||
roll.dropped = true;
|
||||
}
|
||||
}
|
||||
|
||||
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | labels added: ${JSON.stringify(rollSet)}`);
|
||||
|
||||
if (rollConf.match.returnTotal) {
|
||||
sumOverride.value = rollVals.filter((count) => count !== 0).length;
|
||||
}
|
||||
}
|
||||
|
||||
return [rollSet, sumOverride];
|
||||
};
|
||||
|
|
|
@ -19,7 +19,7 @@ export const generateFormattedRoll = (rollConf: string, modifiers: RollModifiers
|
|||
let tempComplex = false;
|
||||
|
||||
// Generate the roll, passing flags thru
|
||||
const tempRollSet = executeRoll(rollConf, modifiers);
|
||||
const [tempRollSet, sumOverride] = executeRoll(rollConf, modifiers);
|
||||
|
||||
// Loop thru all parts of the roll to document everything that was done to create the total roll
|
||||
tempRollSet.forEach((e) => {
|
||||
|
@ -72,8 +72,13 @@ export const generateFormattedRoll = (rollConf: string, modifiers: RollModifiers
|
|||
postFormat = `!${postFormat}`;
|
||||
}
|
||||
|
||||
let rollLabel = '';
|
||||
if (e.matchLabel) {
|
||||
rollLabel = `${e.matchLabel}:`;
|
||||
}
|
||||
|
||||
// Finally add this to the roll's details
|
||||
tempDetails += `${preFormat}${e.roll}${postFormat} + `;
|
||||
tempDetails += `${preFormat}${rollLabel}${e.roll}${postFormat} + `;
|
||||
});
|
||||
// After the looping is done, remove the extra " + " from the details and cap it with the closing ]
|
||||
tempDetails = tempDetails.substring(0, tempDetails.length - 3);
|
||||
|
@ -86,7 +91,7 @@ export const generateFormattedRoll = (rollConf: string, modifiers: RollModifiers
|
|||
|
||||
return {
|
||||
solvedStep: {
|
||||
total: tempTotal,
|
||||
total: sumOverride.on ? sumOverride.value : tempTotal,
|
||||
details: tempDetails,
|
||||
containsCrit: tempCrit,
|
||||
containsFail: tempFail,
|
||||
|
|
|
@ -61,6 +61,11 @@ export const getRollConf = (rollStr: string): RollConf => {
|
|||
penetrating: false,
|
||||
nums: [],
|
||||
},
|
||||
match: {
|
||||
on: false,
|
||||
minCount: 2,
|
||||
returnTotal: false,
|
||||
},
|
||||
};
|
||||
|
||||
// If the dPts is not long enough, throw error
|
||||
|
@ -380,12 +385,23 @@ export const getRollConf = (rollStr: string): RollConf => {
|
|||
!rollConf.exploding.nums.includes(i) && rollConf.exploding.nums.push(i);
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
case 'mt':
|
||||
rollConf.match.on = true;
|
||||
if (afterNumIdx > 0) {
|
||||
// User gave a number to explode on, save it
|
||||
rollConf.match.minCount = tNum;
|
||||
} else {
|
||||
// User did not give number, use cs
|
||||
afterNumIdx = 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Throw error immediately if unknown op is encountered
|
||||
throw new Error(`UnknownOperation_${tSep}`);
|
||||
}
|
||||
|
||||
// Exploding flags get set in their own switch statement to avoid weird duplicated code
|
||||
// Followup switch to avoid weird duplicated code
|
||||
switch (tSep) {
|
||||
case '!o':
|
||||
case '!o=':
|
||||
|
@ -405,6 +421,9 @@ export const getRollConf = (rollStr: string): RollConf => {
|
|||
case '!!<':
|
||||
rollConf.exploding.compounding = true;
|
||||
break;
|
||||
case 'mt':
|
||||
rollConf.match.returnTotal = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Finally slice off everything else parsed this loop
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import { log, LogTypes as LT } from '@Log4Deno';
|
||||
|
||||
import { RollConf, RollSet } from 'artigen/dice/dice.d.ts';
|
||||
|
||||
import { getLoopCount, loopCountCheck } from 'artigen/managers/loopManager.ts';
|
||||
|
||||
import { loggingEnabled } from 'artigen/utils/logFlag.ts';
|
||||
|
||||
// Can either count or sum each die
|
||||
export const generateRollVals = (rollConf: RollConf, rollSet: RollSet[], rollStr: string, count: boolean): Array<number> => {
|
||||
const rollVals = new Array(rollConf.dieSize).fill(0);
|
||||
|
||||
// Count up all rolls
|
||||
for (const ovaRoll of rollSet) {
|
||||
loopCountCheck();
|
||||
|
||||
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | incrementing rollVals for ${JSON.stringify(ovaRoll)}`);
|
||||
if (!ovaRoll.dropped && !ovaRoll.rerolled) {
|
||||
rollVals[ovaRoll.roll - 1] += count ? 1 : ovaRoll.roll;
|
||||
}
|
||||
}
|
||||
|
||||
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | rollVals ${rollVals}`);
|
||||
return rollVals;
|
||||
};
|
|
@ -112,6 +112,9 @@ export const translateError = (solverError: Error): [string, string] => {
|
|||
case 'IllegalVariable':
|
||||
errorMsg = `Error: \`${errorDetails}\` is not a valid variable`;
|
||||
break;
|
||||
case 'TooManyLabels':
|
||||
errorMsg = `Error: ${config.name} can only support a maximum of \`${errorDetails}\` labels when using the dice matching options (\`m\` or \`mt\`)`;
|
||||
break;
|
||||
default:
|
||||
log(LT.ERROR, `Unhandled Parser Error: ${errorName}, ${errorDetails}`);
|
||||
errorMsg = `Unhandled Error: ${solverError.message}\nCheck input and try again, if issue persists, please use \`${config.prefix}report\` to alert the devs of the issue`;
|
||||
|
|
|
@ -230,6 +230,22 @@ Any time a Compounding Explosion happens, the formatting on the die will still s
|
|||
],
|
||||
},
|
||||
],
|
||||
[
|
||||
'dice-matching',
|
||||
{
|
||||
name: 'Dice Matching',
|
||||
description: `**Usage:** \`xdym\`, \`xdymz\`, \`xdymt\`, or \`xdymtz\`
|
||||
\`z\` - Minimum count of matches for a label to be added
|
||||
|
||||
The basic \`m\` option will only add labels without modifying the results, whereas the \`mt\` options will add labels and will change the result of the roll to be equal to the number of labels that have been added.`,
|
||||
example: [
|
||||
'`[[10d6m]]` => [**C:6** + B:2 + 4 + __C:1__ + __C:1__ + B:2 + **C:6** + B:2 + **C:6** + **C:6**] = 36',
|
||||
'`[[10d6m4]]` => [**A:6** + 2 + 4 + __1__ + __1__ + 2 + **A:6** + 2 + **A:6** + **A:6**] = 36',
|
||||
'`[[10d6mt]]` => [**C:6** + B:2 + ~~4~~ + __C:1__ + __C:1__ + B:2 + **C:6** + B:2 + **C:6** + **C:6**] = 3',
|
||||
'`[[10d6mt4]]` => [**A:6** + ~~2~~ + ~~4~~ + ~~__1__~~ + ~~__1__~~ + ~~2~~ + **A:6** + ~~2~~ + **A:6** + **A:6**] = 1',
|
||||
],
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
export const DiceOptionsHelpPages: HelpPage = {
|
||||
|
|
Loading…
Reference in New Issue