Add Min flag and add -max as option for max flag

This commit is contained in:
Ean Milligan 2025-04-27 16:58:51 -04:00
parent 5bef3878cc
commit 4207021fa9
8 changed files with 78 additions and 46 deletions

View File

@ -5,7 +5,7 @@ meta {
} }
get { get {
url: http://localhost:8166/api/roll?user=[discord-user-id]&channel=[discord-channel-id]&rollstr=[artificer-roll-cmd]&documentation=All items below are optional. Flags do not need values.&nd=[no-details-flag]&snd=[super-no-details-flag]&s=[spoiler-results-flag]&m=[max-roll-flag, cannot be used with n flag]&n=[nominal-roll-flag, cannot be used with m flag]&gms=[csv-of-discord-user-ids-to-be-dmed-results]&o=[order-rolls, must be a or d]&c=[count-flag] url: http://localhost:8166/api/roll?user=[discord-user-id]&channel=[discord-channel-id]&rollstr=[artificer-roll-cmd]&documentation=All items below are optional. Flags do not need values.&nd=[no-details-flag]&snd=[super-no-details-flag]&s=[spoiler-results-flag]&m or max=[max-roll-flag, cannot be used with n flag]&min=[[min-roll-flag, cannot be used with n or max&n=[nominal-roll-flag, cannot be used with max or min flag]&gms=[csv-of-discord-user-ids-to-be-dmed-results]&o=[order-rolls, must be a or d]&c=[count-flag]
body: none body: none
auth: inherit auth: inherit
} }
@ -18,8 +18,9 @@ params:query {
nd: [no-details-flag] nd: [no-details-flag]
snd: [super-no-details-flag] snd: [super-no-details-flag]
s: [spoiler-results-flag] s: [spoiler-results-flag]
m: [max-roll-flag, cannot be used with n flag] m or max: [max-roll-flag, cannot be used with n flag]
n: [nominal-roll-flag, cannot be used with m flag] min: [[min-roll-flag, cannot be used with n or max
n: [nominal-roll-flag, cannot be used with max or min flag]
gms: [csv-of-discord-user-ids-to-be-dmed-results] gms: [csv-of-discord-user-ids-to-be-dmed-results]
o: [order-rolls, must be a or d] o: [order-rolls, must be a or d]
c: [count-flag] c: [count-flag]

View File

@ -20,6 +20,7 @@ export const getModifiers = (m: DiscordenoMessage, args: string[], command: stri
superNoDetails: false, superNoDetails: false,
spoiler: '', spoiler: '',
maxRoll: false, maxRoll: false,
minRoll: false,
nominalRoll: false, nominalRoll: false,
gmRoll: false, gmRoll: false,
gms: [], gms: [],
@ -46,9 +47,13 @@ export const getModifiers = (m: DiscordenoMessage, args: string[], command: stri
case '-s': case '-s':
modifiers.spoiler = '||'; modifiers.spoiler = '||';
break; break;
case '-max':
case '-m': case '-m':
modifiers.maxRoll = true; modifiers.maxRoll = true;
break; break;
case '-min':
modifiers.minRoll = true;
break;
case '-n': case '-n':
modifiers.nominalRoll = true; modifiers.nominalRoll = true;
break; break;
@ -106,9 +111,11 @@ export const getModifiers = (m: DiscordenoMessage, args: string[], command: stri
} }
} }
// maxRoll and nominalRoll cannot both be on, throw an error // maxRoll, minRoll, and nominalRoll cannot be on at same time, throw an error
if (modifiers.maxRoll && modifiers.nominalRoll) { if ([modifiers.maxRoll, modifiers.minRoll, modifiers.nominalRoll].filter((b) => b).length > 1) {
m.edit(generateRollError(errorType, 'Cannot maximize and nominize the roll at the same time')).catch((e) => utils.commonLoggers.messageEditError('getModifiers.ts:106', m, e)); m.edit(generateRollError(errorType, 'Can only use one of the following at a time:\n`maximize`, `minimize`, `nominal`')).catch((e) =>
utils.commonLoggers.messageEditError('getModifiers.ts:106', m, e)
);
if (DEVMODE && config.logRolls) { if (DEVMODE && config.logRolls) {
// If enabled, log rolls so we can verify the bots math // If enabled, log rolls so we can verify the bots math

View File

@ -84,7 +84,8 @@ export const apiRoll = async (query: Map<string, string>, apiUserid: bigint): Pr
noDetails: query.has('nd'), noDetails: query.has('nd'),
superNoDetails: query.has('snd'), superNoDetails: query.has('snd'),
spoiler: query.has('s') ? '||' : '', spoiler: query.has('s') ? '||' : '',
maxRoll: query.has('m'), maxRoll: query.has('m') || query.has('max'),
minRoll: query.has('min'),
nominalRoll: query.has('n'), nominalRoll: query.has('n'),
gmRoll: query.has('gms'), gmRoll: query.has('gms'),
gms: query.has('gms') ? (query.get('gms') || '').split(',') : [], gms: query.has('gms') ? (query.get('gms') || '').split(',') : [],

1
src/mod.d.ts vendored
View File

@ -16,6 +16,7 @@ export type RollModifiers = {
superNoDetails: boolean; superNoDetails: boolean;
spoiler: string; spoiler: string;
maxRoll: boolean; maxRoll: boolean;
minRoll: boolean;
nominalRoll: boolean; nominalRoll: boolean;
gmRoll: boolean; gmRoll: boolean;
gms: string[]; gms: string[];

View File

@ -8,7 +8,7 @@ import config from '../../config.ts';
import { RollModifiers } from '../mod.d.ts'; import { RollModifiers } from '../mod.d.ts';
import { CountDetails, ReturnData, SolvedRoll, SolvedStep } from './solver.d.ts'; import { CountDetails, ReturnData, SolvedRoll, SolvedStep } from './solver.d.ts';
import { compareTotalRolls, escapeCharacters, loggingEnabled } from './rollUtils.ts'; import { compareTotalRolls, compareTotalRollsReverse, escapeCharacters, loggingEnabled } from './rollUtils.ts';
import { formatRoll } from './rollFormatter.ts'; import { formatRoll } from './rollFormatter.ts';
import { fullSolver } from './solver.ts'; import { fullSolver } from './solver.ts';
@ -151,7 +151,7 @@ export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll
i += 2; i += 2;
} else if (!operators.includes(mathConf[i].toString())) { } else if (!operators.includes(mathConf[i].toString())) {
// If nothing else has handled it by now, try it as a roll // If nothing else has handled it by now, try it as a roll
const formattedRoll = formatRoll(mathConf[i].toString(), modifiers.maxRoll, modifiers.nominalRoll); const formattedRoll = formatRoll(mathConf[i].toString(), modifiers);
mathConf[i] = formattedRoll.solvedStep; mathConf[i] = formattedRoll.solvedStep;
tempCountDetails.push(formattedRoll.countDetails); tempCountDetails.push(formattedRoll.countDetails);
} }
@ -197,13 +197,14 @@ export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll
let line2 = ''; let line2 = '';
let line3 = ''; let line3 = '';
// If maximizeRoll or nominalRoll are on, mark the output as such, else use default formatting // If a theoretical roll is requested, mark the output as such, else use default formatting
if (modifiers.maxRoll) { if (modifiers.maxRoll || modifiers.minRoll || modifiers.nominalRoll) {
line1 = ` requested the theoretical maximum of:\n\`${config.prefix}${fullCmd}\``; const theoreticalTexts = ['Maximum', 'Minimum', 'Nominal'];
line2 = 'Theoretical Maximum Results: '; const theoreticalBools = [modifiers.maxRoll, modifiers.minRoll, modifiers.nominalRoll];
} else if (modifiers.nominalRoll) { const theoreticalText = theoreticalTexts[theoreticalBools.indexOf(true)];
line1 = ` requested the theoretical nominal of:\n\`${config.prefix}${fullCmd}\``;
line2 = 'Theoretical Nominal Results: '; line1 = ` requested the Theoretical ${theoreticalText} of:\n\`${config.prefix}${fullCmd}\``;
line2 = `Theoretical ${theoreticalText} Results: `;
} else if (modifiers.order === 'a') { } else if (modifiers.order === 'a') {
line1 = ` requested the following rolls to be ordered from least to greatest:\n\`${config.prefix}${fullCmd}\``; line1 = ` requested the following rolls to be ordered from least to greatest:\n\`${config.prefix}${fullCmd}\``;
line2 = 'Results: '; line2 = 'Results: ';
@ -211,8 +212,7 @@ export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll
} else if (modifiers.order === 'd') { } else if (modifiers.order === 'd') {
line1 = ` requested the following rolls to be ordered from greatest to least:\n\`${config.prefix}${fullCmd}\``; line1 = ` requested the following rolls to be ordered from greatest to least:\n\`${config.prefix}${fullCmd}\``;
line2 = 'Results: '; line2 = 'Results: ';
tempReturnData.sort(compareTotalRolls); tempReturnData.sort(compareTotalRollsReverse);
tempReturnData.reverse();
} else { } else {
line1 = ` rolled:\n\`${config.prefix}${fullCmd}\``; line1 = ` rolled:\n\`${config.prefix}${fullCmd}\``;
line2 = 'Results: '; line2 = 'Results: ';

View File

@ -8,17 +8,18 @@ import { roll } from './roller.ts';
import { rollCounter } from './counter.ts'; import { rollCounter } from './counter.ts';
import { RollFormat } from './solver.d.ts'; import { RollFormat } from './solver.d.ts';
import { loggingEnabled } from './rollUtils.ts'; import { loggingEnabled } from './rollUtils.ts';
import { RollModifiers } from '../mod.d.ts';
// formatRoll(rollConf, maximizeRoll, nominalRoll) returns one SolvedStep // formatRoll(rollConf, modifiers) returns one SolvedStep
// formatRoll handles creating and formatting the completed rolls into the SolvedStep format // formatRoll handles creating and formatting the completed rolls into the SolvedStep format
export const formatRoll = (rollConf: string, maximizeRoll: boolean, nominalRoll: boolean): RollFormat => { export const formatRoll = (rollConf: string, modifiers: RollModifiers): RollFormat => {
let tempTotal = 0; let tempTotal = 0;
let tempDetails = '['; let tempDetails = '[';
let tempCrit = false; let tempCrit = false;
let tempFail = false; let tempFail = false;
// Generate the roll, passing flags thru // Generate the roll, passing flags thru
const tempRollSet = roll(rollConf, maximizeRoll, nominalRoll); const tempRollSet = roll(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) => {

View File

@ -3,6 +3,7 @@ import {
// Log4Deno deps // Log4Deno deps
LT, LT,
} from '../../deps.ts'; } from '../../deps.ts';
import { RollModifiers } from '../mod.d.ts';
import { ReturnData, RollSet } from './solver.d.ts'; import { ReturnData, RollSet } from './solver.d.ts';
@ -10,23 +11,25 @@ export const loggingEnabled = false;
// genRoll(size) returns number // genRoll(size) returns number
// genRoll rolls a die of size size and returns the result // genRoll rolls a die of size size and returns the result
export const genRoll = (size: number, maximizeRoll: boolean, nominalRoll: boolean): number => { export const genRoll = (size: number, modifiers: RollModifiers): number => {
if (maximizeRoll) { if (modifiers.maxRoll) {
return size; return size;
} else if (modifiers.minRoll) {
return 1;
} else { } else {
// Math.random * size will return a decimal number between 0 and size (excluding size), so add 1 and floor the result to not get 0 as a result // Math.random * size will return a decimal number between 0 and size (excluding size), so add 1 and floor the result to not get 0 as a result
return nominalRoll ? size / 2 + 0.5 : Math.floor(Math.random() * size + 1); return modifiers.nominalRoll ? size / 2 + 0.5 : Math.floor(Math.random() * size + 1);
} }
}; };
// genFateRoll returns -1|0|1 // genFateRoll returns -1|0|1
// genFateRoll turns a d6 into a fate die, with sides: -1, -1, 0, 0, 1, 1 // genFateRoll turns a d6 into a fate die, with sides: -1, -1, 0, 0, 1, 1
export const genFateRoll = (maximizeRoll: boolean, nominalRoll: boolean): number => { export const genFateRoll = (modifiers: RollModifiers): number => {
if (nominalRoll) { if (modifiers.nominalRoll) {
return 0; return 0;
} else { } else {
const sides = [-1, -1, 0, 0, 1, 1]; const sides = [-1, -1, 0, 0, 1, 1];
return sides[genRoll(6, maximizeRoll, nominalRoll) - 1]; return sides[genRoll(6, modifiers) - 1];
} }
}; };
@ -42,18 +45,24 @@ export const compareRolls = (a: RollSet, b: RollSet): number => {
return 0; return 0;
}; };
// compareTotalRolls(a, b) returns -1|0|1 const internalCompareTotalRolls = (a: ReturnData, b: ReturnData, dir: 1 | -1): number => {
// compareTotalRolls is used to order an array of RollSets by RollSet.roll
export const compareTotalRolls = (a: ReturnData, b: ReturnData): number => {
if (a.rollTotal < b.rollTotal) { if (a.rollTotal < b.rollTotal) {
return -1; return -1 * dir;
} }
if (a.rollTotal > b.rollTotal) { if (a.rollTotal > b.rollTotal) {
return 1; return 1 * dir;
} }
return 0; return 0;
}; };
// compareTotalRolls(a, b) returns -1|0|1
// compareTotalRolls is used to order an array of RollSets by RollSet.roll
export const compareTotalRolls = (a: ReturnData, b: ReturnData): number => internalCompareTotalRolls(a, b, 1);
// compareTotalRollsReverse(a, b) returns 1|0|-1
// compareTotalRollsReverse is used to order an array of RollSets by RollSet.roll reversed
export const compareTotalRollsReverse = (a: ReturnData, b: ReturnData): number => internalCompareTotalRolls(a, b, -1);
// compareRolls(a, b) returns -1|0|1 // compareRolls(a, b) returns -1|0|1
// compareRolls is used to order an array of RollSets by RollSet.origIdx // compareRolls is used to order an array of RollSets by RollSet.origIdx
export const compareOrigIdx = (a: RollSet, b: RollSet): number => { export const compareOrigIdx = (a: RollSet, b: RollSet): number => {

View File

@ -7,6 +7,7 @@ import {
import { RollConf, RollSet, RollType } from './solver.d.ts'; import { RollConf, RollSet, RollType } from './solver.d.ts';
import { compareOrigIdx, compareRolls, genFateRoll, genRoll, loggingEnabled } from './rollUtils.ts'; import { compareOrigIdx, compareRolls, genFateRoll, genRoll, loggingEnabled } from './rollUtils.ts';
import { RollModifiers } from '../mod.d.ts';
// Call with loopCountCheck(++loopCount); // Call with loopCountCheck(++loopCount);
// Will ensure if maxLoops is 10, 10 loops will be allowed, 11 will not. // Will ensure if maxLoops is 10, 10 loops will be allowed, 11 will not.
@ -20,9 +21,9 @@ const throwDoubleSepError = (sep: string): void => {
throw new Error(`DoubleSeparator_${sep}`); throw new Error(`DoubleSeparator_${sep}`);
}; };
// roll(rollStr, maximizeRoll, nominalRoll) returns RollSet // roll(rollStr, modifiers) returns RollSet
// roll parses and executes the rollStr, if needed it will also make the roll the maximum or average // roll parses and executes the rollStr
export const roll = (rollStr: string, maximizeRoll: boolean, nominalRoll: boolean): RollSet[] => { export const roll = (rollStr: string, modifiers: RollModifiers): RollSet[] => {
/* Roll Capabilities /* Roll Capabilities
* Deciphers and rolls a single dice roll set * Deciphers and rolls a single dice roll set
* *
@ -494,7 +495,7 @@ export const roll = (rollStr: string, maximizeRoll: boolean, nominalRoll: boolea
// 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(maximizeRoll, nominalRoll) : genRoll(rollConf.dieSize, maximizeRoll, nominalRoll); rolling.roll = rollType === 'fate' ? genFateRoll(modifiers) : genRoll(rollConf.dieSize, modifiers);
// Set origIdx of roll // Set origIdx of roll
rolling.origIdx = i; rolling.origIdx = i;
@ -534,22 +535,33 @@ export const roll = (rollStr: string, maximizeRoll: boolean, nominalRoll: boolea
// Copy the template to fill out for this iteration // Copy the template to fill out for this iteration
const newReroll = getTemplateRoll(); const newReroll = getTemplateRoll();
if (maximizeRoll) { 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
if (!minMaxOverride) { mmMaxLoop: for (let m = rollConf.dieSize - 1; m > 0; m--) {
mmLoop: for (let m = rollConf.dieSize - 1; m > 0; m--) {
loopCountCheck(++loopCount); loopCountCheck(++loopCount);
if (!rollConf.reroll.nums.includes(m)) { if (!rollConf.reroll.nums.includes(m)) {
minMaxOverride = m; minMaxOverride = m;
break mmLoop; break mmMaxLoop;
}
}
} 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
mmMinLoop: for (let m = 2; m <= rollConf.dieSize; m++) {
loopCountCheck(++loopCount);
if (!rollConf.reroll.nums.includes(m)) {
minMaxOverride = m;
break mmMinLoop;
} }
} }
} }
if (modifiers.maxRoll || modifiers.minRoll) {
newReroll.roll = minMaxOverride; newReroll.roll = minMaxOverride;
} else { } else {
// If nominalRoll is on, set the roll to the average roll of dieSize, otherwise generate a new random roll // If nominalRoll is on, set the roll to the average roll of dieSize, otherwise generate a new random roll
newReroll.roll = genRoll(rollConf.dieSize, maximizeRoll, nominalRoll); newReroll.roll = genRoll(rollConf.dieSize, modifiers);
} }
// If critScore arg is on, check if the roll should be a crit, if its off, check if the roll matches the die size // If critScore arg is on, check if the roll should be a crit, if its off, check if the roll matches the die size
@ -579,7 +591,7 @@ export const roll = (rollStr: string, maximizeRoll: boolean, nominalRoll: boolea
// Copy the template to fill out for this iteration // Copy the template to fill out for this iteration
const newExplodingRoll = getTemplateRoll(); const newExplodingRoll = 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
newExplodingRoll.roll = genRoll(rollConf.dieSize, maximizeRoll, nominalRoll); newExplodingRoll.roll = genRoll(rollConf.dieSize, modifiers);
// Always mark this roll as exploding // Always mark this roll as exploding
newExplodingRoll.exploding = true; newExplodingRoll.exploding = true;