diff --git a/src/solver/parser.ts b/src/solver/parser.ts index 54f1abd..750bf09 100644 --- a/src/solver/parser.ts +++ b/src/solver/parser.ts @@ -15,6 +15,7 @@ import { fullSolver } from './solver.ts'; // parseRoll(fullCmd, modifiers) // parseRoll handles converting fullCmd into a computer readable format for processing, and finally executes the solving export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll => { + const operators = ['^', '*', '/', '%', '+', '-']; const returnmsg = { error: false, errorCode: '', @@ -127,14 +128,14 @@ export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll containsCrit: false, containsFail: false, }]); - } else { + } else if (!operators.includes(mathConf[i].toString())) { // If nothing else has handled it by now, try it as a roll const formattedRoll = formatRoll(mathConf[i].toString(), modifiers.maxRoll, modifiers.nominalRoll); mathConf[i] = formattedRoll.solvedStep; tempCountDetails.push(formattedRoll.countDetails); } - if (mathConf[i - 1] === '-' && (!mathConf[i - 2] || mathConf[i - 2] === '(')) { + if (mathConf[i - 1] === '-' && ((!mathConf[i - 2] && mathConf[i - 2] !== 0) || mathConf[i - 2] === '(')) { if (typeof mathConf[i] === 'number') { mathConf[i] = mathConf[i] * -1; } else { diff --git a/src/solver/rollFormatter.ts b/src/solver/rollFormatter.ts index c7bc1e8..b905db2 100644 --- a/src/solver/rollFormatter.ts +++ b/src/solver/rollFormatter.ts @@ -31,6 +31,7 @@ export const formatRoll = (rollConf: string, maximiseRoll: boolean, nominalRoll: switch (e.type) { case 'ova': case 'roll20': + case 'fate': tempTotal += e.roll; break; case 'cwod': diff --git a/src/solver/rollUtils.ts b/src/solver/rollUtils.ts index 44de63f..58d5b9d 100644 --- a/src/solver/rollUtils.ts +++ b/src/solver/rollUtils.ts @@ -19,6 +19,17 @@ export const genRoll = (size: number, maximiseRoll: boolean, nominalRoll: boolea } }; +// genFateRoll returns -1|0|1 +// genFateRoll turns a d6 into a fate die, with sides: -1, -1, 0, 0, 1, 1 +export const genFateRoll = (maximiseRoll: boolean, nominalRoll: boolean): number => { + if (nominalRoll) { + return 0; + } else { + const sides = [-1, -1, 0, 0, 1, 1]; + return sides[genRoll(6, maximiseRoll, nominalRoll) - 1]; + } +}; + // compareRolls(a, b) returns -1|0|1 // compareRolls is used to order an array of RollSets by RollSet.roll export const compareRolls = (a: RollSet, b: RollSet): number => { diff --git a/src/solver/roller.ts b/src/solver/roller.ts index 9cc28db..92207f1 100644 --- a/src/solver/roller.ts +++ b/src/solver/roller.ts @@ -6,7 +6,7 @@ import { } from '../../deps.ts'; import { RollConf, RollSet, RollType } from './solver.d.ts'; -import { compareOrigidx, compareRolls, genRoll, loggingEnabled } from './rollUtils.ts'; +import { compareOrigidx, compareRolls, genFateRoll, genRoll, loggingEnabled } from './rollUtils.ts'; // roll(rollStr, maximiseRoll, nominalRoll) returns RollSet // roll parses and executes the rollStr, if needed it will also make the roll the maximum or average @@ -24,7 +24,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea const dpts = rollStr.split('d'); // Initialize the configuration to store the parsed data - let rollType: RollType = 'roll20'; + let rollType: RollType = ''; const rollConf: RollConf = { dieCount: 0, dieSize: 0, @@ -103,8 +103,18 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea const ovaParts = rollStr.split('ovad'); rollConf.dieCount = parseInt(ovaParts[0] || '1'); rollConf.dieSize = parseInt(ovaParts[1] || '6'); + } 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/beginnning of the additional options @@ -389,7 +399,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea // Copy the template to fill out for this iteration const rolling = JSON.parse(JSON.stringify(templateRoll)); // If maximiseRoll 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 = genRoll(rollConf.dieSize, maximiseRoll, nominalRoll); + rolling.roll = rollType === 'fate' ? genFateRoll(maximiseRoll, nominalRoll) : genRoll(rollConf.dieSize, maximiseRoll, nominalRoll); // Set origidx of roll rolling.origidx = i; @@ -403,7 +413,11 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea if (rollConf.critFail.on && rollConf.critFail.range.indexOf(rolling.roll) >= 0) { rolling.critFail = true; } else if (!rollConf.critFail.on) { - rolling.critFail = rolling.roll === 1; + if (rollType === 'fate') { + rolling.critFail = rolling.roll === -1; + } else { + rolling.critFail = rolling.roll === 1; + } } // Push the newly created roll and loop again diff --git a/src/solver/solver.d.ts b/src/solver/solver.d.ts index af67e86..c5804d3 100644 --- a/src/solver/solver.d.ts +++ b/src/solver/solver.d.ts @@ -1,6 +1,6 @@ // solver.ts custom types -export type RollType = '' | 'roll20' | 'cwod' | 'ova'; +export type RollType = '' | 'roll20' | 'fate' | 'cwod' | 'ova'; // RollSet is used to preserve all information about a calculated roll export type RollSet = { diff --git a/src/solver/solver.ts b/src/solver/solver.ts index 4127fae..45f5d23 100644 --- a/src/solver/solver.ts +++ b/src/solver/solver.ts @@ -111,7 +111,7 @@ export const fullSolver = (conf: (string | number | SolvedStep)[], wrapDetails: subStepSolve.containsFail = operand1.containsFail; } else { // else parse it as a number and add it to the subStep details - if (operand1) { + if (operand1 || operand1 == 0) { oper1 = parseFloat(operand1.toString()); subStepSolve.details = `${oper1.toString()}\\${conf[i]}`; }