Implement Custom Dice Shapes
This commit is contained in:
		
							parent
							
								
									b2d4d0c0a4
								
							
						
					
					
						commit
						30f0314695
					
				| 
						 | 
					@ -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]&hr=[hide-raw-roll-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, sn, or max]&n=[nominal-roll-flag, cannot be used with sn, max or min flag]&sn=[simulated-nominal-flag, can pass number with it, cannot be used with max, min, n. or cc]&gms=[csv-of-discord-user-ids-to-be-dmed-results]&o=[order-rolls, must be a or d]&c=[count-flag]&cc=[confirm-crit-flag, cannot be used with sn]&rd=[roll-dist-flag]&nv-or-vn=[number-variables-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]&hr=[hide-raw-roll-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, sn, or max]&n=[nominal-roll-flag, cannot be used with sn, max or min flag]&sn=[simulated-nominal-flag, can pass number with it, cannot be used with max, min, n. or cc]&gms=[csv-of-discord-user-ids-to-be-dmed-results]&o=[order-rolls, must be a or d]&c=[count-flag]&cc=[confirm-crit-flag, cannot be used with sn]&rd=[roll-dist-flag]&nv-or-vn=[number-variables-flag]&cd=[custom-dice, format value as name:[side1,side2,...,sideN], use ; to separate multiple custom dice]
 | 
				
			||||||
  body: none
 | 
					  body: none
 | 
				
			||||||
  auth: inherit
 | 
					  auth: inherit
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -29,4 +29,5 @@ params:query {
 | 
				
			||||||
  cc: [confirm-crit-flag, cannot be used with sn]
 | 
					  cc: [confirm-crit-flag, cannot be used with sn]
 | 
				
			||||||
  rd: [roll-dist-flag]
 | 
					  rd: [roll-dist-flag]
 | 
				
			||||||
  nv-or-vn: [number-variables-flag]
 | 
					  nv-or-vn: [number-variables-flag]
 | 
				
			||||||
 | 
					  cd: [custom-dice, format value as name:[side1,side2,...,sideN], use ; to separate multiple custom dice]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -149,6 +149,7 @@ The Artificer comes with a few supplemental commands to the main rolling command
 | 
				
			||||||
    * `-rd` - Roll Distribution - Shows a raw roll distribution of all dice in roll
 | 
					    * `-rd` - Roll Distribution - Shows a raw roll distribution of all dice in roll
 | 
				
			||||||
    * `-hr` - Hide Raw - Hide the raw input, showing only the results/details of the roll
 | 
					    * `-hr` - Hide Raw - Hide the raw input, showing only the results/details of the roll
 | 
				
			||||||
    * `-nv` or `-vn` - Number Variables - Adds `xN` before each roll command in the details section for debug reasons
 | 
					    * `-nv` or `-vn` - Number Variables - Adds `xN` before each roll command in the details section for debug reasons
 | 
				
			||||||
 | 
					    * `-cd` - Custom Dice shapes - Allows a list of `name:[side1,side2,...,sideN]` separated by `;` to be passed to create special shaped dice
 | 
				
			||||||
  * The results have some formatting applied on them to provide details on what happened during this roll.
 | 
					  * The results have some formatting applied on them to provide details on what happened during this roll.
 | 
				
			||||||
    * Critical successes will be **bolded**
 | 
					    * Critical successes will be **bolded**
 | 
				
			||||||
    * Critical fails will be <ins>underlined</ins>
 | 
					    * Critical fails will be <ins>underlined</ins>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
				
			||||||
type RollType = '' | 'roll20' | 'fate' | 'cwod' | 'ova';
 | 
					type RollType = '' | 'custom' | '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 {
 | 
				
			||||||
| 
						 | 
					@ -35,6 +35,8 @@ export interface CountDetails {
 | 
				
			||||||
// use rollDistKey to generate the key
 | 
					// use rollDistKey to generate the key
 | 
				
			||||||
export type RollDistributionMap = Map<string, number[]>;
 | 
					export type RollDistributionMap = Map<string, number[]>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type CustomDiceShapes = Map<string, number[]>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RollFormat is the return structure for the rollFormatter
 | 
					// RollFormat is the return structure for the rollFormatter
 | 
				
			||||||
export interface FormattedRoll {
 | 
					export interface FormattedRoll {
 | 
				
			||||||
  solvedStep: SolvedStep;
 | 
					  solvedStep: SolvedStep;
 | 
				
			||||||
| 
						 | 
					@ -60,6 +62,7 @@ export interface RollModifiers {
 | 
				
			||||||
  confirmCrit: boolean;
 | 
					  confirmCrit: boolean;
 | 
				
			||||||
  rollDist: boolean;
 | 
					  rollDist: boolean;
 | 
				
			||||||
  numberVariables: boolean;
 | 
					  numberVariables: boolean;
 | 
				
			||||||
 | 
					  customDiceShapes: CustomDiceShapes;
 | 
				
			||||||
  apiWarn: string;
 | 
					  apiWarn: string;
 | 
				
			||||||
  valid: boolean;
 | 
					  valid: boolean;
 | 
				
			||||||
  error: Error;
 | 
					  error: Error;
 | 
				
			||||||
| 
						 | 
					@ -115,6 +118,7 @@ export interface GroupConf extends BaseConf {
 | 
				
			||||||
// RollConf carries the machine readable roll configuration the user specified
 | 
					// RollConf carries the machine readable roll configuration the user specified
 | 
				
			||||||
export interface RollConf extends BaseConf {
 | 
					export interface RollConf extends BaseConf {
 | 
				
			||||||
  type: RollType;
 | 
					  type: RollType;
 | 
				
			||||||
 | 
					  customType: string | null;
 | 
				
			||||||
  dieCount: number;
 | 
					  dieCount: number;
 | 
				
			||||||
  dieSize: number;
 | 
					  dieSize: number;
 | 
				
			||||||
  dPercent: DPercentConf;
 | 
					  dPercent: DPercentConf;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
import { log, LogTypes as LT } from '@Log4Deno';
 | 
					import { log, LogTypes as LT } from '@Log4Deno';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { ExecutedRoll, RollModifiers, RollSet, SumOverride } from 'artigen/dice/dice.d.ts';
 | 
					import { ExecutedRoll, RollModifiers, RollSet, SumOverride } from 'artigen/dice/dice.d.ts';
 | 
				
			||||||
import { genFateRoll, genRoll } from 'artigen/dice/randomRoll.ts';
 | 
					import { generateRoll } from 'artigen/dice/randomRoll.ts';
 | 
				
			||||||
import { getRollConf } from 'artigen/dice/getRollConf.ts';
 | 
					import { getRollConf } from 'artigen/dice/getRollConf.ts';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { loggingEnabled } from 'artigen/utils/logFlag.ts';
 | 
					import { loggingEnabled } from 'artigen/utils/logFlag.ts';
 | 
				
			||||||
| 
						 | 
					@ -24,7 +24,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): Executed
 | 
				
			||||||
  rollStr = rollStr.toLowerCase();
 | 
					  rollStr = rollStr.toLowerCase();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Turn the rollStr into a machine readable rollConf
 | 
					  // Turn the rollStr into a machine readable rollConf
 | 
				
			||||||
  const rollConf = getRollConf(rollStr);
 | 
					  const rollConf = getRollConf(rollStr, modifiers.customDiceShapes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Roll the roll
 | 
					  // Roll the roll
 | 
				
			||||||
  const rollSet: RollSet[] = [];
 | 
					  const rollSet: RollSet[] = [];
 | 
				
			||||||
| 
						 | 
					@ -85,12 +85,12 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): Executed
 | 
				
			||||||
    // 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 = rollConf.type === 'fate' ? genFateRoll(modifiers) : genRoll(rollConf.dieSize, modifiers, rollConf.dPercent);
 | 
					    rolling.roll = generateRoll(rollConf, modifiers);
 | 
				
			||||||
    rolling.size = rollConf.dieSize;
 | 
					    rolling.size = rollConf.dieSize;
 | 
				
			||||||
    // Set origIdx of roll
 | 
					    // Set origIdx of roll
 | 
				
			||||||
    rolling.origIdx = i;
 | 
					    rolling.origIdx = i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    flagRoll(rollConf, rolling);
 | 
					    flagRoll(rollConf, rolling, modifiers.customDiceShapes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    loggingEnabled && log(LT.LOG, `${getLoopCount()} Roll done ${JSON.stringify(rolling)}`);
 | 
					    loggingEnabled && log(LT.LOG, `${getLoopCount()} Roll done ${JSON.stringify(rolling)}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -140,10 +140,10 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): Executed
 | 
				
			||||||
          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 = rollConf.type === 'fate' ? genFateRoll(modifiers) : genRoll(rollConf.dieSize, modifiers, rollConf.dPercent);
 | 
					          newReroll.roll = generateRoll(rollConf, modifiers);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        flagRoll(rollConf, newReroll);
 | 
					        flagRoll(rollConf, newReroll, modifiers.customDiceShapes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        loggingEnabled && log(LT.LOG, `${getLoopCount()} Roll done ${JSON.stringify(newReroll)}`);
 | 
					        loggingEnabled && log(LT.LOG, `${getLoopCount()} Roll done ${JSON.stringify(newReroll)}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -161,12 +161,12 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): Executed
 | 
				
			||||||
        // 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 = rollConf.type === 'fate' ? genFateRoll(modifiers) : genRoll(rollConf.dieSize, modifiers, rollConf.dPercent);
 | 
					        newExplodingRoll.roll = generateRoll(rollConf, modifiers);
 | 
				
			||||||
        newExplodingRoll.size = rollConf.dieSize;
 | 
					        newExplodingRoll.size = rollConf.dieSize;
 | 
				
			||||||
        // Always mark this roll as exploding
 | 
					        // Always mark this roll as exploding
 | 
				
			||||||
        newExplodingRoll.exploding = true;
 | 
					        newExplodingRoll.exploding = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        flagRoll(rollConf, newExplodingRoll);
 | 
					        flagRoll(rollConf, newExplodingRoll, modifiers.customDiceShapes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        loggingEnabled && log(LT.LOG, `${getLoopCount()} Roll done ${JSON.stringify(newExplodingRoll)}`);
 | 
					        loggingEnabled && log(LT.LOG, `${getLoopCount()} Roll done ${JSON.stringify(newExplodingRoll)}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,16 +28,7 @@ export const formatRoll = (executedRoll: ExecutedRoll, modifiers: RollModifiers)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!e.dropped && !e.rerolled) {
 | 
					    if (!e.dropped && !e.rerolled) {
 | 
				
			||||||
      // If the roll was not dropped or rerolled, add it to the stepTotal and flag the critHit/critFail
 | 
					      // If the roll was not dropped or rerolled, add it to the stepTotal and flag the critHit/critFail
 | 
				
			||||||
      switch (e.type) {
 | 
					 | 
				
			||||||
        case 'ova':
 | 
					 | 
				
			||||||
        case 'roll20':
 | 
					 | 
				
			||||||
        case 'fate':
 | 
					 | 
				
			||||||
      tempTotal += e.roll;
 | 
					      tempTotal += e.roll;
 | 
				
			||||||
          break;
 | 
					 | 
				
			||||||
        case 'cwod':
 | 
					 | 
				
			||||||
          tempTotal += e.success ? 1 : 0;
 | 
					 | 
				
			||||||
          break;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      if (e.critHit) {
 | 
					      if (e.critHit) {
 | 
				
			||||||
        tempCrit = true;
 | 
					        tempCrit = true;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,10 @@
 | 
				
			||||||
import { log, LogTypes as LT } from '@Log4Deno';
 | 
					import { log, LogTypes as LT } from '@Log4Deno';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { RollModifiers } from 'artigen/dice/dice.d.ts';
 | 
					 | 
				
			||||||
import config from '~config';
 | 
					import config from '~config';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { RollModifiers } from 'artigen/dice/dice.d.ts';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const reservedCharacters = ['d', '%', '^', '*', '(', ')', '{', '}', '/', '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
 | 
				
			||||||
export const Modifiers = Object.freeze({
 | 
					export const Modifiers = Object.freeze({
 | 
				
			||||||
  Count: '-c',
 | 
					  Count: '-c',
 | 
				
			||||||
  NoDetails: '-nd',
 | 
					  NoDetails: '-nd',
 | 
				
			||||||
| 
						 | 
					@ -21,6 +23,7 @@ export const Modifiers = Object.freeze({
 | 
				
			||||||
  RollDistribution: '-rd',
 | 
					  RollDistribution: '-rd',
 | 
				
			||||||
  NumberVariables: '-nv',
 | 
					  NumberVariables: '-nv',
 | 
				
			||||||
  VariablesNumber: '-vn',
 | 
					  VariablesNumber: '-vn',
 | 
				
			||||||
 | 
					  CustomDiceShapes: '-cd',
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const getModifiers = (args: string[]): [RollModifiers, string[]] => {
 | 
					export const getModifiers = (args: string[]): [RollModifiers, string[]] => {
 | 
				
			||||||
| 
						 | 
					@ -41,6 +44,7 @@ export const getModifiers = (args: string[]): [RollModifiers, string[]] => {
 | 
				
			||||||
    confirmCrit: false,
 | 
					    confirmCrit: false,
 | 
				
			||||||
    rollDist: false,
 | 
					    rollDist: false,
 | 
				
			||||||
    numberVariables: false,
 | 
					    numberVariables: false,
 | 
				
			||||||
 | 
					    customDiceShapes: new Map<string, number[]>(),
 | 
				
			||||||
    apiWarn: '',
 | 
					    apiWarn: '',
 | 
				
			||||||
    valid: true,
 | 
					    valid: true,
 | 
				
			||||||
    error: new Error(),
 | 
					    error: new Error(),
 | 
				
			||||||
| 
						 | 
					@ -48,7 +52,7 @@ export const getModifiers = (args: string[]): [RollModifiers, string[]] => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Check if any of the args are command flags and pull those out into the modifiers object
 | 
					  // Check if any of the args are command flags and pull those out into the modifiers object
 | 
				
			||||||
  for (let i = 0; i < args.length; i++) {
 | 
					  for (let i = 0; i < args.length; i++) {
 | 
				
			||||||
    log(LT.LOG, `Checking ${args.join(' ')} for command modifiers ${i}`);
 | 
					    log(LT.LOG, `Checking ${args.join(' ')} for command modifiers ${i} | ${args[i]}`);
 | 
				
			||||||
    let defaultCase = false;
 | 
					    let defaultCase = false;
 | 
				
			||||||
    switch (args[i].toLowerCase()) {
 | 
					    switch (args[i].toLowerCase()) {
 | 
				
			||||||
      case Modifiers.Count:
 | 
					      case Modifiers.Count:
 | 
				
			||||||
| 
						 | 
					@ -103,6 +107,7 @@ export const getModifiers = (args: string[]): [RollModifiers, string[]] => {
 | 
				
			||||||
          // If -gm is on and none were found, throw an error
 | 
					          // If -gm is on and none were found, throw an error
 | 
				
			||||||
          modifiers.error.name = 'NoGMsFound';
 | 
					          modifiers.error.name = 'NoGMsFound';
 | 
				
			||||||
          modifiers.error.message = 'Must specify at least one GM by @mentioning them';
 | 
					          modifiers.error.message = 'Must specify at least one GM by @mentioning them';
 | 
				
			||||||
 | 
					          modifiers.valid = false;
 | 
				
			||||||
          return [modifiers, args];
 | 
					          return [modifiers, args];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
| 
						 | 
					@ -114,6 +119,7 @@ export const getModifiers = (args: string[]): [RollModifiers, string[]] => {
 | 
				
			||||||
          // If -o is on and asc or desc was not specified, error out
 | 
					          // If -o is on and asc or desc was not specified, error out
 | 
				
			||||||
          modifiers.error.name = 'NoOrderFound';
 | 
					          modifiers.error.name = 'NoOrderFound';
 | 
				
			||||||
          modifiers.error.message = 'Must specify `a` or `d` to order the rolls ascending or descending';
 | 
					          modifiers.error.message = 'Must specify `a` or `d` to order the rolls ascending or descending';
 | 
				
			||||||
 | 
					          modifiers.valid = false;
 | 
				
			||||||
          return [modifiers, args];
 | 
					          return [modifiers, args];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -129,6 +135,65 @@ export const getModifiers = (args: string[]): [RollModifiers, string[]] => {
 | 
				
			||||||
      case Modifiers.VariablesNumber:
 | 
					      case Modifiers.VariablesNumber:
 | 
				
			||||||
        modifiers.numberVariables = true;
 | 
					        modifiers.numberVariables = true;
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
 | 
					      case Modifiers.CustomDiceShapes: {
 | 
				
			||||||
 | 
					        // Shift the -cd out of the array so the dice shapes are next
 | 
				
			||||||
 | 
					        args.splice(i, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const cdSyntaxMessage =
 | 
				
			||||||
 | 
					          'Must specify at least one custom dice shape using the `name:[side1,side2,...,sideN]` syntax.  If multiple custom dice shapes are needed, use a `;` to separate the list.';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const shapes = (args[i] ?? '').split(';').filter((x) => x);
 | 
				
			||||||
 | 
					        if (!shapes.length) {
 | 
				
			||||||
 | 
					          modifiers.error.name = 'NoShapesSpecified';
 | 
				
			||||||
 | 
					          modifiers.error.message = `No custom shaped dice found.\n\n${cdSyntaxMessage}`;
 | 
				
			||||||
 | 
					          modifiers.valid = false;
 | 
				
			||||||
 | 
					          return [modifiers, args];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (const shape of shapes) {
 | 
				
			||||||
 | 
					          const [name, rawSides] = shape.split(':').filter((x) => x);
 | 
				
			||||||
 | 
					          if (!name || !rawSides || !rawSides.includes('[') || !rawSides.includes(']')) {
 | 
				
			||||||
 | 
					            modifiers.error.name = 'InvalidShapeSpecified';
 | 
				
			||||||
 | 
					            modifiers.error.message = `One of the custom dice is not formatted correctly.\n\n${cdSyntaxMessage}`;
 | 
				
			||||||
 | 
					            modifiers.valid = false;
 | 
				
			||||||
 | 
					            return [modifiers, args];
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          if (modifiers.customDiceShapes.has(name)) {
 | 
				
			||||||
 | 
					            modifiers.error.name = 'ShapeAlreadySpecified';
 | 
				
			||||||
 | 
					            modifiers.error.message = `Shape \`${name}\` is already specified, please give it a different name.\n\n${cdSyntaxMessage}`;
 | 
				
			||||||
 | 
					            modifiers.valid = false;
 | 
				
			||||||
 | 
					            return [modifiers, args];
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          if (reservedCharacters.some((char) => name.includes(char))) {
 | 
				
			||||||
 | 
					            modifiers.error.name = 'InvalidCharacterInCDName';
 | 
				
			||||||
 | 
					            modifiers.error.message = `Custom dice names cannot include any of the following characters:\n${JSON.stringify(
 | 
				
			||||||
 | 
					              reservedCharacters
 | 
				
			||||||
 | 
					            )}\n\n${cdSyntaxMessage}`;
 | 
				
			||||||
 | 
					            modifiers.valid = false;
 | 
				
			||||||
 | 
					            return [modifiers, args];
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          const sides = rawSides
 | 
				
			||||||
 | 
					            .replaceAll('[', '')
 | 
				
			||||||
 | 
					            .replaceAll(']', '')
 | 
				
			||||||
 | 
					            .split(',')
 | 
				
			||||||
 | 
					            .filter((x) => x)
 | 
				
			||||||
 | 
					            .map((side) => parseInt(side));
 | 
				
			||||||
 | 
					          if (!sides.length) {
 | 
				
			||||||
 | 
					            modifiers.error.name = 'NoCustomSidesSpecified';
 | 
				
			||||||
 | 
					            modifiers.error.message = `No sides found for \`${name}\`.\n\n${cdSyntaxMessage}`;
 | 
				
			||||||
 | 
					            modifiers.valid = false;
 | 
				
			||||||
 | 
					            return [modifiers, args];
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          modifiers.customDiceShapes.set(name, sides);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        log(LT.LOG, `Generated Custom Dice: ${JSON.stringify(modifiers.customDiceShapes.entries().toArray())}`);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
        // Default case should not mess with the array
 | 
					        // Default case should not mess with the array
 | 
				
			||||||
        defaultCase = true;
 | 
					        defaultCase = true;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
import { log, LogTypes as LT } from '@Log4Deno';
 | 
					import { log, LogTypes as LT } from '@Log4Deno';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { RollConf } from 'artigen/dice/dice.d.ts';
 | 
					import { CustomDiceShapes, RollConf } from 'artigen/dice/dice.d.ts';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { DiceOptions, NumberlessDiceOptions } from 'artigen/dice/rollOptions.ts';
 | 
					import { DiceOptions, NumberlessDiceOptions } from 'artigen/dice/rollOptions.ts';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,13 +14,14 @@ const throwDoubleSepError = (sep: string): void => {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Converts a rollStr into a machine readable rollConf
 | 
					// Converts a rollStr into a machine readable rollConf
 | 
				
			||||||
export const getRollConf = (rollStr: string): RollConf => {
 | 
					export const getRollConf = (rollStr: string, customTypes: CustomDiceShapes = new Map<string, number[]>()): RollConf => {
 | 
				
			||||||
  // Split the roll on the die size (and the drop if its there)
 | 
					  // Split the roll on the die size (and the drop if its there)
 | 
				
			||||||
  const dPts = rollStr.split('d');
 | 
					  const dPts = rollStr.split('d');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Initialize the configuration to store the parsed data
 | 
					  // Initialize the configuration to store the parsed data
 | 
				
			||||||
  const rollConf: RollConf = {
 | 
					  const rollConf: RollConf = {
 | 
				
			||||||
    type: '',
 | 
					    type: '',
 | 
				
			||||||
 | 
					    customType: null,
 | 
				
			||||||
    dieCount: 0,
 | 
					    dieCount: 0,
 | 
				
			||||||
    dieSize: 0,
 | 
					    dieSize: 0,
 | 
				
			||||||
    dPercent: {
 | 
					    dPercent: {
 | 
				
			||||||
| 
						 | 
					@ -92,10 +93,12 @@ export const getRollConf = (rollStr: string): RollConf => {
 | 
				
			||||||
  const rawDC = dPts.shift() || '1';
 | 
					  const rawDC = dPts.shift() || '1';
 | 
				
			||||||
  if (rawDC.includes('.')) {
 | 
					  if (rawDC.includes('.')) {
 | 
				
			||||||
    throw new Error('WholeDieCountSizeOnly');
 | 
					    throw new Error('WholeDieCountSizeOnly');
 | 
				
			||||||
  } else if (!rawDC.endsWith('cwo') && !rawDC.endsWith('ova') && rawDC.match(/\D/)) {
 | 
					 | 
				
			||||||
    throw new Error(`CannotParseDieCount_${rawDC}`);
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  const tempDC = rawDC.replace(/\D/g, '');
 | 
					  const tempDC = rawDC.replace(/\D/g, '');
 | 
				
			||||||
 | 
					  const numberlessRawDC = rawDC.replace(/\d/g, '');
 | 
				
			||||||
 | 
					  if (!tempDC && !numberlessRawDC) {
 | 
				
			||||||
 | 
					    throw new Error(`CannotParseDieCount_${rawDC}`);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  // Rejoin all remaining parts
 | 
					  // Rejoin all remaining parts
 | 
				
			||||||
  let remains = dPts.join('d');
 | 
					  let remains = dPts.join('d');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -160,6 +163,12 @@ export const getRollConf = (rollStr: string): RollConf => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // remove F from the remains
 | 
					    // remove F from the remains
 | 
				
			||||||
    remains = remains.slice(1);
 | 
					    remains = remains.slice(1);
 | 
				
			||||||
 | 
					  } else if (customTypes.has(numberlessRawDC)) {
 | 
				
			||||||
 | 
					    // custom dice setup
 | 
				
			||||||
 | 
					    rollConf.type = 'custom';
 | 
				
			||||||
 | 
					    rollConf.customType = numberlessRawDC;
 | 
				
			||||||
 | 
					    rollConf.dieCount = isNaN(parseInt(tempDC ?? '1')) ? 1 : parseInt(tempDC ?? '1');
 | 
				
			||||||
 | 
					    rollConf.dieSize = Math.max(...(customTypes.get(numberlessRawDC) ?? []));
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    // roll20 dice setup
 | 
					    // roll20 dice setup
 | 
				
			||||||
    rollConf.type = 'roll20';
 | 
					    rollConf.type = 'roll20';
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,10 @@
 | 
				
			||||||
import { DPercentConf, RollModifiers } from 'artigen/dice/dice.d.ts';
 | 
					import { DPercentConf, RollConf, RollModifiers } from 'artigen/dice/dice.d.ts';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// genRoll(size) returns number
 | 
					import { basicReducer } from 'artigen/utils/reducers.ts';
 | 
				
			||||||
// genRoll rolls a die of size size and returns the result
 | 
					
 | 
				
			||||||
export const genRoll = (size: number, modifiers: RollModifiers, dPercent: DPercentConf): number => {
 | 
					// genBasicRoll(size, modifiers, dPercent) returns number
 | 
				
			||||||
 | 
					// genBasicRoll rolls a die of size size and returns the result
 | 
				
			||||||
 | 
					const genBasicRoll = (size: number, modifiers: RollModifiers, dPercent: DPercentConf): number => {
 | 
				
			||||||
  let result;
 | 
					  let result;
 | 
				
			||||||
  if (modifiers.maxRoll) {
 | 
					  if (modifiers.maxRoll) {
 | 
				
			||||||
    result = size;
 | 
					    result = size;
 | 
				
			||||||
| 
						 | 
					@ -15,13 +17,25 @@ export const genRoll = (size: number, modifiers: RollModifiers, dPercent: DPerce
 | 
				
			||||||
  return dPercent.on ? (result - 1) * dPercent.sizeAdjustment : result;
 | 
					  return dPercent.on ? (result - 1) * dPercent.sizeAdjustment : result;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// genFateRoll returns -1|0|1
 | 
					const getRollFromArray = (sides: number[], modifiers: RollModifiers): number => {
 | 
				
			||||||
// genFateRoll turns a d6 into a fate die, with sides: -1, -1, 0, 0, 1, 1
 | 
					 | 
				
			||||||
export const genFateRoll = (modifiers: RollModifiers): number => {
 | 
					 | 
				
			||||||
  if (modifiers.nominalRoll) {
 | 
					  if (modifiers.nominalRoll) {
 | 
				
			||||||
    return 0;
 | 
					    return sides.reduce(basicReducer, 0) / sides.length;
 | 
				
			||||||
  } else {
 | 
					  } else if (modifiers.maxRoll) {
 | 
				
			||||||
    const sides = [-1, -1, 0, 0, 1, 1];
 | 
					    return Math.max(...sides);
 | 
				
			||||||
    return sides[genRoll(6, modifiers, <DPercentConf> { on: false }) - 1];
 | 
					  } else if (modifiers.minRoll) {
 | 
				
			||||||
 | 
					    return Math.min(...sides);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return sides[genBasicRoll(sides.length, modifiers, <DPercentConf>{ on: false }) - 1];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const generateRoll = (rollConf: RollConf, modifiers: RollModifiers): number => {
 | 
				
			||||||
 | 
					  switch (rollConf.type) {
 | 
				
			||||||
 | 
					    case 'fate':
 | 
				
			||||||
 | 
					      return getRollFromArray([-1, -1, 0, 0, 1, 1], modifiers);
 | 
				
			||||||
 | 
					    case 'custom':
 | 
				
			||||||
 | 
					      return getRollFromArray(modifiers.customDiceShapes.get(rollConf.customType ?? '') ?? [], modifiers);
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      return genBasicRoll(rollConf.dieSize, modifiers, rollConf.dPercent);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -310,7 +310,9 @@ export const tokenizeMath = (
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Now that mathConf is parsed, send it into the solver
 | 
					  // Now that mathConf is parsed, send it into the solver
 | 
				
			||||||
 | 
					  loggingEnabled && log(LT.LOG, `Sending mathConf to solver ${JSON.stringify(mathConf)}`);
 | 
				
			||||||
  const tempSolved = mathSolver(mathConf);
 | 
					  const tempSolved = mathSolver(mathConf);
 | 
				
			||||||
 | 
					  loggingEnabled && log(LT.LOG, `SolvedStep back from mathSolver ${JSON.stringify(tempSolved)}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Push all of this step's solved data into the temp array
 | 
					  // Push all of this step's solved data into the temp array
 | 
				
			||||||
  return [
 | 
					  return [
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
import { RollConf, RollSet } from 'artigen/dice/dice.d.ts';
 | 
					import { CustomDiceShapes, RollConf, RollSet } from 'artigen/dice/dice.d.ts';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const flagRoll = (rollConf: RollConf, rollSet: RollSet) => {
 | 
					export const flagRoll = (rollConf: RollConf, rollSet: RollSet, customDiceShapes: CustomDiceShapes) => {
 | 
				
			||||||
  // 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
 | 
				
			||||||
  if (rollConf.critScore.on && rollConf.critScore.range.includes(rollSet.roll)) {
 | 
					  if (rollConf.critScore.on && rollConf.critScore.range.includes(rollSet.roll)) {
 | 
				
			||||||
    rollSet.critHit = true;
 | 
					    rollSet.critHit = true;
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,8 @@ export const flagRoll = (rollConf: RollConf, rollSet: RollSet) => {
 | 
				
			||||||
  } else if (!rollConf.critFail.on) {
 | 
					  } else if (!rollConf.critFail.on) {
 | 
				
			||||||
    if (rollConf.type === 'fate') {
 | 
					    if (rollConf.type === 'fate') {
 | 
				
			||||||
      rollSet.critFail = rollSet.roll === -1;
 | 
					      rollSet.critFail = rollSet.roll === -1;
 | 
				
			||||||
 | 
					    } else if (rollConf.type === 'custom') {
 | 
				
			||||||
 | 
					      rollSet.critFail = rollSet.roll === Math.min(...(customDiceShapes.get(rollConf.customType ?? '') ?? []));
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      rollSet.critFail = rollSet.roll === (rollConf.dPercent.on ? 0 : 1);
 | 
					      rollSet.critFail = rollSet.roll === (rollConf.dPercent.on ? 0 : 1);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -103,7 +103,8 @@ const getDistName = (key: string) => {
 | 
				
			||||||
      return `CWOD d${size}`;
 | 
					      return `CWOD d${size}`;
 | 
				
			||||||
    case 'ova':
 | 
					    case 'ova':
 | 
				
			||||||
      return `OVA d${size}`;
 | 
					      return `OVA d${size}`;
 | 
				
			||||||
    case 'roll20':
 | 
					    case 'custom':
 | 
				
			||||||
 | 
					      return `Custom d${size}`;
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      return `d${size}`;
 | 
					      return `d${size}`;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -113,19 +114,21 @@ export const generateRollDistsEmbed = (rollDists: RollDistributionMap): ArtigenE
 | 
				
			||||||
  const fields = rollDists
 | 
					  const fields = rollDists
 | 
				
			||||||
    .entries()
 | 
					    .entries()
 | 
				
			||||||
    .toArray()
 | 
					    .toArray()
 | 
				
			||||||
    .slice(0, 25)
 | 
					 | 
				
			||||||
    .map(([key, distArr]) => {
 | 
					    .map(([key, distArr]) => {
 | 
				
			||||||
      const total = distArr.reduce(basicReducer, 0);
 | 
					      const total = distArr.reduce(basicReducer, 0);
 | 
				
			||||||
      return {
 | 
					      return {
 | 
				
			||||||
        name: `${getDistName(key)} (Total rolls: ${total}):`,
 | 
					        name: `${getDistName(key)} (Total rolls: ${total}):`,
 | 
				
			||||||
        value: distArr.map((cnt, dieIdx) => `${key.startsWith('fate') ? dieIdx - 1 : dieIdx + 1}: ${cnt} (${((cnt / total) * 100).toFixed(1)}%)`).join('\n'),
 | 
					        value: distArr
 | 
				
			||||||
 | 
					          .map((cnt, dieIdx) => key.startsWith('custom') && cnt === 0 ? '' : `${key.startsWith('fate') ? dieIdx - 1 : dieIdx + 1}: ${cnt} (${((cnt / total) * 100).toFixed(1)}%)`)
 | 
				
			||||||
 | 
					          .filter((x) => x)
 | 
				
			||||||
 | 
					          .join('\n'),
 | 
				
			||||||
        inline: true,
 | 
					        inline: true,
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  const rollDistTitle = 'Roll Distributions:';
 | 
					  const rollDistTitle = 'Roll Distributions:';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const totalSize = fields.map((field) => field.name.length + field.value.length).reduce(basicReducer, 0);
 | 
					  const totalSize = fields.map((field) => field.name.length + field.value.length).reduce(basicReducer, 0);
 | 
				
			||||||
  if (totalSize > 4000 || fields.some((field) => field.name.length > 256 || field.value.length > 1024)) {
 | 
					  if (totalSize > 4000 || fields.length > 25 || fields.some((field) => field.name.length > 256 || field.value.length > 1024)) {
 | 
				
			||||||
    const rollDistBlob = new Blob([fields.map((field) => `# ${field.name}\n${field.value}`).join('\n\n') as BlobPart], { type: 'text' });
 | 
					    const rollDistBlob = new Blob([fields.map((field) => `# ${field.name}\n${field.value}`).join('\n\n') as BlobPart], { type: 'text' });
 | 
				
			||||||
    if (rollDistBlob.size > config.maxFileSize) {
 | 
					    if (rollDistBlob.size > config.maxFileSize) {
 | 
				
			||||||
      const rollDistErrDesc =
 | 
					      const rollDistErrDesc =
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,6 +4,7 @@ import { log, LogTypes as LT } from '@Log4Deno';
 | 
				
			||||||
import config from '~config';
 | 
					import config from '~config';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { RollModifiers } from 'artigen/dice/dice.d.ts';
 | 
					import { RollModifiers } from 'artigen/dice/dice.d.ts';
 | 
				
			||||||
 | 
					import { reservedCharacters } from 'artigen/dice/getModifiers.ts';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { sendRollRequest } from 'artigen/managers/queueManager.ts';
 | 
					import { sendRollRequest } from 'artigen/managers/queueManager.ts';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -100,11 +101,48 @@ export const apiRoll = async (query: Map<string, string>, apiUserid: bigint): Pr
 | 
				
			||||||
          confirmCrit: query.has('cc'),
 | 
					          confirmCrit: query.has('cc'),
 | 
				
			||||||
          rollDist: query.has('rd'),
 | 
					          rollDist: query.has('rd'),
 | 
				
			||||||
          numberVariables: query.has('nv') || query.has('vn'),
 | 
					          numberVariables: query.has('nv') || query.has('vn'),
 | 
				
			||||||
 | 
					          customDiceShapes: new Map<string, number[]>(),
 | 
				
			||||||
          apiWarn: hideWarn ? '' : apiWarning,
 | 
					          apiWarn: hideWarn ? '' : apiWarning,
 | 
				
			||||||
          valid: true,
 | 
					          valid: true,
 | 
				
			||||||
          error: new Error(),
 | 
					          error: new Error(),
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Handle adding all sides into the cds array
 | 
				
			||||||
 | 
					        if (query.has('cd')) {
 | 
				
			||||||
 | 
					          const shapes = (query.get('cd') ?? '').split(';').filter((x) => x);
 | 
				
			||||||
 | 
					          if (!shapes.length) {
 | 
				
			||||||
 | 
					            return stdResp.BadRequest('cd specified without any shapes provided');
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          for (const shape of shapes) {
 | 
				
			||||||
 | 
					            const [name, rawSides] = shape.split(':').filter((x) => x);
 | 
				
			||||||
 | 
					            if (!name || !rawSides) {
 | 
				
			||||||
 | 
					              return stdResp.BadRequest(
 | 
				
			||||||
 | 
					                'cd specified with invalid pattern.  Must be in format of `name:[side1,side2,...,sideN]`.  If multiple custom dice shapes are needed, use a `;` to separate the list'
 | 
				
			||||||
 | 
					              );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (modifiers.customDiceShapes.has(name)) {
 | 
				
			||||||
 | 
					              return stdResp.BadRequest('cd specified, cannot repeat names');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (reservedCharacters.some((char) => name.includes(char))) {
 | 
				
			||||||
 | 
					              return stdResp.BadRequest('cd specified, die name includes invalid characters.  Reserved Character List: ' + JSON.stringify(reservedCharacters));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const sides = rawSides
 | 
				
			||||||
 | 
					              .replaceAll('[', '')
 | 
				
			||||||
 | 
					              .replaceAll(']', '')
 | 
				
			||||||
 | 
					              .split(',')
 | 
				
			||||||
 | 
					              .filter((x) => x)
 | 
				
			||||||
 | 
					              .map((side) => parseInt(side));
 | 
				
			||||||
 | 
					            if (!sides.length) {
 | 
				
			||||||
 | 
					              return stdResp.BadRequest('cd specified without any sides provided');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            modifiers.customDiceShapes.set(name, sides);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // maxRoll, minRoll, and nominalRoll cannot be on at same time, throw an error
 | 
					        // maxRoll, minRoll, and nominalRoll cannot be on at same time, throw an error
 | 
				
			||||||
        if ([modifiers.maxRoll, modifiers.minRoll, modifiers.nominalRoll, modifiers.simulatedNominal].filter((b) => b).length > 1) {
 | 
					        if ([modifiers.maxRoll, modifiers.minRoll, modifiers.nominalRoll, modifiers.simulatedNominal].filter((b) => b).length > 1) {
 | 
				
			||||||
          return stdResp.BadRequest('Can only use one of the following at a time:\n`maximize`, `minimize`, `nominal`, `simulatedNominal`');
 | 
					          return stdResp.BadRequest('Can only use one of the following at a time:\n`maximize`, `minimize`, `nominal`, `simulatedNominal`');
 | 
				
			||||||
| 
						 | 
					@ -139,7 +177,7 @@ export const apiRoll = async (query: Map<string, string>, apiUserid: bigint): Pr
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      // Alert API user that they messed up
 | 
					      // Alert API user that they messed up
 | 
				
			||||||
      return stdResp.Forbidden(
 | 
					      return stdResp.Forbidden(
 | 
				
			||||||
        `Verify you are a member of the guild you are sending this roll to.  If you are, the ${config.name} may not have that registered, please send a message in the guild so ${config.name} can register this.  This registration is temporary, so if you see this error again, just poke your server again.`,
 | 
					        `Verify you are a member of the guild you are sending this roll to.  If you are, the ${config.name} may not have that registered, please send a message in the guild so ${config.name} can register this.  This registration is temporary, so if you see this error again, just poke your server again.`
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue