Implement dice matching option
This commit is contained in:
		
							parent
							
								
									791dd3a626
								
							
						
					
					
						commit
						0e009441ca
					
				| 
						 | 
				
			
			@ -106,6 +106,10 @@ The Artificer comes with a few supplemental commands to the main rolling command
 | 
			
		|||
  |  !!=u         |  Optional   |      Yes      |  compounding explosion, rolls one dy for each die that lands on u, but adds the resulting explosion to the die that caused this explosion                                |
 | 
			
		||||
  |  !!>u         |  Optional   |      Yes      |  compounding explosion, rolls one dy for each die that lands on u or greater, but adds the resulting explosion to the die that caused this explosion                     |
 | 
			
		||||
  |  !!<u         |  Optional   |      Yes      |  compounding explosion, rolls one dy for each die that lands on u or under, but adds the resulting explosion to the die that caused this explosion                       |
 | 
			
		||||
  |  m            |  Optional   |      No       |  matching dice, adds labels to any dice that match                                                                                                                       |
 | 
			
		||||
  |  mz           |  Optional   |      No       |  matching dice, adds labels to any dice that have z or more matches                                                                                                      |
 | 
			
		||||
  |  mt           |  Optional   |      No       |  matching dice, adds labels to any dice that match, changes result to be the count of labels added                                                                       |
 | 
			
		||||
  |  mtz          |  Optional   |      No       |  matching dice, adds labels to any dice that have z or more matches, changes result to be the count of labels added                                                      |
 | 
			
		||||
 | 
			
		||||
  * If the parameter is Required, it must be provided at all times.
 | 
			
		||||
  * If the parameter is Repeatable, it may occur multiple times in the roll configuration.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,6 +15,7 @@ export interface RollSet {
 | 
			
		|||
  critHit: boolean;
 | 
			
		||||
  critFail: boolean;
 | 
			
		||||
  isComplex: boolean;
 | 
			
		||||
  matchLabel: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CountDetails is the object holding the count data for creating the Count Embed
 | 
			
		||||
| 
						 | 
				
			
			@ -102,4 +103,14 @@ export interface RollConf {
 | 
			
		|||
    penetrating: boolean;
 | 
			
		||||
    nums: number[];
 | 
			
		||||
  };
 | 
			
		||||
  match: {
 | 
			
		||||
    on: boolean;
 | 
			
		||||
    minCount: number;
 | 
			
		||||
    returnTotal: boolean;
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface SumOverride {
 | 
			
		||||
  on: boolean;
 | 
			
		||||
  value: number;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
import { log, LogTypes as LT } from '@Log4Deno';
 | 
			
		||||
 | 
			
		||||
import { RollModifiers, RollSet } from 'artigen/dice/dice.d.ts';
 | 
			
		||||
import { RollModifiers, RollSet, SumOverride } from 'artigen/dice/dice.d.ts';
 | 
			
		||||
import { genFateRoll, genRoll } from 'artigen/dice/randomRoll.ts';
 | 
			
		||||
import { getRollConf } from 'artigen/dice/getRollConf.ts';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -8,10 +8,11 @@ import { loggingEnabled } from 'artigen/utils/logFlag.ts';
 | 
			
		|||
import { compareOrigIdx, compareRolls } from 'artigen/utils/sortFuncs.ts';
 | 
			
		||||
 | 
			
		||||
import { getLoopCount, loopCountCheck } from 'artigen/managers/loopManager.ts';
 | 
			
		||||
import { generateRollVals } from 'artigen/utils/rollValCounter.ts';
 | 
			
		||||
 | 
			
		||||
// roll(rollStr, modifiers) returns RollSet
 | 
			
		||||
// roll parses and executes the rollStr
 | 
			
		||||
export const executeRoll = (rollStr: string, modifiers: RollModifiers): RollSet[] => {
 | 
			
		||||
export const executeRoll = (rollStr: string, modifiers: RollModifiers): [RollSet[], SumOverride] => {
 | 
			
		||||
  /* Roll Capabilities
 | 
			
		||||
   * Deciphers and rolls a single dice roll set
 | 
			
		||||
   *
 | 
			
		||||
| 
						 | 
				
			
			@ -67,6 +68,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): RollSet[
 | 
			
		|||
      rollConf.critScore.on ||
 | 
			
		||||
      rollConf.critFail.on ||
 | 
			
		||||
      rollConf.exploding.on,
 | 
			
		||||
    matchLabel: '',
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // Initial rolling, not handling reroll or exploding here
 | 
			
		||||
| 
						 | 
				
			
			@ -307,18 +309,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): RollSet[
 | 
			
		|||
 | 
			
		||||
  // Handle OVA dropping/keeping
 | 
			
		||||
  if (rollConf.type === 'ova') {
 | 
			
		||||
    // Make "empty" vals array to easily sum up which die value is the greatest
 | 
			
		||||
    const rollVals: Array<number> = new Array(rollConf.dieSize).fill(0);
 | 
			
		||||
 | 
			
		||||
    // Sum up all rolls
 | 
			
		||||
    for (const ovaRoll of rollSet) {
 | 
			
		||||
      loopCountCheck();
 | 
			
		||||
 | 
			
		||||
      loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | incrementing rollVals for ${JSON.stringify(ovaRoll)}`);
 | 
			
		||||
      if (!ovaRoll.dropped && !ovaRoll.rerolled) {
 | 
			
		||||
        rollVals[ovaRoll.roll - 1] += ovaRoll.roll;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    const rollVals: Array<number> = generateRollVals(rollConf, rollSet, rollStr, false);
 | 
			
		||||
 | 
			
		||||
    // Find max value, using lastIndexOf to use the greatest die size max in case of duplicate maximums
 | 
			
		||||
    const maxRoll = rollVals.lastIndexOf(Math.max(...rollVals)) + 1;
 | 
			
		||||
| 
						 | 
				
			
			@ -337,5 +328,47 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): RollSet[
 | 
			
		|||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return rollSet;
 | 
			
		||||
  const sumOverride: SumOverride = {
 | 
			
		||||
    on: rollConf.match.returnTotal,
 | 
			
		||||
    value: 0,
 | 
			
		||||
  };
 | 
			
		||||
  if (rollConf.match.on) {
 | 
			
		||||
    const rollVals: Array<number> = generateRollVals(rollConf, rollSet, rollStr, true).map((count) => (count >= rollConf.match.minCount ? count : 0));
 | 
			
		||||
    const labels = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
 | 
			
		||||
    let labelIdx = 0;
 | 
			
		||||
    const rollLabels: Array<string> = rollVals.map((count) => {
 | 
			
		||||
      loopCountCheck();
 | 
			
		||||
 | 
			
		||||
      if (labelIdx >= labels.length) {
 | 
			
		||||
        throw new Error(`TooManyLabels_${labels.length}`);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (count) {
 | 
			
		||||
        return labels[labelIdx++];
 | 
			
		||||
      }
 | 
			
		||||
      return '';
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | current match state: ${rollVals} | ${rollLabels}`);
 | 
			
		||||
 | 
			
		||||
    // Apply labels
 | 
			
		||||
    for (const roll of rollSet) {
 | 
			
		||||
      loopCountCheck();
 | 
			
		||||
 | 
			
		||||
      loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | trying to add a label to ${JSON.stringify(roll)}`);
 | 
			
		||||
      if (rollLabels[roll.roll - 1]) {
 | 
			
		||||
        roll.matchLabel = rollLabels[roll.roll - 1];
 | 
			
		||||
      } else if (rollConf.match.returnTotal) {
 | 
			
		||||
        roll.dropped = true;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | labels added: ${JSON.stringify(rollSet)}`);
 | 
			
		||||
 | 
			
		||||
    if (rollConf.match.returnTotal) {
 | 
			
		||||
      sumOverride.value = rollVals.filter((count) => count !== 0).length;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return [rollSet, sumOverride];
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,7 +19,7 @@ export const generateFormattedRoll = (rollConf: string, modifiers: RollModifiers
 | 
			
		|||
  let tempComplex = false;
 | 
			
		||||
 | 
			
		||||
  // Generate the roll, passing flags thru
 | 
			
		||||
  const tempRollSet = executeRoll(rollConf, modifiers);
 | 
			
		||||
  const [tempRollSet, sumOverride] = executeRoll(rollConf, modifiers);
 | 
			
		||||
 | 
			
		||||
  // Loop thru all parts of the roll to document everything that was done to create the total roll
 | 
			
		||||
  tempRollSet.forEach((e) => {
 | 
			
		||||
| 
						 | 
				
			
			@ -72,8 +72,13 @@ export const generateFormattedRoll = (rollConf: string, modifiers: RollModifiers
 | 
			
		|||
      postFormat = `!${postFormat}`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let rollLabel = '';
 | 
			
		||||
    if (e.matchLabel) {
 | 
			
		||||
      rollLabel = `${e.matchLabel}:`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Finally add this to the roll's details
 | 
			
		||||
    tempDetails += `${preFormat}${e.roll}${postFormat} + `;
 | 
			
		||||
    tempDetails += `${preFormat}${rollLabel}${e.roll}${postFormat} + `;
 | 
			
		||||
  });
 | 
			
		||||
  // After the looping is done, remove the extra " + " from the details and cap it with the closing ]
 | 
			
		||||
  tempDetails = tempDetails.substring(0, tempDetails.length - 3);
 | 
			
		||||
| 
						 | 
				
			
			@ -86,7 +91,7 @@ export const generateFormattedRoll = (rollConf: string, modifiers: RollModifiers
 | 
			
		|||
 | 
			
		||||
  return {
 | 
			
		||||
    solvedStep: {
 | 
			
		||||
      total: tempTotal,
 | 
			
		||||
      total: sumOverride.on ? sumOverride.value : tempTotal,
 | 
			
		||||
      details: tempDetails,
 | 
			
		||||
      containsCrit: tempCrit,
 | 
			
		||||
      containsFail: tempFail,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -61,6 +61,11 @@ export const getRollConf = (rollStr: string): RollConf => {
 | 
			
		|||
      penetrating: false,
 | 
			
		||||
      nums: [],
 | 
			
		||||
    },
 | 
			
		||||
    match: {
 | 
			
		||||
      on: false,
 | 
			
		||||
      minCount: 2,
 | 
			
		||||
      returnTotal: false,
 | 
			
		||||
    },
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // If the dPts is not long enough, throw error
 | 
			
		||||
| 
						 | 
				
			
			@ -380,12 +385,23 @@ export const getRollConf = (rollStr: string): RollConf => {
 | 
			
		|||
            !rollConf.exploding.nums.includes(i) && rollConf.exploding.nums.push(i);
 | 
			
		||||
          }
 | 
			
		||||
          break;
 | 
			
		||||
        case 'm':
 | 
			
		||||
        case 'mt':
 | 
			
		||||
          rollConf.match.on = true;
 | 
			
		||||
          if (afterNumIdx > 0) {
 | 
			
		||||
            // User gave a number to explode on, save it
 | 
			
		||||
            rollConf.match.minCount = tNum;
 | 
			
		||||
          } else {
 | 
			
		||||
            // User did not give number, use cs
 | 
			
		||||
            afterNumIdx = 1;
 | 
			
		||||
          }
 | 
			
		||||
          break;
 | 
			
		||||
        default:
 | 
			
		||||
          // 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
 | 
			
		||||
      // Followup switch to avoid weird duplicated code
 | 
			
		||||
      switch (tSep) {
 | 
			
		||||
        case '!o':
 | 
			
		||||
        case '!o=':
 | 
			
		||||
| 
						 | 
				
			
			@ -405,6 +421,9 @@ export const getRollConf = (rollStr: string): RollConf => {
 | 
			
		|||
        case '!!<':
 | 
			
		||||
          rollConf.exploding.compounding = true;
 | 
			
		||||
          break;
 | 
			
		||||
        case 'mt':
 | 
			
		||||
          rollConf.match.returnTotal = true;
 | 
			
		||||
          break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Finally slice off everything else parsed this loop
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,25 @@
 | 
			
		|||
import { log, LogTypes as LT } from '@Log4Deno';
 | 
			
		||||
 | 
			
		||||
import { RollConf, RollSet } from 'artigen/dice/dice.d.ts';
 | 
			
		||||
 | 
			
		||||
import { getLoopCount, loopCountCheck } from 'artigen/managers/loopManager.ts';
 | 
			
		||||
 | 
			
		||||
import { loggingEnabled } from 'artigen/utils/logFlag.ts';
 | 
			
		||||
 | 
			
		||||
// Can either count or sum each die
 | 
			
		||||
export const generateRollVals = (rollConf: RollConf, rollSet: RollSet[], rollStr: string, count: boolean): Array<number> => {
 | 
			
		||||
  const rollVals = new Array(rollConf.dieSize).fill(0);
 | 
			
		||||
 | 
			
		||||
  // Count up all rolls
 | 
			
		||||
  for (const ovaRoll of rollSet) {
 | 
			
		||||
    loopCountCheck();
 | 
			
		||||
 | 
			
		||||
    loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | incrementing rollVals for ${JSON.stringify(ovaRoll)}`);
 | 
			
		||||
    if (!ovaRoll.dropped && !ovaRoll.rerolled) {
 | 
			
		||||
      rollVals[ovaRoll.roll - 1] += count ? 1 : ovaRoll.roll;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | rollVals ${rollVals}`);
 | 
			
		||||
  return rollVals;
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -112,6 +112,9 @@ export const translateError = (solverError: Error): [string, string] => {
 | 
			
		|||
    case 'IllegalVariable':
 | 
			
		||||
      errorMsg = `Error: \`${errorDetails}\` is not a valid variable`;
 | 
			
		||||
      break;
 | 
			
		||||
    case 'TooManyLabels':
 | 
			
		||||
      errorMsg = `Error: ${config.name} can only support a maximum of \`${errorDetails}\` labels when using the dice matching options (\`m\` or \`mt\`)`;
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      log(LT.ERROR, `Unhandled Parser Error: ${errorName}, ${errorDetails}`);
 | 
			
		||||
      errorMsg = `Unhandled Error: ${solverError.message}\nCheck input and try again, if issue persists, please use \`${config.prefix}report\` to alert the devs of the issue`;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -230,6 +230,22 @@ Any time a Compounding Explosion happens, the formatting on the die will still s
 | 
			
		|||
      ],
 | 
			
		||||
    },
 | 
			
		||||
  ],
 | 
			
		||||
  [
 | 
			
		||||
    'dice-matching',
 | 
			
		||||
    {
 | 
			
		||||
      name: 'Dice Matching',
 | 
			
		||||
      description: `**Usage:** \`xdym\`, \`xdymz\`, \`xdymt\`, or \`xdymtz\`
 | 
			
		||||
\`z\` - Minimum count of matches for a label to be added
 | 
			
		||||
 | 
			
		||||
The basic \`m\` option will only add labels without modifying the results, whereas the \`mt\` options will add labels and will change the result of the roll to be equal to the number of labels that have been added.`,
 | 
			
		||||
      example: [
 | 
			
		||||
        '`[[10d6m]]` => [**C:6** + B:2 + 4 + __C:1__ + __C:1__ + B:2 + **C:6** + B:2 + **C:6** + **C:6**] = 36',
 | 
			
		||||
        '`[[10d6m4]]` => [**A:6** + 2 + 4 + __1__ + __1__ + 2 + **A:6** + 2 + **A:6** + **A:6**] = 36',
 | 
			
		||||
        '`[[10d6mt]]` => [**C:6** + B:2 + ~~4~~ + __C:1__ + __C:1__ + B:2 + **C:6** + B:2 + **C:6** + **C:6**] = 3',
 | 
			
		||||
        '`[[10d6mt4]]` => [**A:6** + ~~2~~ + ~~4~~ + ~~__1__~~ + ~~__1__~~ + ~~2~~ + **A:6** + ~~2~~ + **A:6** + **A:6**] = 1',
 | 
			
		||||
      ],
 | 
			
		||||
    },
 | 
			
		||||
  ],
 | 
			
		||||
]);
 | 
			
		||||
 | 
			
		||||
export const DiceOptionsHelpPages: HelpPage = {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue