Move getRollConf to new file, move loopCountChecker to a manager file, implement loopCountCheck on all loops inside artigen
This commit is contained in:
parent
c6c1a8918e
commit
bb9a2014ed
|
@ -6,6 +6,8 @@ import { ReturnData } from 'artigen/artigen.d.ts';
|
||||||
|
|
||||||
import { CountDetails, RollModifiers } from 'artigen/dice/dice.d.ts';
|
import { CountDetails, RollModifiers } from 'artigen/dice/dice.d.ts';
|
||||||
|
|
||||||
|
import { loopCountCheck } from 'artigen/managers/loopManager.ts';
|
||||||
|
|
||||||
import { tokenizeMath } from 'artigen/math/mathTokenizer.ts';
|
import { tokenizeMath } from 'artigen/math/mathTokenizer.ts';
|
||||||
|
|
||||||
import { closeInternal, internalWrapRegex, openInternal } from 'artigen/utils/escape.ts';
|
import { closeInternal, internalWrapRegex, openInternal } from 'artigen/utils/escape.ts';
|
||||||
|
@ -21,6 +23,8 @@ export const tokenizeCmd = (cmd: string[], modifiers: RollModifiers, topLevel: b
|
||||||
|
|
||||||
// Wrapped commands still exist, unwrap them
|
// Wrapped commands still exist, unwrap them
|
||||||
while (cmd.includes(config.prefix)) {
|
while (cmd.includes(config.prefix)) {
|
||||||
|
loopCountCheck();
|
||||||
|
|
||||||
const openIdx = cmd.indexOf(config.prefix);
|
const openIdx = cmd.indexOf(config.prefix);
|
||||||
const closeIdx = getMatchingPostfixIdx(cmd, openIdx);
|
const closeIdx = getMatchingPostfixIdx(cmd, openIdx);
|
||||||
|
|
||||||
|
@ -67,6 +71,8 @@ export const tokenizeCmd = (cmd: string[], modifiers: RollModifiers, topLevel: b
|
||||||
const initConf = data.initConfig.split(internalWrapRegex).filter((x) => x);
|
const initConf = data.initConfig.split(internalWrapRegex).filter((x) => x);
|
||||||
loggingEnabled && log(LT.LOG, `Split solved math initConfig ${JSON.stringify(initConf)}`);
|
loggingEnabled && log(LT.LOG, `Split solved math initConfig ${JSON.stringify(initConf)}`);
|
||||||
while (initConf.includes(openInternal)) {
|
while (initConf.includes(openInternal)) {
|
||||||
|
loopCountCheck();
|
||||||
|
|
||||||
const openIdx = initConf.indexOf(openInternal);
|
const openIdx = initConf.indexOf(openInternal);
|
||||||
const closeIdx = getMatchingInternalIdx(initConf, openIdx);
|
const closeIdx = getMatchingInternalIdx(initConf, openIdx);
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { SolvedStep } from 'artigen/math/math.d.ts';
|
import { SolvedStep } from 'artigen/math/math.d.ts';
|
||||||
|
|
||||||
// Available Roll Types
|
// Available Roll Types
|
||||||
export type RollType = '' | 'roll20' | 'fate' | 'cwod' | 'ova';
|
type RollType = '' | 'roll20' | 'fate' | 'cwod' | 'ova';
|
||||||
|
|
||||||
// RollSet is used to preserve all information about a calculated roll
|
// RollSet is used to preserve all information about a calculated roll
|
||||||
export interface RollSet {
|
export interface RollSet {
|
||||||
|
@ -67,6 +67,7 @@ export interface DPercentConf {
|
||||||
|
|
||||||
// RollConf is used by the roll20 setup
|
// RollConf is used by the roll20 setup
|
||||||
export interface RollConf {
|
export interface RollConf {
|
||||||
|
type: RollType;
|
||||||
dieCount: number;
|
dieCount: number;
|
||||||
dieSize: number;
|
dieSize: number;
|
||||||
dPercent: DPercentConf;
|
dPercent: DPercentConf;
|
||||||
|
|
|
@ -1,24 +1,13 @@
|
||||||
import { log, LogTypes as LT } from '@Log4Deno';
|
import { log, LogTypes as LT } from '@Log4Deno';
|
||||||
|
|
||||||
import config from '~config';
|
import { RollModifiers, RollSet } from 'artigen/dice/dice.d.ts';
|
||||||
|
import { genFateRoll, genRoll } from 'artigen/dice/randomRoll.ts';
|
||||||
|
import { getRollConf } from 'artigen/dice/getRollConf.ts';
|
||||||
|
|
||||||
import { RollConf, RollModifiers, RollSet, RollType } from 'artigen/dice/dice.d.ts';
|
|
||||||
|
|
||||||
import { genFateRoll, genRoll } from 'artigen/utils/generateRoll.ts';
|
|
||||||
import { loggingEnabled } from 'artigen/utils/logFlag.ts';
|
import { loggingEnabled } from 'artigen/utils/logFlag.ts';
|
||||||
import { compareOrigIdx, compareRolls } from 'artigen/utils/sortFuncs.ts';
|
import { compareOrigIdx, compareRolls } from 'artigen/utils/sortFuncs.ts';
|
||||||
|
|
||||||
// Call with loopCountCheck(++loopCount);
|
import { getLoopCount, loopCountCheck } from 'artigen/managers/loopManager.ts';
|
||||||
// Will ensure if maxLoops is 10, 10 loops will be allowed, 11 will not.
|
|
||||||
const loopCountCheck = (loopCount: number): void => {
|
|
||||||
if (loopCount > config.limits.maxLoops) {
|
|
||||||
throw new Error('MaxLoopsExceeded');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const throwDoubleSepError = (sep: string): void => {
|
|
||||||
throw new Error(`DoubleSeparator_${sep}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
// roll(rollStr, modifiers) returns RollSet
|
// roll(rollStr, modifiers) returns RollSet
|
||||||
// roll parses and executes the rollStr
|
// roll parses and executes the rollStr
|
||||||
|
@ -29,441 +18,11 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): RollSet[
|
||||||
* Check the README.md of this project for details on the roll options. I gave up trying to keep three places updated at once.
|
* Check the README.md of this project for details on the roll options. I gave up trying to keep three places updated at once.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Begin counting the number of loops to prevent from getting into an infinite loop
|
|
||||||
let loopCount = 0;
|
|
||||||
|
|
||||||
// Make entire roll lowercase for ease of parsing
|
// Make entire roll lowercase for ease of parsing
|
||||||
rollStr = rollStr.toLowerCase();
|
rollStr = rollStr.toLowerCase();
|
||||||
|
|
||||||
// Split the roll on the die size (and the drop if its there)
|
// Turn the rollStr into a machine readable rollConf
|
||||||
const dPts = rollStr.split('d');
|
const rollConf = getRollConf(rollStr);
|
||||||
|
|
||||||
// Initialize the configuration to store the parsed data
|
|
||||||
let rollType: RollType = '';
|
|
||||||
const rollConf: RollConf = {
|
|
||||||
dieCount: 0,
|
|
||||||
dieSize: 0,
|
|
||||||
dPercent: {
|
|
||||||
on: false,
|
|
||||||
sizeAdjustment: 0,
|
|
||||||
critVal: 0,
|
|
||||||
},
|
|
||||||
drop: {
|
|
||||||
on: false,
|
|
||||||
count: 0,
|
|
||||||
},
|
|
||||||
keep: {
|
|
||||||
on: false,
|
|
||||||
count: 0,
|
|
||||||
},
|
|
||||||
dropHigh: {
|
|
||||||
on: false,
|
|
||||||
count: 0,
|
|
||||||
},
|
|
||||||
keepLow: {
|
|
||||||
on: false,
|
|
||||||
count: 0,
|
|
||||||
},
|
|
||||||
reroll: {
|
|
||||||
on: false,
|
|
||||||
once: false,
|
|
||||||
nums: [],
|
|
||||||
},
|
|
||||||
critScore: {
|
|
||||||
on: false,
|
|
||||||
range: [],
|
|
||||||
},
|
|
||||||
critFail: {
|
|
||||||
on: false,
|
|
||||||
range: [],
|
|
||||||
},
|
|
||||||
exploding: {
|
|
||||||
on: false,
|
|
||||||
once: false,
|
|
||||||
compounding: false,
|
|
||||||
penetrating: false,
|
|
||||||
nums: [],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// If the dPts is not long enough, throw error
|
|
||||||
if (dPts.length < 2) {
|
|
||||||
throw new Error('YouNeedAD');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill out the die count, first item will either be an int or empty string, short circuit execution will take care of replacing the empty string with a 1
|
|
||||||
const rawDC = dPts.shift() || '1';
|
|
||||||
if (rawDC.includes('.')) {
|
|
||||||
throw new Error('WholeDieCountSizeOnly');
|
|
||||||
} else if (rawDC.match(/\D/)) {
|
|
||||||
throw new Error(`CannotParseDieCount_${rawDC}`);
|
|
||||||
}
|
|
||||||
const tempDC = rawDC.replace(/\D/g, '');
|
|
||||||
// Rejoin all remaining parts
|
|
||||||
let remains = dPts.join('d');
|
|
||||||
|
|
||||||
// Manual Parsing for custom roll types
|
|
||||||
let manualParse = false;
|
|
||||||
if (rawDC.endsWith('cwo')) {
|
|
||||||
// CWOD dice parsing
|
|
||||||
rollType = 'cwod';
|
|
||||||
manualParse = true;
|
|
||||||
|
|
||||||
// Get CWOD parts, setting count and getting difficulty
|
|
||||||
const cwodParts = rollStr.split('cwod');
|
|
||||||
rollConf.dieCount = parseInt(cwodParts[0] || '1');
|
|
||||||
rollConf.dieSize = 10;
|
|
||||||
|
|
||||||
// Use critScore to set the difficulty
|
|
||||||
rollConf.critScore.on = true;
|
|
||||||
const difficulty = parseInt(cwodParts[1] || '10');
|
|
||||||
for (let i = difficulty; i <= rollConf.dieSize; i++) {
|
|
||||||
loopCountCheck(++loopCount);
|
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `${loopCount} Handling cwod ${rollStr} | Parsing difficulty ${i}`);
|
|
||||||
rollConf.critScore.range.push(i);
|
|
||||||
}
|
|
||||||
} else if (rawDC.endsWith('ova')) {
|
|
||||||
// OVA dice parsing
|
|
||||||
rollType = 'ova';
|
|
||||||
manualParse = true;
|
|
||||||
|
|
||||||
// Get OVA parts, setting count and getting difficulty
|
|
||||||
const ovaParts = rollStr.split('ovad');
|
|
||||||
const ovaPart1 = ovaParts[1] || '6';
|
|
||||||
if (ovaPart1.search(/\d+\.\d/) === 0) {
|
|
||||||
throw new Error('WholeDieCountSizeOnly');
|
|
||||||
}
|
|
||||||
rollConf.dieCount = parseInt(ovaParts[0] || '1');
|
|
||||||
rollConf.dieSize = parseInt(ovaPart1);
|
|
||||||
} else if (remains.startsWith('f')) {
|
|
||||||
// fate dice setup
|
|
||||||
rollType = 'fate';
|
|
||||||
rollConf.dieCount = parseInt(tempDC);
|
|
||||||
// dieSize set to 1 as 1 is max face value, a six sided die is used internally
|
|
||||||
rollConf.dieSize = 1;
|
|
||||||
|
|
||||||
// remove F from the remains
|
|
||||||
remains = remains.slice(1);
|
|
||||||
} else {
|
|
||||||
// roll20 dice setup
|
|
||||||
rollType = 'roll20';
|
|
||||||
rollConf.dieCount = parseInt(tempDC);
|
|
||||||
|
|
||||||
// Finds the end of the die size/beginning of the additional options
|
|
||||||
let afterDieIdx = dPts[0].search(/[^%\d]/);
|
|
||||||
if (afterDieIdx === -1) {
|
|
||||||
afterDieIdx = dPts[0].length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the die size out of the remains and into the rollConf
|
|
||||||
const rawDS = remains.slice(0, afterDieIdx);
|
|
||||||
remains = remains.slice(afterDieIdx);
|
|
||||||
|
|
||||||
if (rawDS.startsWith('%')) {
|
|
||||||
rollConf.dieSize = 10;
|
|
||||||
rollConf.dPercent.on = true;
|
|
||||||
const percentCount = rawDS.match(/%/g)?.length ?? 1;
|
|
||||||
rollConf.dPercent.sizeAdjustment = Math.pow(10, percentCount - 1);
|
|
||||||
rollConf.dPercent.critVal = Math.pow(10, percentCount) - rollConf.dPercent.sizeAdjustment;
|
|
||||||
} else {
|
|
||||||
rollConf.dieSize = parseInt(rawDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remains.search(/\.\d/) === 0) {
|
|
||||||
throw new Error('WholeDieCountSizeOnly');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!rollConf.dieCount || !rollConf.dieSize) {
|
|
||||||
throw new Error('YouNeedAD');
|
|
||||||
}
|
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `${loopCount} Handling ${rollType} ${rollStr} | Parsed Die Count: ${rollConf.dieCount}`);
|
|
||||||
loggingEnabled && log(LT.LOG, `${loopCount} Handling ${rollType} ${rollStr} | Parsed Die Size: ${rollConf.dieSize}`);
|
|
||||||
|
|
||||||
// Finish parsing the roll
|
|
||||||
if (!manualParse && remains.length > 0) {
|
|
||||||
// Determine if the first item is a drop, and if it is, add the d back in
|
|
||||||
if (remains.search(/\D/) !== 0 || remains.indexOf('l') === 0 || remains.indexOf('h') === 0) {
|
|
||||||
remains = `d${remains}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop until all remaining args are parsed
|
|
||||||
while (remains.length > 0) {
|
|
||||||
loopCountCheck(++loopCount);
|
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `${loopCount} Handling ${rollType} ${rollStr} | Parsing remains ${remains}`);
|
|
||||||
// Find the next number in the remains to be able to cut out the rule name
|
|
||||||
let afterSepIdx = remains.search(/\d/);
|
|
||||||
if (afterSepIdx < 0) {
|
|
||||||
afterSepIdx = remains.length;
|
|
||||||
}
|
|
||||||
// Save the rule name to tSep and remove it from remains
|
|
||||||
const tSep = remains.slice(0, afterSepIdx);
|
|
||||||
remains = remains.slice(afterSepIdx);
|
|
||||||
// Find the next non-number in the remains to be able to cut out the count/num
|
|
||||||
let afterNumIdx = remains.search(/\D/);
|
|
||||||
if (afterNumIdx < 0) {
|
|
||||||
afterNumIdx = remains.length;
|
|
||||||
}
|
|
||||||
// Save the count/num to tNum leaving it in remains for the time being
|
|
||||||
const tNum = parseInt(remains.slice(0, afterNumIdx));
|
|
||||||
|
|
||||||
// Switch on rule name
|
|
||||||
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;
|
|
||||||
break;
|
|
||||||
case 'ro':
|
|
||||||
case 'ro=':
|
|
||||||
rollConf.reroll.once = true;
|
|
||||||
// falls through as ro/ro= functions the same as r/r= in this context
|
|
||||||
case 'r':
|
|
||||||
case 'r=':
|
|
||||||
// Configure Reroll (this can happen multiple times)
|
|
||||||
rollConf.reroll.on = true;
|
|
||||||
!rollConf.reroll.nums.includes(tNum) && rollConf.reroll.nums.push(tNum);
|
|
||||||
break;
|
|
||||||
case 'ro>':
|
|
||||||
rollConf.reroll.once = true;
|
|
||||||
// falls through as ro> functions the same as r> in this context
|
|
||||||
case 'r>':
|
|
||||||
// Configure reroll for all numbers greater than or equal to tNum (this could happen multiple times, but why)
|
|
||||||
rollConf.reroll.on = true;
|
|
||||||
for (let i = tNum; i <= rollConf.dieSize; i++) {
|
|
||||||
loopCountCheck(++loopCount);
|
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `${loopCount} Handling ${rollType} ${rollStr} | Parsing r> ${i}`);
|
|
||||||
!rollConf.reroll.nums.includes(i) && rollConf.reroll.nums.push(i);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'ro<':
|
|
||||||
rollConf.reroll.once = true;
|
|
||||||
// falls through as ro< functions the same as r< in this context
|
|
||||||
case 'r<':
|
|
||||||
// Configure reroll for all numbers less than or equal to tNum (this could happen multiple times, but why)
|
|
||||||
rollConf.reroll.on = true;
|
|
||||||
for (let i = 1; i <= tNum; i++) {
|
|
||||||
loopCountCheck(++loopCount);
|
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `${loopCount} Handling ${rollType} ${rollStr} | Parsing r< ${i}`);
|
|
||||||
!rollConf.reroll.nums.includes(i) && rollConf.reroll.nums.push(i);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'cs':
|
|
||||||
case 'cs=':
|
|
||||||
// Configure CritScore for one number (this can happen multiple times)
|
|
||||||
rollConf.critScore.on = true;
|
|
||||||
!rollConf.critScore.range.includes(tNum) && rollConf.critScore.range.push(tNum);
|
|
||||||
break;
|
|
||||||
case 'cs>':
|
|
||||||
// Configure CritScore for all numbers greater than or equal to tNum (this could happen multiple times, but why)
|
|
||||||
rollConf.critScore.on = true;
|
|
||||||
for (let i = tNum; i <= rollConf.dieSize; i++) {
|
|
||||||
loopCountCheck(++loopCount);
|
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `${loopCount} Handling ${rollType} ${rollStr} | Parsing cs> ${i}`);
|
|
||||||
!rollConf.critScore.range.includes(i) && rollConf.critScore.range.push(i);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'cs<':
|
|
||||||
// Configure CritScore for all numbers less than or equal to tNum (this could happen multiple times, but why)
|
|
||||||
rollConf.critScore.on = true;
|
|
||||||
for (let i = 0; i <= tNum; i++) {
|
|
||||||
loopCountCheck(++loopCount);
|
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `${loopCount} Handling ${rollType} ${rollStr} | Parsing cs< ${i}`);
|
|
||||||
!rollConf.critScore.range.includes(i) && rollConf.critScore.range.push(i);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'cf':
|
|
||||||
case 'cf=':
|
|
||||||
// Configure CritFail for one number (this can happen multiple times)
|
|
||||||
rollConf.critFail.on = true;
|
|
||||||
!rollConf.critFail.range.includes(tNum) && rollConf.critFail.range.push(tNum);
|
|
||||||
break;
|
|
||||||
case 'cf>':
|
|
||||||
// Configure CritFail for all numbers greater than or equal to tNum (this could happen multiple times, but why)
|
|
||||||
rollConf.critFail.on = true;
|
|
||||||
for (let i = tNum; i <= rollConf.dieSize; i++) {
|
|
||||||
loopCountCheck(++loopCount);
|
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `${loopCount} Handling ${rollType} ${rollStr} | Parsing cf> ${i}`);
|
|
||||||
!rollConf.critFail.range.includes(i) && rollConf.critFail.range.push(i);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'cf<':
|
|
||||||
// Configure CritFail for all numbers less than or equal to tNum (this could happen multiple times, but why)
|
|
||||||
rollConf.critFail.on = true;
|
|
||||||
for (let i = 0; i <= tNum; i++) {
|
|
||||||
loopCountCheck(++loopCount);
|
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `${loopCount} Handling ${rollType} ${rollStr} | Parsing cf< ${i}`);
|
|
||||||
!rollConf.critFail.range.includes(i) && rollConf.critFail.range.push(i);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case '!':
|
|
||||||
case '!o':
|
|
||||||
case '!p':
|
|
||||||
case '!!':
|
|
||||||
// Configure Exploding
|
|
||||||
rollConf.exploding.on = true;
|
|
||||||
if (afterNumIdx > 0) {
|
|
||||||
// User gave a number to explode on, save it
|
|
||||||
!rollConf.exploding.nums.includes(tNum) && rollConf.exploding.nums.push(tNum);
|
|
||||||
} else {
|
|
||||||
// User did not give number, use cs
|
|
||||||
afterNumIdx = 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case '!=':
|
|
||||||
case '!o=':
|
|
||||||
case '!p=':
|
|
||||||
case '!!=':
|
|
||||||
// Configure Exploding (this can happen multiple times)
|
|
||||||
rollConf.exploding.on = true;
|
|
||||||
!rollConf.exploding.nums.includes(tNum) && rollConf.exploding.nums.push(tNum);
|
|
||||||
break;
|
|
||||||
case '!>':
|
|
||||||
case '!o>':
|
|
||||||
case '!p>':
|
|
||||||
case '!!>':
|
|
||||||
// Configure Exploding for all numbers greater than or equal to tNum (this could happen multiple times, but why)
|
|
||||||
rollConf.exploding.on = true;
|
|
||||||
for (let i = tNum; i <= rollConf.dieSize; i++) {
|
|
||||||
loopCountCheck(++loopCount);
|
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `${loopCount} Handling ${rollType} ${rollStr} | Parsing !> ${i}`);
|
|
||||||
!rollConf.exploding.nums.includes(i) && rollConf.exploding.nums.push(i);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case '!<':
|
|
||||||
case '!o<':
|
|
||||||
case '!p<':
|
|
||||||
case '!!<':
|
|
||||||
// Configure Exploding for all numbers less than or equal to tNum (this could happen multiple times, but why)
|
|
||||||
rollConf.exploding.on = true;
|
|
||||||
for (let i = 1; i <= tNum; i++) {
|
|
||||||
loopCountCheck(++loopCount);
|
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `${loopCount} Handling ${rollType} ${rollStr} | Parsing !< ${i}`);
|
|
||||||
!rollConf.exploding.nums.includes(i) && rollConf.exploding.nums.push(i);
|
|
||||||
}
|
|
||||||
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
|
|
||||||
switch (tSep) {
|
|
||||||
case '!o':
|
|
||||||
case '!o=':
|
|
||||||
case '!o>':
|
|
||||||
case '!o<':
|
|
||||||
rollConf.exploding.once = true;
|
|
||||||
break;
|
|
||||||
case '!p':
|
|
||||||
case '!p=':
|
|
||||||
case '!p>':
|
|
||||||
case '!p<':
|
|
||||||
rollConf.exploding.penetrating = true;
|
|
||||||
break;
|
|
||||||
case '!!':
|
|
||||||
case '!!=':
|
|
||||||
case '!!>':
|
|
||||||
case '!!<':
|
|
||||||
rollConf.exploding.compounding = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally slice off everything else parsed this loop
|
|
||||||
remains = remains.slice(afterNumIdx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify the parse, throwing errors for every invalid config
|
|
||||||
if (rollConf.dieCount < 0) {
|
|
||||||
throw new Error('NoZerosAllowed_base');
|
|
||||||
}
|
|
||||||
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) => {
|
|
||||||
loggingEnabled && log(LT.LOG, `Handling ${rollType} ${rollStr} | Checking if drop/keep is on ${e}`);
|
|
||||||
if (e) {
|
|
||||||
dkdkCnt++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (dkdkCnt > 1) {
|
|
||||||
throw new Error('FormattingError_dk');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rollConf.drop.on && rollConf.drop.count === 0) {
|
|
||||||
throw new Error('NoZerosAllowed_drop');
|
|
||||||
}
|
|
||||||
if (rollConf.keep.on && rollConf.keep.count === 0) {
|
|
||||||
throw new Error('NoZerosAllowed_keep');
|
|
||||||
}
|
|
||||||
if (rollConf.dropHigh.on && rollConf.dropHigh.count === 0) {
|
|
||||||
throw new Error('NoZerosAllowed_dropHigh');
|
|
||||||
}
|
|
||||||
if (rollConf.keepLow.on && rollConf.keepLow.count === 0) {
|
|
||||||
throw new Error('NoZerosAllowed_keepLow');
|
|
||||||
}
|
|
||||||
if (rollConf.reroll.on && !rollConf.dPercent.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 > (rollConf.dPercent.on ? -1 : 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');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Roll the roll
|
// Roll the roll
|
||||||
const rollSet = [];
|
const rollSet = [];
|
||||||
|
@ -492,7 +51,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): RollSet[
|
||||||
|
|
||||||
// Initialize a template rollSet to copy multiple times
|
// Initialize a template rollSet to copy multiple times
|
||||||
const getTemplateRoll = (): RollSet => ({
|
const getTemplateRoll = (): RollSet => ({
|
||||||
type: rollType,
|
type: rollConf.type,
|
||||||
origIdx: 0,
|
origIdx: 0,
|
||||||
roll: 0,
|
roll: 0,
|
||||||
dropped: false,
|
dropped: false,
|
||||||
|
@ -504,14 +63,14 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): RollSet[
|
||||||
|
|
||||||
// Initial rolling, not handling reroll or exploding here
|
// Initial rolling, not handling reroll or exploding here
|
||||||
for (let i = 0; i < rollConf.dieCount; i++) {
|
for (let i = 0; i < rollConf.dieCount; i++) {
|
||||||
loggingEnabled && log(LT.LOG, `${loopCount} Handling ${rollType} ${rollStr} | Initial rolling ${i} of ${JSON.stringify(rollConf)}`);
|
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Initial rolling ${i} of ${JSON.stringify(rollConf)}`);
|
||||||
// If loopCount gets too high, stop trying to calculate infinity
|
// If loopCount gets too high, stop trying to calculate infinity
|
||||||
loopCountCheck(++loopCount);
|
loopCountCheck();
|
||||||
|
|
||||||
// Copy the template to fill out for this iteration
|
// Copy the template to fill out for this iteration
|
||||||
const rolling = getTemplateRoll();
|
const rolling = getTemplateRoll();
|
||||||
// If maximizeRoll is on, set the roll to the dieSize, else if nominalRoll is on, set the roll to the average roll of dieSize, else generate a new random roll
|
// If maximizeRoll is on, set the roll to the dieSize, else if nominalRoll is on, set the roll to the average roll of dieSize, else generate a new random roll
|
||||||
rolling.roll = rollType === 'fate' ? genFateRoll(modifiers) : genRoll(rollConf.dieSize, modifiers, rollConf.dPercent);
|
rolling.roll = rollConf.type === 'fate' ? genFateRoll(modifiers) : genRoll(rollConf.dieSize, modifiers, rollConf.dPercent);
|
||||||
// Set origIdx of roll
|
// Set origIdx of roll
|
||||||
rolling.origIdx = i;
|
rolling.origIdx = i;
|
||||||
|
|
||||||
|
@ -525,7 +84,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): RollSet[
|
||||||
if (rollConf.critFail.on && rollConf.critFail.range.includes(rolling.roll)) {
|
if (rollConf.critFail.on && rollConf.critFail.range.includes(rolling.roll)) {
|
||||||
rolling.critFail = true;
|
rolling.critFail = true;
|
||||||
} else if (!rollConf.critFail.on) {
|
} else if (!rollConf.critFail.on) {
|
||||||
if (rollType === 'fate') {
|
if (rollConf.type === 'fate') {
|
||||||
rolling.critFail = rolling.roll === -1;
|
rolling.critFail = rolling.roll === -1;
|
||||||
} else {
|
} else {
|
||||||
rolling.critFail = rolling.roll === (rollConf.dPercent.on ? 0 : 1);
|
rolling.critFail = rolling.roll === (rollConf.dPercent.on ? 0 : 1);
|
||||||
|
@ -540,9 +99,9 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): RollSet[
|
||||||
if (rollConf.reroll.on || rollConf.exploding.on) {
|
if (rollConf.reroll.on || rollConf.exploding.on) {
|
||||||
let minMaxOverride = 0;
|
let minMaxOverride = 0;
|
||||||
for (let i = 0; i < rollSet.length; i++) {
|
for (let i = 0; i < rollSet.length; i++) {
|
||||||
loggingEnabled && log(LT.LOG, `${loopCount} Handling ${rollType} ${rollStr} | Handling rerolling and exploding ${JSON.stringify(rollSet[i])}`);
|
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Handling rerolling and exploding ${JSON.stringify(rollSet[i])}`);
|
||||||
// If loopCount gets too high, stop trying to calculate infinity
|
// If loopCount gets too high, stop trying to calculate infinity
|
||||||
loopCountCheck(++loopCount);
|
loopCountCheck();
|
||||||
|
|
||||||
// This big boolean statement first checks if reroll is on, if the roll is within the reroll range, and finally if ro is ON, make sure we haven't already rerolled the roll
|
// This big boolean statement first checks if reroll is on, if the roll is within the reroll range, and finally if ro is ON, make sure we haven't already rerolled the roll
|
||||||
if (rollConf.reroll.on && rollConf.reroll.nums.includes(rollSet[i].roll) && (!rollConf.reroll.once || !rollSet[i ? i - 1 : i].rerolled)) {
|
if (rollConf.reroll.on && rollConf.reroll.nums.includes(rollSet[i].roll) && (!rollConf.reroll.once || !rollSet[i ? i - 1 : i].rerolled)) {
|
||||||
|
@ -554,7 +113,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): RollSet[
|
||||||
if (modifiers.maxRoll && !minMaxOverride) {
|
if (modifiers.maxRoll && !minMaxOverride) {
|
||||||
// If maximizeRoll is on and we've entered the reroll code, dieSize is not allowed, determine the next best option and always return that
|
// If maximizeRoll is on and we've entered the reroll code, dieSize is not allowed, determine the next best option and always return that
|
||||||
mmMaxLoop: for (let m = rollConf.dieSize - 1; m > 0; m--) {
|
mmMaxLoop: for (let m = rollConf.dieSize - 1; m > 0; m--) {
|
||||||
loopCountCheck(++loopCount);
|
loopCountCheck();
|
||||||
|
|
||||||
if (!rollConf.reroll.nums.includes(m)) {
|
if (!rollConf.reroll.nums.includes(m)) {
|
||||||
minMaxOverride = m;
|
minMaxOverride = m;
|
||||||
|
@ -564,7 +123,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): RollSet[
|
||||||
} else if (modifiers.minRoll && !minMaxOverride) {
|
} else if (modifiers.minRoll && !minMaxOverride) {
|
||||||
// If minimizeRoll is on and we've entered the reroll code, 1 is not allowed, determine the next best option and always return that
|
// If minimizeRoll is on and we've entered the reroll code, 1 is not allowed, determine the next best option and always return that
|
||||||
mmMinLoop: for (let m = rollConf.dPercent.on ? 1 : 2; m <= rollConf.dieSize; m++) {
|
mmMinLoop: for (let m = rollConf.dPercent.on ? 1 : 2; m <= rollConf.dieSize; m++) {
|
||||||
loopCountCheck(++loopCount);
|
loopCountCheck();
|
||||||
|
|
||||||
if (!rollConf.reroll.nums.includes(m)) {
|
if (!rollConf.reroll.nums.includes(m)) {
|
||||||
minMaxOverride = m;
|
minMaxOverride = m;
|
||||||
|
@ -633,9 +192,9 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): RollSet[
|
||||||
// If penetrating is on, do the decrements
|
// If penetrating is on, do the decrements
|
||||||
if (rollConf.exploding.penetrating) {
|
if (rollConf.exploding.penetrating) {
|
||||||
for (const penRoll of rollSet) {
|
for (const penRoll of rollSet) {
|
||||||
loggingEnabled && log(LT.LOG, `${loopCount} Handling ${rollType} ${rollStr} | Handling penetrating explosions ${JSON.stringify(penRoll)}`);
|
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Handling penetrating explosions ${JSON.stringify(penRoll)}`);
|
||||||
// If loopCount gets too high, stop trying to calculate infinity
|
// If loopCount gets too high, stop trying to calculate infinity
|
||||||
loopCountCheck(++loopCount);
|
loopCountCheck();
|
||||||
|
|
||||||
// If the die was from an explosion, decrement it by one
|
// If the die was from an explosion, decrement it by one
|
||||||
if (penRoll.exploding) {
|
if (penRoll.exploding) {
|
||||||
|
@ -647,9 +206,9 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): RollSet[
|
||||||
// Handle compounding explosions
|
// Handle compounding explosions
|
||||||
if (rollConf.exploding.compounding) {
|
if (rollConf.exploding.compounding) {
|
||||||
for (let i = 0; i < rollSet.length; i++) {
|
for (let i = 0; i < rollSet.length; i++) {
|
||||||
loggingEnabled && log(LT.LOG, `${loopCount} Handling ${rollType} ${rollStr} | Handling compounding explosions ${JSON.stringify(rollSet[i])}`);
|
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Handling compounding explosions ${JSON.stringify(rollSet[i])}`);
|
||||||
// If loopCount gets too high, stop trying to calculate infinity
|
// If loopCount gets too high, stop trying to calculate infinity
|
||||||
loopCountCheck(++loopCount);
|
loopCountCheck();
|
||||||
|
|
||||||
// Compound the exploding rolls, including the exploding flag and
|
// Compound the exploding rolls, including the exploding flag and
|
||||||
if (rollSet[i].exploding) {
|
if (rollSet[i].exploding) {
|
||||||
|
@ -664,15 +223,15 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): RollSet[
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we need to handle the drop/keep flags
|
// If we need to handle the drop/keep flags
|
||||||
if (dkdkCnt > 0) {
|
if (rollConf.drop.on || rollConf.keep.on || rollConf.dropHigh.on || rollConf.keepLow.on) {
|
||||||
// Count how many rerolled dice there are if the reroll flag was on
|
// Count how many rerolled dice there are if the reroll flag was on
|
||||||
let rerollCount = 0;
|
let rerollCount = 0;
|
||||||
if (rollConf.reroll.on) {
|
if (rollConf.reroll.on) {
|
||||||
for (let j = 0; j < rollSet.length; j++) {
|
for (let j = 0; j < rollSet.length; j++) {
|
||||||
// If loopCount gets too high, stop trying to calculate infinity
|
// If loopCount gets too high, stop trying to calculate infinity
|
||||||
loopCountCheck(++loopCount);
|
loopCountCheck();
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `${loopCount} Handling ${rollType} ${rollStr} | Setting originalIdx on ${JSON.stringify(rollSet[j])}`);
|
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Setting originalIdx on ${JSON.stringify(rollSet[j])}`);
|
||||||
rollSet[j].origIdx = j;
|
rollSet[j].origIdx = j;
|
||||||
|
|
||||||
if (rollSet[j].rerolled) {
|
if (rollSet[j].rerolled) {
|
||||||
|
@ -720,9 +279,9 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): RollSet[
|
||||||
let i = 0;
|
let i = 0;
|
||||||
while (dropCount > 0 && i < rollSet.length) {
|
while (dropCount > 0 && i < rollSet.length) {
|
||||||
// If loopCount gets too high, stop trying to calculate infinity
|
// If loopCount gets too high, stop trying to calculate infinity
|
||||||
loopCountCheck(++loopCount);
|
loopCountCheck();
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `${loopCount} Handling ${rollType} ${rollStr} | Dropping dice ${dropCount} ${JSON.stringify(rollSet[i])}`);
|
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Dropping dice ${dropCount} ${JSON.stringify(rollSet[i])}`);
|
||||||
// Skip all rolls that were rerolled
|
// Skip all rolls that were rerolled
|
||||||
if (!rollSet[i].rerolled) {
|
if (!rollSet[i].rerolled) {
|
||||||
rollSet[i].dropped = true;
|
rollSet[i].dropped = true;
|
||||||
|
@ -736,15 +295,15 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): RollSet[
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle OVA dropping/keeping
|
// Handle OVA dropping/keeping
|
||||||
if (rollType === 'ova') {
|
if (rollConf.type === 'ova') {
|
||||||
// Make "empty" vals array to easily sum up which die value is the greatest
|
// Make "empty" vals array to easily sum up which die value is the greatest
|
||||||
const rollVals: Array<number> = new Array(rollConf.dieSize).fill(0);
|
const rollVals: Array<number> = new Array(rollConf.dieSize).fill(0);
|
||||||
|
|
||||||
// Sum up all rolls
|
// Sum up all rolls
|
||||||
for (const ovaRoll of rollSet) {
|
for (const ovaRoll of rollSet) {
|
||||||
loopCountCheck(++loopCount);
|
loopCountCheck();
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `${loopCount} Handling ${rollType} ${rollStr} | incrementing rollVals for ${ovaRoll}`);
|
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | incrementing rollVals for ${ovaRoll}`);
|
||||||
rollVals[ovaRoll.roll - 1] += ovaRoll.roll;
|
rollVals[ovaRoll.roll - 1] += ovaRoll.roll;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -753,10 +312,10 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): RollSet[
|
||||||
|
|
||||||
// Drop all dice that are not a part of the max
|
// Drop all dice that are not a part of the max
|
||||||
for (const ovaRoll of rollSet) {
|
for (const ovaRoll of rollSet) {
|
||||||
loopCountCheck(++loopCount);
|
loopCountCheck();
|
||||||
|
|
||||||
loggingEnabled &&
|
loggingEnabled &&
|
||||||
log(LT.LOG, `${loopCount} Handling ${rollType} ${rollStr} | checking if this roll should be dropped ${ovaRoll.roll} | to keep: ${maxRoll}`);
|
log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | checking if this roll should be dropped ${ovaRoll.roll} | to keep: ${maxRoll}`);
|
||||||
if (ovaRoll.roll !== maxRoll) {
|
if (ovaRoll.roll !== maxRoll) {
|
||||||
ovaRoll.dropped = true;
|
ovaRoll.dropped = true;
|
||||||
ovaRoll.critFail = false;
|
ovaRoll.critFail = false;
|
||||||
|
|
|
@ -0,0 +1,446 @@
|
||||||
|
import { log, LogTypes as LT } from '@Log4Deno';
|
||||||
|
|
||||||
|
import { RollConf } from 'artigen/dice/dice.d.ts';
|
||||||
|
|
||||||
|
import { getLoopCount, loopCountCheck } from 'src/artigen/managers/loopManager.ts';
|
||||||
|
|
||||||
|
import { loggingEnabled } from 'artigen/utils/logFlag.ts';
|
||||||
|
|
||||||
|
const throwDoubleSepError = (sep: string): void => {
|
||||||
|
throw new Error(`DoubleSeparator_${sep}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Converts a rollStr into a machine readable rollConf
|
||||||
|
export const getRollConf = (rollStr: string): RollConf => {
|
||||||
|
// Split the roll on the die size (and the drop if its there)
|
||||||
|
const dPts = rollStr.split('d');
|
||||||
|
|
||||||
|
// Initialize the configuration to store the parsed data
|
||||||
|
const rollConf: RollConf = {
|
||||||
|
type: '',
|
||||||
|
dieCount: 0,
|
||||||
|
dieSize: 0,
|
||||||
|
dPercent: {
|
||||||
|
on: false,
|
||||||
|
sizeAdjustment: 0,
|
||||||
|
critVal: 0,
|
||||||
|
},
|
||||||
|
drop: {
|
||||||
|
on: false,
|
||||||
|
count: 0,
|
||||||
|
},
|
||||||
|
keep: {
|
||||||
|
on: false,
|
||||||
|
count: 0,
|
||||||
|
},
|
||||||
|
dropHigh: {
|
||||||
|
on: false,
|
||||||
|
count: 0,
|
||||||
|
},
|
||||||
|
keepLow: {
|
||||||
|
on: false,
|
||||||
|
count: 0,
|
||||||
|
},
|
||||||
|
reroll: {
|
||||||
|
on: false,
|
||||||
|
once: false,
|
||||||
|
nums: [],
|
||||||
|
},
|
||||||
|
critScore: {
|
||||||
|
on: false,
|
||||||
|
range: [],
|
||||||
|
},
|
||||||
|
critFail: {
|
||||||
|
on: false,
|
||||||
|
range: [],
|
||||||
|
},
|
||||||
|
exploding: {
|
||||||
|
on: false,
|
||||||
|
once: false,
|
||||||
|
compounding: false,
|
||||||
|
penetrating: false,
|
||||||
|
nums: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// If the dPts is not long enough, throw error
|
||||||
|
if (dPts.length < 2) {
|
||||||
|
throw new Error('YouNeedAD');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill out the die count, first item will either be an int or empty string, short circuit execution will take care of replacing the empty string with a 1
|
||||||
|
const rawDC = dPts.shift() || '1';
|
||||||
|
if (rawDC.includes('.')) {
|
||||||
|
throw new Error('WholeDieCountSizeOnly');
|
||||||
|
} else if (rawDC.match(/\D/)) {
|
||||||
|
throw new Error(`CannotParseDieCount_${rawDC}`);
|
||||||
|
}
|
||||||
|
const tempDC = rawDC.replace(/\D/g, '');
|
||||||
|
// Rejoin all remaining parts
|
||||||
|
let remains = dPts.join('d');
|
||||||
|
|
||||||
|
// Manual Parsing for custom roll types
|
||||||
|
let manualParse = false;
|
||||||
|
if (rawDC.endsWith('cwo')) {
|
||||||
|
// CWOD dice parsing
|
||||||
|
rollConf.type = 'cwod';
|
||||||
|
manualParse = true;
|
||||||
|
|
||||||
|
// Get CWOD parts, setting count and getting difficulty
|
||||||
|
const cwodParts = rollStr.split('cwod');
|
||||||
|
rollConf.dieCount = parseInt(cwodParts[0] || '1');
|
||||||
|
rollConf.dieSize = 10;
|
||||||
|
|
||||||
|
// Use critScore to set the difficulty
|
||||||
|
rollConf.critScore.on = true;
|
||||||
|
const difficulty = parseInt(cwodParts[1] || '10');
|
||||||
|
for (let i = difficulty; i <= rollConf.dieSize; i++) {
|
||||||
|
loopCountCheck();
|
||||||
|
|
||||||
|
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling cwod ${rollStr} | Parsing difficulty ${i}`);
|
||||||
|
rollConf.critScore.range.push(i);
|
||||||
|
}
|
||||||
|
} else if (rawDC.endsWith('ova')) {
|
||||||
|
// OVA dice parsing
|
||||||
|
rollConf.type = 'ova';
|
||||||
|
manualParse = true;
|
||||||
|
|
||||||
|
// Get OVA parts, setting count and getting difficulty
|
||||||
|
const ovaParts = rollStr.split('ovad');
|
||||||
|
const ovaPart1 = ovaParts[1] || '6';
|
||||||
|
if (ovaPart1.search(/\d+\.\d/) === 0) {
|
||||||
|
throw new Error('WholeDieCountSizeOnly');
|
||||||
|
}
|
||||||
|
rollConf.dieCount = parseInt(ovaParts[0] || '1');
|
||||||
|
rollConf.dieSize = parseInt(ovaPart1);
|
||||||
|
} else if (remains.startsWith('f')) {
|
||||||
|
// fate dice setup
|
||||||
|
rollConf.type = 'fate';
|
||||||
|
rollConf.dieCount = parseInt(tempDC);
|
||||||
|
// dieSize set to 1 as 1 is max face value, a six sided die is used internally
|
||||||
|
rollConf.dieSize = 1;
|
||||||
|
|
||||||
|
// remove F from the remains
|
||||||
|
remains = remains.slice(1);
|
||||||
|
} else {
|
||||||
|
// roll20 dice setup
|
||||||
|
rollConf.type = 'roll20';
|
||||||
|
rollConf.dieCount = parseInt(tempDC);
|
||||||
|
|
||||||
|
// Finds the end of the die size/beginning of the additional options
|
||||||
|
let afterDieIdx = dPts[0].search(/[^%\d]/);
|
||||||
|
if (afterDieIdx === -1) {
|
||||||
|
afterDieIdx = dPts[0].length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the die size out of the remains and into the rollConf
|
||||||
|
const rawDS = remains.slice(0, afterDieIdx);
|
||||||
|
remains = remains.slice(afterDieIdx);
|
||||||
|
|
||||||
|
if (rawDS.startsWith('%')) {
|
||||||
|
rollConf.dieSize = 10;
|
||||||
|
rollConf.dPercent.on = true;
|
||||||
|
const percentCount = rawDS.match(/%/g)?.length ?? 1;
|
||||||
|
rollConf.dPercent.sizeAdjustment = Math.pow(10, percentCount - 1);
|
||||||
|
rollConf.dPercent.critVal = Math.pow(10, percentCount) - rollConf.dPercent.sizeAdjustment;
|
||||||
|
} else {
|
||||||
|
rollConf.dieSize = parseInt(rawDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remains.search(/\.\d/) === 0) {
|
||||||
|
throw new Error('WholeDieCountSizeOnly');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rollConf.dieCount || !rollConf.dieSize) {
|
||||||
|
throw new Error('YouNeedAD');
|
||||||
|
}
|
||||||
|
|
||||||
|
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Parsed Die Count: ${rollConf.dieCount}`);
|
||||||
|
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Parsed Die Size: ${rollConf.dieSize}`);
|
||||||
|
|
||||||
|
// Finish parsing the roll
|
||||||
|
if (!manualParse && remains.length > 0) {
|
||||||
|
// Determine if the first item is a drop, and if it is, add the d back in
|
||||||
|
if (remains.search(/\D/) !== 0 || remains.indexOf('l') === 0 || remains.indexOf('h') === 0) {
|
||||||
|
remains = `d${remains}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop until all remaining args are parsed
|
||||||
|
while (remains.length > 0) {
|
||||||
|
loopCountCheck();
|
||||||
|
|
||||||
|
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Parsing remains ${remains}`);
|
||||||
|
// Find the next number in the remains to be able to cut out the rule name
|
||||||
|
let afterSepIdx = remains.search(/\d/);
|
||||||
|
if (afterSepIdx < 0) {
|
||||||
|
afterSepIdx = remains.length;
|
||||||
|
}
|
||||||
|
// Save the rule name to tSep and remove it from remains
|
||||||
|
const tSep = remains.slice(0, afterSepIdx);
|
||||||
|
remains = remains.slice(afterSepIdx);
|
||||||
|
// Find the next non-number in the remains to be able to cut out the count/num
|
||||||
|
let afterNumIdx = remains.search(/\D/);
|
||||||
|
if (afterNumIdx < 0) {
|
||||||
|
afterNumIdx = remains.length;
|
||||||
|
}
|
||||||
|
// Save the count/num to tNum leaving it in remains for the time being
|
||||||
|
const tNum = parseInt(remains.slice(0, afterNumIdx));
|
||||||
|
|
||||||
|
// Switch on rule name
|
||||||
|
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;
|
||||||
|
break;
|
||||||
|
case 'ro':
|
||||||
|
case 'ro=':
|
||||||
|
rollConf.reroll.once = true;
|
||||||
|
// falls through as ro/ro= functions the same as r/r= in this context
|
||||||
|
case 'r':
|
||||||
|
case 'r=':
|
||||||
|
// Configure Reroll (this can happen multiple times)
|
||||||
|
rollConf.reroll.on = true;
|
||||||
|
!rollConf.reroll.nums.includes(tNum) && rollConf.reroll.nums.push(tNum);
|
||||||
|
break;
|
||||||
|
case 'ro>':
|
||||||
|
rollConf.reroll.once = true;
|
||||||
|
// falls through as ro> functions the same as r> in this context
|
||||||
|
case 'r>':
|
||||||
|
// Configure reroll for all numbers greater than or equal to tNum (this could happen multiple times, but why)
|
||||||
|
rollConf.reroll.on = true;
|
||||||
|
for (let i = tNum; i <= rollConf.dieSize; i++) {
|
||||||
|
loopCountCheck();
|
||||||
|
|
||||||
|
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Parsing r> ${i}`);
|
||||||
|
!rollConf.reroll.nums.includes(i) && rollConf.reroll.nums.push(i);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'ro<':
|
||||||
|
rollConf.reroll.once = true;
|
||||||
|
// falls through as ro< functions the same as r< in this context
|
||||||
|
case 'r<':
|
||||||
|
// Configure reroll for all numbers less than or equal to tNum (this could happen multiple times, but why)
|
||||||
|
rollConf.reroll.on = true;
|
||||||
|
for (let i = 1; i <= tNum; i++) {
|
||||||
|
loopCountCheck();
|
||||||
|
|
||||||
|
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Parsing r< ${i}`);
|
||||||
|
!rollConf.reroll.nums.includes(i) && rollConf.reroll.nums.push(i);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'cs':
|
||||||
|
case 'cs=':
|
||||||
|
// Configure CritScore for one number (this can happen multiple times)
|
||||||
|
rollConf.critScore.on = true;
|
||||||
|
!rollConf.critScore.range.includes(tNum) && rollConf.critScore.range.push(tNum);
|
||||||
|
break;
|
||||||
|
case 'cs>':
|
||||||
|
// Configure CritScore for all numbers greater than or equal to tNum (this could happen multiple times, but why)
|
||||||
|
rollConf.critScore.on = true;
|
||||||
|
for (let i = tNum; i <= rollConf.dieSize; i++) {
|
||||||
|
loopCountCheck();
|
||||||
|
|
||||||
|
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Parsing cs> ${i}`);
|
||||||
|
!rollConf.critScore.range.includes(i) && rollConf.critScore.range.push(i);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'cs<':
|
||||||
|
// Configure CritScore for all numbers less than or equal to tNum (this could happen multiple times, but why)
|
||||||
|
rollConf.critScore.on = true;
|
||||||
|
for (let i = 0; i <= tNum; i++) {
|
||||||
|
loopCountCheck();
|
||||||
|
|
||||||
|
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Parsing cs< ${i}`);
|
||||||
|
!rollConf.critScore.range.includes(i) && rollConf.critScore.range.push(i);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'cf':
|
||||||
|
case 'cf=':
|
||||||
|
// Configure CritFail for one number (this can happen multiple times)
|
||||||
|
rollConf.critFail.on = true;
|
||||||
|
!rollConf.critFail.range.includes(tNum) && rollConf.critFail.range.push(tNum);
|
||||||
|
break;
|
||||||
|
case 'cf>':
|
||||||
|
// Configure CritFail for all numbers greater than or equal to tNum (this could happen multiple times, but why)
|
||||||
|
rollConf.critFail.on = true;
|
||||||
|
for (let i = tNum; i <= rollConf.dieSize; i++) {
|
||||||
|
loopCountCheck();
|
||||||
|
|
||||||
|
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Parsing cf> ${i}`);
|
||||||
|
!rollConf.critFail.range.includes(i) && rollConf.critFail.range.push(i);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'cf<':
|
||||||
|
// Configure CritFail for all numbers less than or equal to tNum (this could happen multiple times, but why)
|
||||||
|
rollConf.critFail.on = true;
|
||||||
|
for (let i = 0; i <= tNum; i++) {
|
||||||
|
loopCountCheck();
|
||||||
|
|
||||||
|
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Parsing cf< ${i}`);
|
||||||
|
!rollConf.critFail.range.includes(i) && rollConf.critFail.range.push(i);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '!':
|
||||||
|
case '!o':
|
||||||
|
case '!p':
|
||||||
|
case '!!':
|
||||||
|
// Configure Exploding
|
||||||
|
rollConf.exploding.on = true;
|
||||||
|
if (afterNumIdx > 0) {
|
||||||
|
// User gave a number to explode on, save it
|
||||||
|
!rollConf.exploding.nums.includes(tNum) && rollConf.exploding.nums.push(tNum);
|
||||||
|
} else {
|
||||||
|
// User did not give number, use cs
|
||||||
|
afterNumIdx = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '!=':
|
||||||
|
case '!o=':
|
||||||
|
case '!p=':
|
||||||
|
case '!!=':
|
||||||
|
// Configure Exploding (this can happen multiple times)
|
||||||
|
rollConf.exploding.on = true;
|
||||||
|
!rollConf.exploding.nums.includes(tNum) && rollConf.exploding.nums.push(tNum);
|
||||||
|
break;
|
||||||
|
case '!>':
|
||||||
|
case '!o>':
|
||||||
|
case '!p>':
|
||||||
|
case '!!>':
|
||||||
|
// Configure Exploding for all numbers greater than or equal to tNum (this could happen multiple times, but why)
|
||||||
|
rollConf.exploding.on = true;
|
||||||
|
for (let i = tNum; i <= rollConf.dieSize; i++) {
|
||||||
|
loopCountCheck();
|
||||||
|
|
||||||
|
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Parsing !> ${i}`);
|
||||||
|
!rollConf.exploding.nums.includes(i) && rollConf.exploding.nums.push(i);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '!<':
|
||||||
|
case '!o<':
|
||||||
|
case '!p<':
|
||||||
|
case '!!<':
|
||||||
|
// Configure Exploding for all numbers less than or equal to tNum (this could happen multiple times, but why)
|
||||||
|
rollConf.exploding.on = true;
|
||||||
|
for (let i = 1; i <= tNum; i++) {
|
||||||
|
loopCountCheck();
|
||||||
|
|
||||||
|
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Parsing !< ${i}`);
|
||||||
|
!rollConf.exploding.nums.includes(i) && rollConf.exploding.nums.push(i);
|
||||||
|
}
|
||||||
|
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
|
||||||
|
switch (tSep) {
|
||||||
|
case '!o':
|
||||||
|
case '!o=':
|
||||||
|
case '!o>':
|
||||||
|
case '!o<':
|
||||||
|
rollConf.exploding.once = true;
|
||||||
|
break;
|
||||||
|
case '!p':
|
||||||
|
case '!p=':
|
||||||
|
case '!p>':
|
||||||
|
case '!p<':
|
||||||
|
rollConf.exploding.penetrating = true;
|
||||||
|
break;
|
||||||
|
case '!!':
|
||||||
|
case '!!=':
|
||||||
|
case '!!>':
|
||||||
|
case '!!<':
|
||||||
|
rollConf.exploding.compounding = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally slice off everything else parsed this loop
|
||||||
|
remains = remains.slice(afterNumIdx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the parse, throwing errors for every invalid config
|
||||||
|
if (rollConf.dieCount < 0) {
|
||||||
|
throw new Error('NoZerosAllowed_base');
|
||||||
|
}
|
||||||
|
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) => {
|
||||||
|
loggingEnabled && log(LT.LOG, `Handling ${rollConf.type} ${rollStr} | Checking if drop/keep is on ${e}`);
|
||||||
|
if (e) {
|
||||||
|
dkdkCnt++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (dkdkCnt > 1) {
|
||||||
|
throw new Error('FormattingError_dk');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rollConf.drop.on && rollConf.drop.count === 0) {
|
||||||
|
throw new Error('NoZerosAllowed_drop');
|
||||||
|
}
|
||||||
|
if (rollConf.keep.on && rollConf.keep.count === 0) {
|
||||||
|
throw new Error('NoZerosAllowed_keep');
|
||||||
|
}
|
||||||
|
if (rollConf.dropHigh.on && rollConf.dropHigh.count === 0) {
|
||||||
|
throw new Error('NoZerosAllowed_dropHigh');
|
||||||
|
}
|
||||||
|
if (rollConf.keepLow.on && rollConf.keepLow.count === 0) {
|
||||||
|
throw new Error('NoZerosAllowed_keepLow');
|
||||||
|
}
|
||||||
|
if (rollConf.reroll.on && !rollConf.dPercent.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 > (rollConf.dPercent.on ? -1 : 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');
|
||||||
|
}
|
||||||
|
|
||||||
|
return rollConf;
|
||||||
|
};
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { log, LogTypes as LT } from '@Log4Deno';
|
||||||
|
|
||||||
|
import config from '~config';
|
||||||
|
|
||||||
|
import { loggingEnabled } from 'src/artigen/utils/logFlag.ts';
|
||||||
|
|
||||||
|
let loopCount = 0;
|
||||||
|
loggingEnabled && log(LT.LOG, 'Loop Manager Initialized');
|
||||||
|
|
||||||
|
// Will ensure if maxLoops is 10, 10 loops will be allowed, 11 will not.
|
||||||
|
export const loopCountCheck = (): void => {
|
||||||
|
loopCount++;
|
||||||
|
if (loopCount > config.limits.maxLoops) {
|
||||||
|
throw new Error('MaxLoopsExceeded');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getLoopCount = (): number => loopCount;
|
|
@ -7,6 +7,8 @@ import { log, LogTypes as LT } from '@Log4Deno';
|
||||||
|
|
||||||
import { MathConf, SolvedStep } from 'artigen/math/math.d.ts';
|
import { MathConf, SolvedStep } from 'artigen/math/math.d.ts';
|
||||||
|
|
||||||
|
import { loopCountCheck } from 'artigen/managers/loopManager.ts';
|
||||||
|
|
||||||
import { legalMath, legalMathOperators } from 'artigen/utils/legalMath.ts';
|
import { legalMath, legalMathOperators } from 'artigen/utils/legalMath.ts';
|
||||||
import { loggingEnabled } from 'artigen/utils/logFlag.ts';
|
import { loggingEnabled } from 'artigen/utils/logFlag.ts';
|
||||||
import { getMatchingParenIdx } from 'artigen/utils/parenBalance.ts';
|
import { getMatchingParenIdx } from 'artigen/utils/parenBalance.ts';
|
||||||
|
@ -31,6 +33,8 @@ export const mathSolver = (conf: MathConf[], wrapDetails = false): SolvedStep =>
|
||||||
|
|
||||||
// Evaluate all parenthesis
|
// Evaluate all parenthesis
|
||||||
while (conf.includes('(')) {
|
while (conf.includes('(')) {
|
||||||
|
loopCountCheck();
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Looking for (`);
|
loggingEnabled && log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Looking for (`);
|
||||||
// Get first open parenthesis
|
// Get first open parenthesis
|
||||||
let openParenIdx = conf.indexOf('(');
|
let openParenIdx = conf.indexOf('(');
|
||||||
|
@ -72,6 +76,8 @@ export const mathSolver = (conf: MathConf[], wrapDetails = false): SolvedStep =>
|
||||||
loggingEnabled && log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Evaluating ${JSON.stringify(curOps)}`);
|
loggingEnabled && log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Evaluating ${JSON.stringify(curOps)}`);
|
||||||
// Iterate thru all operators/operands in the conf
|
// Iterate thru all operators/operands in the conf
|
||||||
for (let i = 0; i < conf.length; i++) {
|
for (let i = 0; i < conf.length; i++) {
|
||||||
|
loopCountCheck();
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Evaluating ${JSON.stringify(curOps)} | Checking ${JSON.stringify(conf[i])}`);
|
loggingEnabled && log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Evaluating ${JSON.stringify(curOps)} | Checking ${JSON.stringify(conf[i])}`);
|
||||||
// Check if the current index is in the active tier of operators
|
// Check if the current index is in the active tier of operators
|
||||||
if (curOps.includes(conf[i].toString())) {
|
if (curOps.includes(conf[i].toString())) {
|
||||||
|
|
|
@ -7,6 +7,8 @@ import { MathConf, SolvedStep } from 'artigen/math/math.d.ts';
|
||||||
import { CountDetails, RollModifiers } from 'artigen/dice/dice.d.ts';
|
import { CountDetails, RollModifiers } from 'artigen/dice/dice.d.ts';
|
||||||
import { generateFormattedRoll } from 'artigen/dice/generateFormattedRoll.ts';
|
import { generateFormattedRoll } from 'artigen/dice/generateFormattedRoll.ts';
|
||||||
|
|
||||||
|
import { loopCountCheck } from 'artigen/managers/loopManager.ts';
|
||||||
|
|
||||||
import { mathSolver } from 'artigen/math/mathSolver.ts';
|
import { mathSolver } from 'artigen/math/mathSolver.ts';
|
||||||
|
|
||||||
import { cmdSplitRegex, internalWrapRegex } from 'artigen/utils/escape.ts';
|
import { cmdSplitRegex, internalWrapRegex } from 'artigen/utils/escape.ts';
|
||||||
|
@ -35,6 +37,8 @@ export const tokenizeMath = (cmd: string, modifiers: RollModifiers): [ReturnData
|
||||||
|
|
||||||
// Evaluate all rolls into stepSolve format and all numbers into floats
|
// Evaluate all rolls into stepSolve format and all numbers into floats
|
||||||
for (let i = 0; i < mathConf.length; i++) {
|
for (let i = 0; i < mathConf.length; i++) {
|
||||||
|
loopCountCheck();
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `Parsing roll ${JSON.stringify(cmd)} | Evaluating rolls into math-able items ${JSON.stringify(mathConf[i])}`);
|
loggingEnabled && log(LT.LOG, `Parsing roll ${JSON.stringify(cmd)} | Evaluating rolls into math-able items ${JSON.stringify(mathConf[i])}`);
|
||||||
|
|
||||||
const strMathConfI = mathConf[i].toString();
|
const strMathConfI = mathConf[i].toString();
|
||||||
|
|
|
@ -3,12 +3,15 @@ import { log, LogTypes as LT } from '@Log4Deno';
|
||||||
import config from '~config';
|
import config from '~config';
|
||||||
|
|
||||||
import { loggingEnabled } from 'artigen/utils/logFlag.ts';
|
import { loggingEnabled } from 'artigen/utils/logFlag.ts';
|
||||||
|
import { loopCountCheck } from 'src/artigen/managers/loopManager.ts';
|
||||||
|
|
||||||
// escapeCharacters(str, esc) returns str
|
// escapeCharacters(str, esc) returns str
|
||||||
// escapeCharacters escapes all characters listed in esc
|
// escapeCharacters escapes all characters listed in esc
|
||||||
export const escapeCharacters = (str: string, esc: string): string => {
|
export const escapeCharacters = (str: string, esc: string): string => {
|
||||||
// Loop thru each esc char one at a time
|
// Loop thru each esc char one at a time
|
||||||
for (const e of esc) {
|
for (const e of esc) {
|
||||||
|
loopCountCheck();
|
||||||
|
|
||||||
loggingEnabled && log(LT.LOG, `Escaping character ${e} | ${str}, ${esc}`);
|
loggingEnabled && log(LT.LOG, `Escaping character ${e} | ${str}, ${esc}`);
|
||||||
// Create a new regex to look for that char that needs replaced and escape it
|
// Create a new regex to look for that char that needs replaced and escape it
|
||||||
const tempRgx = new RegExp(`[${e}]`, 'g');
|
const tempRgx = new RegExp(`[${e}]`, 'g');
|
||||||
|
|
|
@ -2,6 +2,8 @@ import { log, LogTypes as LT } from '@Log4Deno';
|
||||||
|
|
||||||
import config from '~config';
|
import config from '~config';
|
||||||
|
|
||||||
|
import { loopCountCheck } from 'artigen/managers/loopManager.ts';
|
||||||
|
|
||||||
import { MathConf } from 'artigen/math/math.d.ts';
|
import { MathConf } from 'artigen/math/math.d.ts';
|
||||||
|
|
||||||
import { closeInternal, openInternal } from 'artigen/utils/escape.ts';
|
import { closeInternal, openInternal } from 'artigen/utils/escape.ts';
|
||||||
|
@ -12,6 +14,7 @@ const checkBalance = (conf: MathConf[], openStr: string, closeStr: string, error
|
||||||
|
|
||||||
// Verify there are equal numbers of opening and closing parenthesis by adding 1 for opening parens and subtracting 1 for closing parens
|
// Verify there are equal numbers of opening and closing parenthesis by adding 1 for opening parens and subtracting 1 for closing parens
|
||||||
for (let i = openIdx; i < conf.length; i++) {
|
for (let i = openIdx; i < conf.length; i++) {
|
||||||
|
loopCountCheck();
|
||||||
loggingEnabled &&
|
loggingEnabled &&
|
||||||
log(
|
log(
|
||||||
LT.LOG,
|
LT.LOG,
|
||||||
|
|
Loading…
Reference in New Issue