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, 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 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 |
|
| !!<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 Required, it must be provided at all times.
|
||||||
* If the parameter is Repeatable, it may occur multiple times in the roll configuration.
|
* If the parameter is Repeatable, it may occur multiple times in the roll configuration.
|
||||||
|
|
|
@ -15,6 +15,7 @@ export interface RollSet {
|
||||||
critHit: boolean;
|
critHit: boolean;
|
||||||
critFail: boolean;
|
critFail: boolean;
|
||||||
isComplex: boolean;
|
isComplex: boolean;
|
||||||
|
matchLabel: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CountDetails is the object holding the count data for creating the Count Embed
|
// CountDetails is the object holding the count data for creating the Count Embed
|
||||||
|
@ -102,4 +103,14 @@ export interface RollConf {
|
||||||
penetrating: boolean;
|
penetrating: boolean;
|
||||||
nums: number[];
|
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 { 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 { genFateRoll, genRoll } from 'artigen/dice/randomRoll.ts';
|
||||||
import { getRollConf } from 'artigen/dice/getRollConf.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 { compareOrigIdx, compareRolls } from 'artigen/utils/sortFuncs.ts';
|
||||||
|
|
||||||
import { getLoopCount, loopCountCheck } from 'artigen/managers/loopManager.ts';
|
import { getLoopCount, loopCountCheck } from 'artigen/managers/loopManager.ts';
|
||||||
|
import { generateRollVals } from 'artigen/utils/rollValCounter.ts';
|
||||||
|
|
||||||
// roll(rollStr, modifiers) returns RollSet
|
// roll(rollStr, modifiers) returns RollSet
|
||||||
// roll parses and executes the rollStr
|
// 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
|
/* Roll Capabilities
|
||||||
* Deciphers and rolls a single dice roll set
|
* Deciphers and rolls a single dice roll set
|
||||||
*
|
*
|
||||||
|
@ -67,6 +68,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): RollSet[
|
||||||
rollConf.critScore.on ||
|
rollConf.critScore.on ||
|
||||||
rollConf.critFail.on ||
|
rollConf.critFail.on ||
|
||||||
rollConf.exploding.on,
|
rollConf.exploding.on,
|
||||||
|
matchLabel: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
// Initial rolling, not handling reroll or exploding here
|
// Initial rolling, not handling reroll or exploding here
|
||||||
|
@ -307,18 +309,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): RollSet[
|
||||||
|
|
||||||
// Handle OVA dropping/keeping
|
// Handle OVA dropping/keeping
|
||||||
if (rollConf.type === 'ova') {
|
if (rollConf.type === 'ova') {
|
||||||
// Make "empty" vals array to easily sum up which die value is the greatest
|
const rollVals: Array<number> = generateRollVals(rollConf, rollSet, rollStr, false);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find max value, using lastIndexOf to use the greatest die size max in case of duplicate maximums
|
// 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;
|
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;
|
let tempComplex = false;
|
||||||
|
|
||||||
// Generate the roll, passing flags thru
|
// 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
|
// Loop thru all parts of the roll to document everything that was done to create the total roll
|
||||||
tempRollSet.forEach((e) => {
|
tempRollSet.forEach((e) => {
|
||||||
|
@ -72,8 +72,13 @@ export const generateFormattedRoll = (rollConf: string, modifiers: RollModifiers
|
||||||
postFormat = `!${postFormat}`;
|
postFormat = `!${postFormat}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let rollLabel = '';
|
||||||
|
if (e.matchLabel) {
|
||||||
|
rollLabel = `${e.matchLabel}:`;
|
||||||
|
}
|
||||||
|
|
||||||
// Finally add this to the roll's details
|
// 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 ]
|
// After the looping is done, remove the extra " + " from the details and cap it with the closing ]
|
||||||
tempDetails = tempDetails.substring(0, tempDetails.length - 3);
|
tempDetails = tempDetails.substring(0, tempDetails.length - 3);
|
||||||
|
@ -86,7 +91,7 @@ export const generateFormattedRoll = (rollConf: string, modifiers: RollModifiers
|
||||||
|
|
||||||
return {
|
return {
|
||||||
solvedStep: {
|
solvedStep: {
|
||||||
total: tempTotal,
|
total: sumOverride.on ? sumOverride.value : tempTotal,
|
||||||
details: tempDetails,
|
details: tempDetails,
|
||||||
containsCrit: tempCrit,
|
containsCrit: tempCrit,
|
||||||
containsFail: tempFail,
|
containsFail: tempFail,
|
||||||
|
|
|
@ -61,6 +61,11 @@ export const getRollConf = (rollStr: string): RollConf => {
|
||||||
penetrating: false,
|
penetrating: false,
|
||||||
nums: [],
|
nums: [],
|
||||||
},
|
},
|
||||||
|
match: {
|
||||||
|
on: false,
|
||||||
|
minCount: 2,
|
||||||
|
returnTotal: false,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// If the dPts is not long enough, throw error
|
// 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);
|
!rollConf.exploding.nums.includes(i) && rollConf.exploding.nums.push(i);
|
||||||
}
|
}
|
||||||
break;
|
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:
|
default:
|
||||||
// Throw error immediately if unknown op is encountered
|
// Throw error immediately if unknown op is encountered
|
||||||
throw new Error(`UnknownOperation_${tSep}`);
|
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) {
|
switch (tSep) {
|
||||||
case '!o':
|
case '!o':
|
||||||
case '!o=':
|
case '!o=':
|
||||||
|
@ -405,6 +421,9 @@ export const getRollConf = (rollStr: string): RollConf => {
|
||||||
case '!!<':
|
case '!!<':
|
||||||
rollConf.exploding.compounding = true;
|
rollConf.exploding.compounding = true;
|
||||||
break;
|
break;
|
||||||
|
case 'mt':
|
||||||
|
rollConf.match.returnTotal = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally slice off everything else parsed this loop
|
// 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':
|
case 'IllegalVariable':
|
||||||
errorMsg = `Error: \`${errorDetails}\` is not a valid variable`;
|
errorMsg = `Error: \`${errorDetails}\` is not a valid variable`;
|
||||||
break;
|
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:
|
default:
|
||||||
log(LT.ERROR, `Unhandled Parser Error: ${errorName}, ${errorDetails}`);
|
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`;
|
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 = {
|
export const DiceOptionsHelpPages: HelpPage = {
|
||||||
|
|
Loading…
Reference in New Issue