Add target success/failures option
This commit is contained in:
		
							parent
							
								
									38bc021455
								
							
						
					
					
						commit
						15fd57ea18
					
				| 
						 | 
				
			
			@ -16,6 +16,8 @@ export interface RollSet {
 | 
			
		|||
  critFail: boolean;
 | 
			
		||||
  isComplex: boolean;
 | 
			
		||||
  matchLabel: string;
 | 
			
		||||
  success: boolean;
 | 
			
		||||
  fail: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CountDetails is the object holding the count data for creating the Count Embed
 | 
			
		||||
| 
						 | 
				
			
			@ -119,9 +121,18 @@ export interface RollConf {
 | 
			
		|||
    returnTotal: boolean;
 | 
			
		||||
  };
 | 
			
		||||
  sort: SortDisabled | SortEnabled;
 | 
			
		||||
  success: RangeConf;
 | 
			
		||||
  fail: RangeConf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface SumOverride {
 | 
			
		||||
  on: boolean;
 | 
			
		||||
  value: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ExecutedRoll {
 | 
			
		||||
  rollSet: RollSet[];
 | 
			
		||||
  countSuccessOverride: boolean;
 | 
			
		||||
  countFailOverride: boolean;
 | 
			
		||||
  sumOverride: SumOverride;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,6 +42,13 @@ export const DiceOptions = Object.freeze({
 | 
			
		|||
  Sort: 's',
 | 
			
		||||
  SortAsc: 'sa',
 | 
			
		||||
  SortDesc: 'sd',
 | 
			
		||||
  SuccessLt: '<',
 | 
			
		||||
  SuccessGtr: '>',
 | 
			
		||||
  SuccessEqu: '=',
 | 
			
		||||
  Fail: 'f',
 | 
			
		||||
  FailLt: 'f<',
 | 
			
		||||
  FailGtr: 'f>',
 | 
			
		||||
  FailEqu: 'f=',
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Should be ordered such that 'mt' will be encountered before 'm'
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,18 +1,19 @@
 | 
			
		|||
import { log, LogTypes as LT } from '@Log4Deno';
 | 
			
		||||
 | 
			
		||||
import { 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 { getRollConf } from 'artigen/dice/getRollConf.ts';
 | 
			
		||||
 | 
			
		||||
import { loggingEnabled } from 'artigen/utils/logFlag.ts';
 | 
			
		||||
import { compareOrigIdx, compareRolls, compareRollsReverse } from 'artigen/utils/sortFuncs.ts';
 | 
			
		||||
 | 
			
		||||
import { flagRoll } from 'artigen/utils/diceFlagger.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[], SumOverride] => {
 | 
			
		||||
export const executeRoll = (rollStr: string, modifiers: RollModifiers): ExecutedRoll => {
 | 
			
		||||
  /* Roll Capabilities
 | 
			
		||||
   * Deciphers and rolls a single dice roll set
 | 
			
		||||
   *
 | 
			
		||||
| 
						 | 
				
			
			@ -67,8 +68,12 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): [RollSet
 | 
			
		|||
      rollConf.keepLow.on ||
 | 
			
		||||
      rollConf.critScore.on ||
 | 
			
		||||
      rollConf.critFail.on ||
 | 
			
		||||
      rollConf.exploding.on,
 | 
			
		||||
      rollConf.exploding.on ||
 | 
			
		||||
      rollConf.success.on ||
 | 
			
		||||
      rollConf.fail.on,
 | 
			
		||||
    matchLabel: '',
 | 
			
		||||
    success: false,
 | 
			
		||||
    fail: false,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // Initial rolling, not handling reroll or exploding here
 | 
			
		||||
| 
						 | 
				
			
			@ -85,22 +90,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): [RollSet
 | 
			
		|||
    // Set origIdx of roll
 | 
			
		||||
    rolling.origIdx = i;
 | 
			
		||||
 | 
			
		||||
    // 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(rolling.roll)) {
 | 
			
		||||
      rolling.critHit = true;
 | 
			
		||||
    } else if (!rollConf.critScore.on) {
 | 
			
		||||
      rolling.critHit = rolling.roll === (rollConf.dPercent.on ? rollConf.dPercent.critVal : rollConf.dieSize);
 | 
			
		||||
    }
 | 
			
		||||
    // If critFail arg is on, check if the roll should be a fail, if its off, check if the roll matches 1
 | 
			
		||||
    if (rollConf.critFail.on && rollConf.critFail.range.includes(rolling.roll)) {
 | 
			
		||||
      rolling.critFail = true;
 | 
			
		||||
    } else if (!rollConf.critFail.on) {
 | 
			
		||||
      if (rollConf.type === 'fate') {
 | 
			
		||||
        rolling.critFail = rolling.roll === -1;
 | 
			
		||||
      } else {
 | 
			
		||||
        rolling.critFail = rolling.roll === (rollConf.dPercent.on ? 0 : 1);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    flagRoll(rollConf, rolling);
 | 
			
		||||
 | 
			
		||||
    // Push the newly created roll and loop again
 | 
			
		||||
    rollSet.push(rolling);
 | 
			
		||||
| 
						 | 
				
			
			@ -151,18 +141,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): [RollSet
 | 
			
		|||
          newReroll.roll = genRoll(rollConf.dieSize, modifiers, rollConf.dPercent);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 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(newReroll.roll)) {
 | 
			
		||||
          newReroll.critHit = true;
 | 
			
		||||
        } else if (!rollConf.critScore.on) {
 | 
			
		||||
          newReroll.critHit = newReroll.roll === (rollConf.dPercent.on ? rollConf.dPercent.critVal : rollConf.dieSize);
 | 
			
		||||
        }
 | 
			
		||||
        // If critFail arg is on, check if the roll should be a fail, if its off, check if the roll matches 1
 | 
			
		||||
        if (rollConf.critFail.on && rollConf.critFail.range.includes(newReroll.roll)) {
 | 
			
		||||
          newReroll.critFail = true;
 | 
			
		||||
        } else if (!rollConf.critFail.on) {
 | 
			
		||||
          newReroll.critFail = newReroll.roll === (rollConf.dPercent.on ? 0 : 1);
 | 
			
		||||
        }
 | 
			
		||||
        flagRoll(rollConf, newReroll);
 | 
			
		||||
 | 
			
		||||
        // Slot this new roll in after the current iteration so it can be processed in the next loop
 | 
			
		||||
        rollSet.splice(i + 1, 0, newReroll);
 | 
			
		||||
| 
						 | 
				
			
			@ -183,18 +162,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): [RollSet
 | 
			
		|||
        // Always mark this roll as exploding
 | 
			
		||||
        newExplodingRoll.exploding = true;
 | 
			
		||||
 | 
			
		||||
        // 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(newExplodingRoll.roll)) {
 | 
			
		||||
          newExplodingRoll.critHit = true;
 | 
			
		||||
        } else if (!rollConf.critScore.on) {
 | 
			
		||||
          newExplodingRoll.critHit = newExplodingRoll.roll === (rollConf.dPercent.on ? rollConf.dPercent.critVal : rollConf.dieSize);
 | 
			
		||||
        }
 | 
			
		||||
        // If critFail arg is on, check if the roll should be a fail, if its off, check if the roll matches 1
 | 
			
		||||
        if (rollConf.critFail.on && rollConf.critFail.range.includes(newExplodingRoll.roll)) {
 | 
			
		||||
          newExplodingRoll.critFail = true;
 | 
			
		||||
        } else if (!rollConf.critFail.on) {
 | 
			
		||||
          newExplodingRoll.critFail = newExplodingRoll.roll === (rollConf.dPercent.on ? 0 : 1);
 | 
			
		||||
        }
 | 
			
		||||
        flagRoll(rollConf, newExplodingRoll);
 | 
			
		||||
 | 
			
		||||
        // Slot this new roll in after the current iteration so it can be processed in the next loop
 | 
			
		||||
        rollSet.splice(i + 1, 0, newExplodingRoll);
 | 
			
		||||
| 
						 | 
				
			
			@ -374,5 +342,10 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): [RollSet
 | 
			
		|||
    rollSet.sort(rollConf.sort.direction === 'a' ? compareRolls : compareRollsReverse);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return [rollSet, sumOverride];
 | 
			
		||||
  return {
 | 
			
		||||
    rollSet,
 | 
			
		||||
    sumOverride,
 | 
			
		||||
    countSuccessOverride: rollConf.success.on,
 | 
			
		||||
    countFailOverride: rollConf.fail.on,
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,10 +19,10 @@ export const generateFormattedRoll = (rollConf: string, modifiers: RollModifiers
 | 
			
		|||
  let tempComplex = false;
 | 
			
		||||
 | 
			
		||||
  // Generate the roll, passing flags thru
 | 
			
		||||
  const [tempRollSet, sumOverride] = executeRoll(rollConf, modifiers);
 | 
			
		||||
  const executedRoll = executeRoll(rollConf, modifiers);
 | 
			
		||||
 | 
			
		||||
  // Loop thru all parts of the roll to document everything that was done to create the total roll
 | 
			
		||||
  tempRollSet.forEach((e) => {
 | 
			
		||||
  executedRoll.rollSet.forEach((e) => {
 | 
			
		||||
    loopCountCheck();
 | 
			
		||||
 | 
			
		||||
    loggingEnabled && log(LT.LOG, `Formatting roll ${rollConf} | ${JSON.stringify(e)}`);
 | 
			
		||||
| 
						 | 
				
			
			@ -38,7 +38,7 @@ export const generateFormattedRoll = (rollConf: string, modifiers: RollModifiers
 | 
			
		|||
          tempTotal += e.roll;
 | 
			
		||||
          break;
 | 
			
		||||
        case 'cwod':
 | 
			
		||||
          tempTotal += e.critHit ? 1 : 0;
 | 
			
		||||
          tempTotal += e.success ? 1 : 0;
 | 
			
		||||
          break;
 | 
			
		||||
      }
 | 
			
		||||
      if (e.critHit) {
 | 
			
		||||
| 
						 | 
				
			
			@ -82,22 +82,33 @@ export const generateFormattedRoll = (rollConf: string, modifiers: RollModifiers
 | 
			
		|||
  });
 | 
			
		||||
  // After the looping is done, remove the extra " + " from the details and cap it with the closing ]
 | 
			
		||||
  tempDetails = tempDetails.substring(0, tempDetails.length - 3);
 | 
			
		||||
  if (tempRollSet[0]?.type === 'cwod') {
 | 
			
		||||
    const successCnt = tempRollSet.filter((e) => !e.dropped && !e.rerolled && e.critHit).length;
 | 
			
		||||
    const failCnt = tempRollSet.filter((e) => !e.dropped && !e.rerolled && e.critFail).length;
 | 
			
		||||
    tempDetails += `, ${successCnt} Success${successCnt !== 1 ? 'es' : ''}, ${failCnt} Fail${failCnt !== 1 ? 's' : ''}`;
 | 
			
		||||
  if (executedRoll.countSuccessOverride) {
 | 
			
		||||
    const successCnt = executedRoll.rollSet.filter((e) => !e.dropped && !e.rerolled && e.success).length;
 | 
			
		||||
    tempDetails += `, ${successCnt} Success${successCnt !== 1 ? 'es' : ''}`;
 | 
			
		||||
 | 
			
		||||
    executedRoll.sumOverride.on = true;
 | 
			
		||||
    executedRoll.sumOverride.value += successCnt;
 | 
			
		||||
  }
 | 
			
		||||
  if (executedRoll.countFailOverride) {
 | 
			
		||||
    const failCnt = executedRoll.rollSet.filter((e) => !e.dropped && !e.rerolled && e.fail).length;
 | 
			
		||||
    tempDetails += `, ${failCnt} Fail${failCnt !== 1 ? 's' : ''}`;
 | 
			
		||||
 | 
			
		||||
    executedRoll.sumOverride.on = true;
 | 
			
		||||
    if (executedRoll.rollSet[0]?.type !== 'cwod') {
 | 
			
		||||
      executedRoll.sumOverride.value -= failCnt;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  tempDetails += ']';
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    solvedStep: {
 | 
			
		||||
      total: sumOverride.on ? sumOverride.value : tempTotal,
 | 
			
		||||
      total: executedRoll.sumOverride.on ? executedRoll.sumOverride.value : tempTotal,
 | 
			
		||||
      details: tempDetails,
 | 
			
		||||
      containsCrit: tempCrit,
 | 
			
		||||
      containsFail: tempFail,
 | 
			
		||||
      isComplex: tempComplex,
 | 
			
		||||
    },
 | 
			
		||||
    countDetails: modifiers.count || modifiers.confirmCrit ? rollCounter(tempRollSet) : rollCounter([]),
 | 
			
		||||
    rollDistributions: modifiers.rollDist ? createRollDistMap(tempRollSet) : new Map<string, number[]>(),
 | 
			
		||||
    countDetails: modifiers.count || modifiers.confirmCrit ? rollCounter(executedRoll.rollSet) : rollCounter([]),
 | 
			
		||||
    rollDistributions: modifiers.rollDist ? createRollDistMap(executedRoll.rollSet) : new Map<string, number[]>(),
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,9 +4,11 @@ import { RollConf } from 'artigen/dice/dice.d.ts';
 | 
			
		|||
 | 
			
		||||
import { getLoopCount, loopCountCheck } from 'artigen/managers/loopManager.ts';
 | 
			
		||||
 | 
			
		||||
import { loggingEnabled } from 'artigen/utils/logFlag.ts';
 | 
			
		||||
import { DiceOptions, NumberlessDiceOptions } from 'artigen/dice/diceOptions.ts';
 | 
			
		||||
 | 
			
		||||
import { loggingEnabled } from 'artigen/utils/logFlag.ts';
 | 
			
		||||
import { addToRange, gtrAddToRange, ltAddToRange } from 'artigen/utils/rangeAdder.ts';
 | 
			
		||||
 | 
			
		||||
const throwDoubleSepError = (sep: string): void => {
 | 
			
		||||
  throw new Error(`DoubleSeparator_${sep}`);
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -71,6 +73,14 @@ export const getRollConf = (rollStr: string): RollConf => {
 | 
			
		|||
      on: false,
 | 
			
		||||
      direction: '',
 | 
			
		||||
    },
 | 
			
		||||
    success: {
 | 
			
		||||
      on: false,
 | 
			
		||||
      range: [],
 | 
			
		||||
    },
 | 
			
		||||
    fail: {
 | 
			
		||||
      on: false,
 | 
			
		||||
      range: [],
 | 
			
		||||
    },
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // If the dPts is not long enough, throw error
 | 
			
		||||
| 
						 | 
				
			
			@ -101,8 +111,10 @@ export const getRollConf = (rollStr: string): RollConf => {
 | 
			
		|||
    rollConf.dieCount = parseInt(cwodParts[0] || '1');
 | 
			
		||||
    rollConf.dieSize = 10;
 | 
			
		||||
 | 
			
		||||
    // Use critScore to set the difficulty
 | 
			
		||||
    rollConf.critScore.on = true;
 | 
			
		||||
    // Use success to set the difficulty
 | 
			
		||||
    rollConf.success.on = true;
 | 
			
		||||
    rollConf.fail.on = true;
 | 
			
		||||
    addToRange('cwod', rollConf.fail.range, 1);
 | 
			
		||||
    const tempDifficulty = (cwodParts[1] ?? '').search(/\d/) === 0 ? cwodParts[1] : '';
 | 
			
		||||
    let afterDifficultyIdx = tempDifficulty.search(/[^\d]/);
 | 
			
		||||
    if (afterDifficultyIdx === -1) {
 | 
			
		||||
| 
						 | 
				
			
			@ -114,7 +126,7 @@ export const getRollConf = (rollStr: string): RollConf => {
 | 
			
		|||
      loopCountCheck();
 | 
			
		||||
 | 
			
		||||
      loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling cwod ${rollStr} | Parsing difficulty ${i}`);
 | 
			
		||||
      rollConf.critScore.range.push(i);
 | 
			
		||||
      rollConf.success.range.push(i);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Remove any garbage from the remains
 | 
			
		||||
| 
						 | 
				
			
			@ -275,7 +287,7 @@ export const getRollConf = (rollStr: string): RollConf => {
 | 
			
		|||
        case DiceOptions.RerollEqu:
 | 
			
		||||
          // Configure Reroll (this can happen multiple times)
 | 
			
		||||
          rollConf.reroll.on = true;
 | 
			
		||||
          !rollConf.reroll.nums.includes(tNum) && rollConf.reroll.nums.push(tNum);
 | 
			
		||||
          addToRange(tSep, rollConf.reroll.nums, tNum);
 | 
			
		||||
          break;
 | 
			
		||||
        case DiceOptions.RerollOnceGtr:
 | 
			
		||||
          rollConf.reroll.once = true;
 | 
			
		||||
| 
						 | 
				
			
			@ -283,12 +295,7 @@ export const getRollConf = (rollStr: string): RollConf => {
 | 
			
		|||
        case DiceOptions.RerollGtr:
 | 
			
		||||
          // 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);
 | 
			
		||||
          }
 | 
			
		||||
          gtrAddToRange(tSep, rollConf.reroll.nums, tNum, rollConf.dieSize);
 | 
			
		||||
          break;
 | 
			
		||||
        case DiceOptions.RerollOnceLt:
 | 
			
		||||
          rollConf.reroll.once = true;
 | 
			
		||||
| 
						 | 
				
			
			@ -296,64 +303,39 @@ export const getRollConf = (rollStr: string): RollConf => {
 | 
			
		|||
        case DiceOptions.RerollLt:
 | 
			
		||||
          // 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);
 | 
			
		||||
          }
 | 
			
		||||
          ltAddToRange(tSep, rollConf.reroll.nums, tNum);
 | 
			
		||||
          break;
 | 
			
		||||
        case DiceOptions.CritSuccess:
 | 
			
		||||
        case DiceOptions.CritSuccessEqu:
 | 
			
		||||
          // Configure CritScore for one number (this can happen multiple times)
 | 
			
		||||
          rollConf.critScore.on = true;
 | 
			
		||||
          !rollConf.critScore.range.includes(tNum) && rollConf.critScore.range.push(tNum);
 | 
			
		||||
          addToRange(tSep, rollConf.critScore.range, tNum);
 | 
			
		||||
          break;
 | 
			
		||||
        case DiceOptions.CritSuccessGtr:
 | 
			
		||||
          // 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);
 | 
			
		||||
          }
 | 
			
		||||
          gtrAddToRange(tSep, rollConf.critScore.range, tNum, rollConf.dieSize);
 | 
			
		||||
          break;
 | 
			
		||||
        case DiceOptions.CritSuccessLt:
 | 
			
		||||
          // 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);
 | 
			
		||||
          }
 | 
			
		||||
          ltAddToRange(tSep, rollConf.critScore.range, tNum);
 | 
			
		||||
          break;
 | 
			
		||||
        case DiceOptions.CritFail:
 | 
			
		||||
        case DiceOptions.CritFailEqu:
 | 
			
		||||
          // Configure CritFail for one number (this can happen multiple times)
 | 
			
		||||
          rollConf.critFail.on = true;
 | 
			
		||||
          !rollConf.critFail.range.includes(tNum) && rollConf.critFail.range.push(tNum);
 | 
			
		||||
          addToRange(tSep, rollConf.critFail.range, tNum);
 | 
			
		||||
          break;
 | 
			
		||||
        case DiceOptions.CritFailGtr:
 | 
			
		||||
          // 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);
 | 
			
		||||
          }
 | 
			
		||||
          gtrAddToRange(tSep, rollConf.critFail.range, tNum, rollConf.dieSize);
 | 
			
		||||
          break;
 | 
			
		||||
        case DiceOptions.CritFailLt:
 | 
			
		||||
          // 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);
 | 
			
		||||
          }
 | 
			
		||||
          ltAddToRange(tSep, rollConf.critFail.range, tNum);
 | 
			
		||||
          break;
 | 
			
		||||
        case DiceOptions.Exploding:
 | 
			
		||||
        case DiceOptions.ExplodeOnce:
 | 
			
		||||
| 
						 | 
				
			
			@ -363,7 +345,7 @@ export const getRollConf = (rollStr: string): RollConf => {
 | 
			
		|||
          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);
 | 
			
		||||
            addToRange(tSep, rollConf.exploding.nums, tNum);
 | 
			
		||||
          }
 | 
			
		||||
          break;
 | 
			
		||||
        case DiceOptions.ExplodingEqu:
 | 
			
		||||
| 
						 | 
				
			
			@ -372,7 +354,7 @@ export const getRollConf = (rollStr: string): RollConf => {
 | 
			
		|||
        case DiceOptions.CompoundingExplosionEqu:
 | 
			
		||||
          // Configure Exploding (this can happen multiple times)
 | 
			
		||||
          rollConf.exploding.on = true;
 | 
			
		||||
          !rollConf.exploding.nums.includes(tNum) && rollConf.exploding.nums.push(tNum);
 | 
			
		||||
          addToRange(tSep, rollConf.exploding.nums, tNum);
 | 
			
		||||
          break;
 | 
			
		||||
        case DiceOptions.ExplodingGtr:
 | 
			
		||||
        case DiceOptions.ExplodeOnceGtr:
 | 
			
		||||
| 
						 | 
				
			
			@ -380,12 +362,7 @@ export const getRollConf = (rollStr: string): RollConf => {
 | 
			
		|||
        case DiceOptions.CompoundingExplosionGtr:
 | 
			
		||||
          // 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);
 | 
			
		||||
          }
 | 
			
		||||
          gtrAddToRange(tSep, rollConf.exploding.nums, tNum, rollConf.dieSize);
 | 
			
		||||
          break;
 | 
			
		||||
        case DiceOptions.ExplodingLt:
 | 
			
		||||
        case DiceOptions.ExplodeOnceLt:
 | 
			
		||||
| 
						 | 
				
			
			@ -393,15 +370,16 @@ export const getRollConf = (rollStr: string): RollConf => {
 | 
			
		|||
        case DiceOptions.CompoundingExplosionLt:
 | 
			
		||||
          // 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);
 | 
			
		||||
          }
 | 
			
		||||
          ltAddToRange(tSep, rollConf.exploding.nums, tNum);
 | 
			
		||||
          break;
 | 
			
		||||
        case DiceOptions.Matching:
 | 
			
		||||
        case DiceOptions.MatchingTotal:
 | 
			
		||||
          rollConf.match.returnTotal = true;
 | 
			
		||||
        // falls through as mt functions the same as m in this context
 | 
			
		||||
        case DiceOptions.Matching:
 | 
			
		||||
          if (rollConf.match.on) {
 | 
			
		||||
            // Ensure we do not override existing settings
 | 
			
		||||
            throwDoubleSepError(tSep);
 | 
			
		||||
          }
 | 
			
		||||
          rollConf.match.on = true;
 | 
			
		||||
          if (afterNumIdx > 0) {
 | 
			
		||||
            // User gave a number to work with, save it
 | 
			
		||||
| 
						 | 
				
			
			@ -410,13 +388,52 @@ export const getRollConf = (rollStr: string): RollConf => {
 | 
			
		|||
          break;
 | 
			
		||||
        case DiceOptions.Sort:
 | 
			
		||||
        case DiceOptions.SortAsc:
 | 
			
		||||
          if (rollConf.sort.on) {
 | 
			
		||||
            // Ensure we do not override existing settings
 | 
			
		||||
            throwDoubleSepError(tSep);
 | 
			
		||||
          }
 | 
			
		||||
          rollConf.sort.on = true;
 | 
			
		||||
          rollConf.sort.direction = 'a';
 | 
			
		||||
          break;
 | 
			
		||||
        case DiceOptions.SortDesc:
 | 
			
		||||
          if (rollConf.sort.on) {
 | 
			
		||||
            // Ensure we do not override existing settings
 | 
			
		||||
            throwDoubleSepError(tSep);
 | 
			
		||||
          }
 | 
			
		||||
          rollConf.sort.on = true;
 | 
			
		||||
          rollConf.sort.direction = 'd';
 | 
			
		||||
          break;
 | 
			
		||||
        case DiceOptions.SuccessEqu:
 | 
			
		||||
          // Configure success (this can happen multiple times)
 | 
			
		||||
          rollConf.success.on = true;
 | 
			
		||||
          addToRange(tSep, rollConf.success.range, tNum);
 | 
			
		||||
          break;
 | 
			
		||||
        case DiceOptions.SuccessGtr:
 | 
			
		||||
          // Configure success for all numbers greater than or equal to tNum (this could happen multiple times, but why)
 | 
			
		||||
          rollConf.success.on = true;
 | 
			
		||||
          gtrAddToRange(tSep, rollConf.success.range, tNum, rollConf.dieSize);
 | 
			
		||||
          break;
 | 
			
		||||
        case DiceOptions.SuccessLt:
 | 
			
		||||
          // Configure success for all numbers less than or equal to tNum (this could happen multiple times, but why)
 | 
			
		||||
          rollConf.success.on = true;
 | 
			
		||||
          ltAddToRange(tSep, rollConf.success.range, tNum);
 | 
			
		||||
          break;
 | 
			
		||||
        case DiceOptions.Fail:
 | 
			
		||||
        case DiceOptions.FailEqu:
 | 
			
		||||
          // Configure fail (this can happen multiple times)
 | 
			
		||||
          rollConf.fail.on = true;
 | 
			
		||||
          addToRange(tSep, rollConf.fail.range, tNum);
 | 
			
		||||
          break;
 | 
			
		||||
        case DiceOptions.FailGtr:
 | 
			
		||||
          // Configure fail for all numbers greater than or equal to tNum (this could happen multiple times, but why)
 | 
			
		||||
          rollConf.fail.on = true;
 | 
			
		||||
          gtrAddToRange(tSep, rollConf.fail.range, tNum, rollConf.dieSize);
 | 
			
		||||
          break;
 | 
			
		||||
        case DiceOptions.FailLt:
 | 
			
		||||
          // Configure fail for all numbers less than or equal to tNum (this could happen multiple times, but why)
 | 
			
		||||
          rollConf.fail.on = true;
 | 
			
		||||
          ltAddToRange(tSep, rollConf.fail.range, tNum);
 | 
			
		||||
          break;
 | 
			
		||||
        default:
 | 
			
		||||
          // Throw error immediately if unknown op is encountered
 | 
			
		||||
          throw new Error(`UnknownOperation_${tSep}`);
 | 
			
		||||
| 
						 | 
				
			
			@ -442,9 +459,6 @@ export const getRollConf = (rollStr: string): RollConf => {
 | 
			
		|||
        case DiceOptions.CompoundingExplosionEqu:
 | 
			
		||||
          rollConf.exploding.compounding = true;
 | 
			
		||||
          break;
 | 
			
		||||
        case DiceOptions.MatchingTotal:
 | 
			
		||||
          rollConf.match.returnTotal = true;
 | 
			
		||||
          break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Finally slice off everything else parsed this loop
 | 
			
		||||
| 
						 | 
				
			
			@ -472,6 +486,10 @@ export const getRollConf = (rollStr: string): RollConf => {
 | 
			
		|||
    throw new Error('FormattingError_dk');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (rollConf.match.on && (rollConf.success.on || rollConf.fail.on)) {
 | 
			
		||||
    throw new Error('FormattingError_mtsf');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (rollConf.drop.on && rollConf.drop.count === 0) {
 | 
			
		||||
    throw new Error('NoZerosAllowed_drop');
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
import { RollConf, RollSet } from 'artigen/dice/dice.d.ts';
 | 
			
		||||
 | 
			
		||||
export const flagRoll = (rollConf: RollConf, rollSet: RollSet) => {
 | 
			
		||||
  // 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)) {
 | 
			
		||||
    rollSet.critHit = true;
 | 
			
		||||
  } else if (!rollConf.critScore.on) {
 | 
			
		||||
    rollSet.critHit = rollSet.roll === (rollConf.dPercent.on ? rollConf.dPercent.critVal : rollConf.dieSize);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // If critFail arg is on, check if the roll should be a fail, if its off, check if the roll matches 1
 | 
			
		||||
  if (rollConf.critFail.on && rollConf.critFail.range.includes(rollSet.roll)) {
 | 
			
		||||
    rollSet.critFail = true;
 | 
			
		||||
  } else if (!rollConf.critFail.on) {
 | 
			
		||||
    if (rollConf.type === 'fate') {
 | 
			
		||||
      rollSet.critFail = rollSet.roll === -1;
 | 
			
		||||
    } else {
 | 
			
		||||
      rollSet.critFail = rollSet.roll === (rollConf.dPercent.on ? 0 : 1);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // If success arg is on, check if roll should be successful
 | 
			
		||||
  if (rollConf.success.on && rollConf.success.range.includes(rollSet.roll)) {
 | 
			
		||||
    rollSet.success = true;
 | 
			
		||||
    rollSet.matchLabel = 'S';
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // If fail arg is on, check if roll should be failed
 | 
			
		||||
  if (rollConf.fail.on && rollConf.fail.range.includes(rollSet.roll)) {
 | 
			
		||||
    rollSet.fail = true;
 | 
			
		||||
    rollSet.matchLabel = 'F';
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
import { log, LogTypes as LT } from '@Log4Deno';
 | 
			
		||||
 | 
			
		||||
import { getLoopCount, loopCountCheck } from 'artigen/managers/loopManager.ts';
 | 
			
		||||
 | 
			
		||||
import { loggingEnabled } from 'artigen/utils/logFlag.ts';
 | 
			
		||||
 | 
			
		||||
// Add tNum to range
 | 
			
		||||
export const addToRange = (tSep: string, range: Array<number>, tNum: number) => {
 | 
			
		||||
  loggingEnabled && log(LT.LOG, `${getLoopCount()} addToRange on ${tSep} attempting to add: ${tNum}`);
 | 
			
		||||
  !range.includes(tNum) && range.push(tNum);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const internalAddMultipleToRange = (tSep: string, range: Array<number>, start: number, end: number) => {
 | 
			
		||||
  for (let i = start; i <= end; i++) {
 | 
			
		||||
    loopCountCheck();
 | 
			
		||||
    addToRange(tSep, range, i);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Add numbers less than or equal to tNum to range
 | 
			
		||||
export const ltAddToRange = (tSep: string, range: Array<number>, tNum: number) => internalAddMultipleToRange(tSep, range, 0, tNum);
 | 
			
		||||
 | 
			
		||||
// Add numbers greater than or equal to tNum to range
 | 
			
		||||
export const gtrAddToRange = (tSep: string, range: Array<number>, tNum: number, dieSize: number) => internalAddMultipleToRange(tSep, range, tNum, dieSize);
 | 
			
		||||
| 
						 | 
				
			
			@ -32,7 +32,18 @@ export const translateError = (solverError: Error): [string, string] => {
 | 
			
		|||
      errorMsg = `Formatting Error: \`${errorDetails}\` should only be specified once per roll, remove all but one and repeat roll`;
 | 
			
		||||
      break;
 | 
			
		||||
    case 'FormattingError':
 | 
			
		||||
      errorMsg = 'Formatting Error: Cannot use Keep and Drop at the same time, remove all but one and repeat roll';
 | 
			
		||||
      errorMsg = 'Formatting Error: ';
 | 
			
		||||
      switch (errorDetails) {
 | 
			
		||||
        case 'dk':
 | 
			
		||||
          errorMsg += 'Cannot use Keep and Drop at the same time, remove all but one and repeat roll';
 | 
			
		||||
          break;
 | 
			
		||||
        case 'mtsf':
 | 
			
		||||
          errorMsg += 'Cannot use Match with CWOD Dice, or the Success or Fail options, remove all but one and repeat roll';
 | 
			
		||||
          break;
 | 
			
		||||
        default:
 | 
			
		||||
          errorMsg += `Unhandled - ${errorDetails}`;
 | 
			
		||||
          break;
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    case 'NoMaxWithDash':
 | 
			
		||||
      errorMsg = 'Formatting Error: CritScore range specified without a maximum, remove - or add maximum to correct';
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -237,7 +237,10 @@ Any time a Compounding Explosion happens, the formatting on the die will still s
 | 
			
		|||
      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.`,
 | 
			
		||||
\`m\` will only add labels without modifying the results.
 | 
			
		||||
\`mt\` will add labels and will change the result of the roll to be equal to the number of labels that have been added.
 | 
			
		||||
 | 
			
		||||
**Notice:** Cannot be combined with Target Number/Successes or Target Failures`,
 | 
			
		||||
      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',
 | 
			
		||||
| 
						 | 
				
			
			@ -260,6 +263,43 @@ The basic \`m\` option will only add labels without modifying the results, where
 | 
			
		|||
      ],
 | 
			
		||||
    },
 | 
			
		||||
  ],
 | 
			
		||||
  [
 | 
			
		||||
    'target-success',
 | 
			
		||||
    {
 | 
			
		||||
      name: 'Target Number/Successes',
 | 
			
		||||
      description: `**Usage:** \`xdy=z\`, \`xdy<z\`, or \`xdy>z\`
 | 
			
		||||
\`z\` - Number to compare to for configuring the success target
 | 
			
		||||
 | 
			
		||||
Will set the result to be the number of successes.  When combined with the Target Failures option, the failures will be subtracted from the result.
 | 
			
		||||
 | 
			
		||||
**Notice:** Cannot be combined with the Dice Matching option`,
 | 
			
		||||
      example: [
 | 
			
		||||
        '`[[10d6=5]]` => [2 + 3 + 3 + __1__ + 2 + S:5 + S:5 + 2 + **6** + __1__, 2 Successes] = **__2__**',
 | 
			
		||||
        '`[[10d6>5]]` => [__1__ + S:5 + 4 + 3 + **S:6** + 2 + 3 + S:5 + S:5 + 3, 4 Successes] = 4',
 | 
			
		||||
        '`[[10d6<5]]` => [S:5 + S:2 + S:2 + **6** + S:2 + **6** + S:4 + S:4 + **6** + S:3, 7 Successes] = **7**',
 | 
			
		||||
        '`[[10d6>5f<2]]` => [__F:1__ + **S:6** + __F:1__ + S:5 + S:5 + S:5 + 4 + **S:6** + 4 + __F:1__, 5 Successes, 3 Fails] = **__2__**',
 | 
			
		||||
      ],
 | 
			
		||||
    },
 | 
			
		||||
  ],
 | 
			
		||||
  [
 | 
			
		||||
    'target-fail',
 | 
			
		||||
    {
 | 
			
		||||
      name: 'Target Failures',
 | 
			
		||||
      description: `**Usage:** \`xdyfz\`, \`xdyf=z\`, \`xdyf<z\`, or \`xdyf>z\`
 | 
			
		||||
\`z\` - Number to compare to for configuring the fail target
 | 
			
		||||
 | 
			
		||||
Will set the result to be the number of fails (as a negative number).  When combined with the Target Number/Successes option, the successes will be added to the result.
 | 
			
		||||
 | 
			
		||||
**Notice:** Cannot be combined with the Dice Matching option`,
 | 
			
		||||
      example: [
 | 
			
		||||
        '`[[10d6f5]]` => [**6** + 4 + **6** + 3 + F:5 + __1__ + **6** + F:5 + 4 + 2, 2 Fails] = **__-2__**',
 | 
			
		||||
        '`[[10d6f=5]]` => [**6** + 4 + **6** + 3 + F:5 + __1__ + **6** + F:5 + 4 + 2, 2 Fails] = **__-2__**',
 | 
			
		||||
        '`[[10d6f>5]]` => [2 + 3 + 3 + **F:6** + F:5 + 2 + __1__ + **F:6** + 2 + **F:6**, 4 Fails] = **__-4__**',
 | 
			
		||||
        '`[[10d6f<5]]` => [F:4 + F:5 + **6** + F:4 + F:3 + F:5 + F:4 + F:2 + F:4 + **6**, 8 Fails] = **-8**',
 | 
			
		||||
        '`[[10d6>5f<2]]` => [F:2 + 4 + __F:1__ + __F:1__ + __F:1__ + **S:6** + __F:1__ + **S:6** + __F:1__ + 3, 2 Successes, 6 Fails] = **__-4__**',
 | 
			
		||||
      ],
 | 
			
		||||
    },
 | 
			
		||||
  ],
 | 
			
		||||
]);
 | 
			
		||||
 | 
			
		||||
export const DiceOptionsHelpPages: HelpPage = {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,8 +37,8 @@ Rolls a Fate die, has 6 sides with values \`[-1, -1, 0, 0, 1, 1]\`.`,
 | 
			
		|||
Rolls a specified number of 10 sided dice and counts successful and failed rolls.`,
 | 
			
		||||
      example: [
 | 
			
		||||
        '`[[cwod]]` => [3, 0 Successes, 0 Fails] = 0',
 | 
			
		||||
        '`[[4cwod]]` => [**10** + __1__ + 9 + __1__, 1 Success, 2 Fails] = **__1__**',
 | 
			
		||||
        '`[[5cwod8]]` => [**10** + 2 + 5 + **8** + 4, 2 Successes, 0 Fails] = **2**',
 | 
			
		||||
        '`[[4cwod]]` =>  [8 + __F:1__ + **S:10** + **S:10**, 2 Successes, 1 Fail] = **__2__**',
 | 
			
		||||
        '`[[5cwod8]]` => [__F:1__ + S:8 + 6 + 7 + S:9, 2 Successes, 1 Fail] = __2__',
 | 
			
		||||
      ],
 | 
			
		||||
    },
 | 
			
		||||
  ],
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue