FULLY SUPPORT GROUPS FOIASHFOIASHFOIASFDH :D

This commit is contained in:
Ean Milligan 2025-07-07 20:17:32 -04:00
parent ad0aef6c94
commit d989e9d473
4 changed files with 132 additions and 31 deletions

View File

@ -6,6 +6,7 @@ type RollType = '' | '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 {
type: RollType; type: RollType;
rollGrpIdx?: number;
origIdx: number; origIdx: number;
roll: number; roll: number;
size: number; size: number;

View File

@ -1,7 +1,6 @@
import { log, LogTypes as LT } from '@Log4Deno'; import { log, LogTypes as LT } from '@Log4Deno';
import { FormattedRoll, RollModifiers } from 'artigen/dice/dice.d.ts'; import { ExecutedRoll, FormattedRoll, RollModifiers } from 'artigen/dice/dice.d.ts';
import { executeRoll } from 'artigen/dice/executeRoll.ts';
import { loopCountCheck } from 'artigen/managers/loopManager.ts'; import { loopCountCheck } from 'artigen/managers/loopManager.ts';
@ -9,23 +8,21 @@ import { rollCounter } from 'artigen/utils/counter.ts';
import { loggingEnabled } from 'artigen/utils/logFlag.ts'; import { loggingEnabled } from 'artigen/utils/logFlag.ts';
import { createRollDistMap } from 'artigen/utils/rollDist.ts'; import { createRollDistMap } from 'artigen/utils/rollDist.ts';
// generateFormattedRoll(rollConf, modifiers) returns one SolvedStep // generateFormattedRoll(executedRoll, modifiers) returns one SolvedStep
// generateFormattedRoll handles creating and formatting the completed rolls into the SolvedStep format // generateFormattedRoll handles creating and formatting the completed rolls into the SolvedStep format
export const generateFormattedRoll = (rollConf: string, modifiers: RollModifiers): FormattedRoll => { export const formatRoll = (executedRoll: ExecutedRoll, modifiers: RollModifiers): FormattedRoll => {
let tempTotal = 0; let tempTotal = 0;
let tempDetails = '['; let tempDetails = '[';
let tempCrit = false; let tempCrit = false;
let tempFail = false; let tempFail = false;
let tempComplex = false; let tempComplex = false;
// Generate the roll, passing flags thru
const executedRoll = executeRoll(rollConf, modifiers);
// Loop thru all parts of the roll to document everything that was done to create the total roll // Loop thru all parts of the roll to document everything that was done to create the total roll
loggingEnabled && log(LT.LOG, `Formatting roll ${JSON.stringify(executedRoll)}`);
executedRoll.rollSet.forEach((e) => { executedRoll.rollSet.forEach((e) => {
loopCountCheck(); loopCountCheck();
loggingEnabled && log(LT.LOG, `Formatting roll ${rollConf} | ${JSON.stringify(e)}`); loggingEnabled && log(LT.LOG, `At ${JSON.stringify(e)}`);
let preFormat = ''; let preFormat = '';
let postFormat = ''; let postFormat = '';

View File

@ -208,26 +208,23 @@ export const handleGroup = (
} }
} else { } else {
loggingEnabled && log(LT.LOG, `In single-mode ${JSON.stringify(commaParts)} ${groupModifiers} ${JSON.stringify(groupConf)}`); loggingEnabled && log(LT.LOG, `In single-mode ${JSON.stringify(commaParts)} ${groupModifiers} ${JSON.stringify(groupConf)}`);
if (groupModifiers.trim()) { const [tempData, tempCounts, tempDists] = tokenizeMath(
// Handle special case where the group modifiers are applied across the dice rolled commaParts[0],
// ex from roll20 docs: {4d6+3d8}k4 - Roll 4 d6's and 3 d8's, out of those 7 dice the highest 4 are kept and summed up. modifiers,
// TODO AAAAAAAAAAAAAAAAA previousResults,
retData = <ReturnData> {}; prevGrpReturnData,
} else { groupModifiers.trim() ? groupConf : null,
// why did you put this in a group, that was entirely pointless );
loggingEnabled && log(LT.LOG, `Solving commaPart: ${commaParts[0]}`);
const [tempData, tempCounts, tempDists] = tokenizeMath(commaParts[0], modifiers, previousResults, prevGrpReturnData);
const data = tempData[0]; const data = tempData[0];
loggingEnabled && log(LT.LOG, `Solved Math for Group is back ${JSON.stringify(data)} | ${JSON.stringify(tempCounts)} ${JSON.stringify(tempDists)}`); loggingEnabled && log(LT.LOG, `Solved Math for Group is back ${JSON.stringify(data)} | ${JSON.stringify(tempCounts)} ${JSON.stringify(tempDists)}`);
countDetails.push(...tempCounts); countDetails.push(...tempCounts);
rollDists.push(...tempDists); rollDists.push(...tempDists);
data.initConfig = `{${data.initConfig}}`; data.initConfig = `{${data.initConfig}}${groupModifiers.trim() ? groupModifiers.replaceAll(' ', '') : ''}`;
data.rollDetails = `{${data.rollDetails}}`; data.rollDetails = `{${data.rollDetails}}`;
retData = data; retData = data;
} }
}
// Handle merging back any nested groups to prevent an internalGrp marker from sneaking out // Handle merging back any nested groups to prevent an internalGrp marker from sneaking out
const initConf = retData.initConfig.split(internalGrpWrapRegex).filter((x) => x); const initConf = retData.initConfig.split(internalGrpWrapRegex).filter((x) => x);

View File

@ -4,8 +4,8 @@ import { ReturnData } from 'artigen/artigen.d.ts';
import { MathConf, SolvedStep } from 'artigen/math/math.d.ts'; import { MathConf, SolvedStep } from 'artigen/math/math.d.ts';
import { CountDetails, RollDistributionMap, RollModifiers } from 'artigen/dice/dice.d.ts'; import { CountDetails, ExecutedRoll, GroupConf, RollDistributionMap, RollModifiers, RollSet } from 'artigen/dice/dice.d.ts';
import { generateFormattedRoll } from 'artigen/dice/generateFormattedRoll.ts'; import { formatRoll } from 'artigen/dice/generateFormattedRoll.ts';
import { loopCountCheck } from 'artigen/managers/loopManager.ts'; import { loopCountCheck } from 'artigen/managers/loopManager.ts';
@ -15,6 +15,8 @@ import { closeInternalGrp, cmdSplitRegex, internalWrapRegex, mathSplitRegex, ope
import { legalMathOperators } from 'artigen/utils/legalMath.ts'; import { legalMathOperators } from 'artigen/utils/legalMath.ts';
import { loggingEnabled } from 'artigen/utils/logFlag.ts'; import { loggingEnabled } from 'artigen/utils/logFlag.ts';
import { assertParenBalance } from 'artigen/utils/parenBalance.ts'; import { assertParenBalance } from 'artigen/utils/parenBalance.ts';
import { executeRoll } from 'artigen/dice/executeRoll.ts';
import { compareOrigIdx, compareRolls } from 'artigen/utils/sortFuncs.ts';
// minusOps are operators that will cause a negative sign to collapse into a number (in cases like + - 1) // minusOps are operators that will cause a negative sign to collapse into a number (in cases like + - 1)
const minusOps = ['(', '^', '**', '*', '/', '%', '+', '-']; const minusOps = ['(', '^', '**', '*', '/', '%', '+', '-'];
@ -25,9 +27,11 @@ export const tokenizeMath = (
modifiers: RollModifiers, modifiers: RollModifiers,
previousResults: number[], previousResults: number[],
groupResults: ReturnData[], groupResults: ReturnData[],
groupConf: GroupConf | null = null,
): [ReturnData[], CountDetails[], RollDistributionMap[]] => { ): [ReturnData[], CountDetails[], RollDistributionMap[]] => {
const countDetails: CountDetails[] = []; const countDetails: CountDetails[] = [];
const rollDists: RollDistributionMap[] = []; const rollDists: RollDistributionMap[] = [];
const executedRolls: Map<number, ExecutedRoll> = new Map();
loggingEnabled && log(LT.LOG, `Parsing roll ${cmd} | ${JSON.stringify(modifiers)} | ${JSON.stringify(previousResults)}`); loggingEnabled && log(LT.LOG, `Parsing roll ${cmd} | ${JSON.stringify(modifiers)} | ${JSON.stringify(previousResults)}`);
@ -177,11 +181,16 @@ export const tokenizeMath = (
} }
} else if (![...allOps, ...legalMathOperators].includes(curMathConfStr)) { } else if (![...allOps, ...legalMathOperators].includes(curMathConfStr)) {
// If nothing else has handled it by now, try it as a roll // If nothing else has handled it by now, try it as a roll
const formattedRoll = generateFormattedRoll(curMathConfStr, modifiers); const executedRoll = executeRoll(curMathConfStr, modifiers);
if (groupConf) {
executedRolls.set(i, executedRoll);
} else {
const formattedRoll = formatRoll(executedRoll, modifiers);
mathConf[i] = formattedRoll.solvedStep; mathConf[i] = formattedRoll.solvedStep;
countDetails.push(formattedRoll.countDetails); countDetails.push(formattedRoll.countDetails);
rollDists.push(formattedRoll.rollDistributions); rollDists.push(formattedRoll.rollDistributions);
} }
}
// Identify if we are in a state where the current number is a negative number // Identify if we are in a state where the current number is a negative number
if (mathConf[i - 1] === '-' && ((!mathConf[i - 2] && mathConf[i - 2] !== 0) || minusOps.includes(<string> mathConf[i - 2]))) { if (mathConf[i - 1] === '-' && ((!mathConf[i - 2] && mathConf[i - 2] !== 0) || minusOps.includes(<string> mathConf[i - 2]))) {
@ -203,6 +212,103 @@ export const tokenizeMath = (
} }
} }
// Handle applying the group config
if (groupConf) {
loggingEnabled && log(LT.LOG, `Applying groupConf to executedRolls | ${JSON.stringify(groupConf)} ${JSON.stringify(executedRolls.entries().toArray())}`);
// Merge all rollSets into one array, adding the idx into each rollSet to allow separating them back out
const allRollSets: RollSet[] = [];
const executedRollArr = executedRolls.entries().toArray();
executedRollArr.forEach(([rollGroupIdx, executedRoll]) => {
executedRoll.rollSet.forEach((roll) => (roll.rollGrpIdx = rollGroupIdx));
allRollSets.push(...executedRoll.rollSet);
});
loggingEnabled && log(LT.LOG, `raw rollSets: ${JSON.stringify(allRollSets)}`);
// Handle drop or keep operations
if (groupConf.drop.on || groupConf.keep.on || groupConf.dropHigh.on || groupConf.keepLow.on) {
allRollSets.sort(compareRolls);
let dropCount = 0;
// For normal drop and keep, simple subtraction is enough to determine how many to drop
// Protections are in to prevent the dropCount from going below 0 or more than the valid rolls to drop
if (groupConf.drop.on) {
dropCount = groupConf.drop.count;
if (dropCount > allRollSets.length) {
dropCount = allRollSets.length;
}
} else if (groupConf.keep.on) {
dropCount = allRollSets.length - groupConf.keep.count;
if (dropCount < 0) {
dropCount = 0;
}
} // For inverted drop and keep, order must be flipped to greatest to least before the simple subtraction can determine how many to drop
// Protections are in to prevent the dropCount from going below 0 or more than the valid rolls to drop
else if (groupConf.dropHigh.on) {
allRollSets.reverse();
dropCount = groupConf.dropHigh.count;
if (dropCount > allRollSets.length) {
dropCount = allRollSets.length;
}
} else if (groupConf.keepLow.on) {
allRollSets.reverse();
dropCount = allRollSets.length - groupConf.keepLow.count;
if (dropCount < 0) {
dropCount = 0;
}
}
let i = 0;
while (dropCount > 0 && i < allRollSets.length) {
loopCountCheck();
loggingEnabled && log(LT.LOG, `Handling group dropping | Dropping ${dropCount}, looking at ${JSON.stringify(allRollSets[i])}`);
if (!allRollSets[i].dropped && !allRollSets[i].rerolled) {
allRollSets[i].dropped = true;
allRollSets[i].success = false;
allRollSets[i].fail = false;
allRollSets[i].matchLabel = '';
dropCount--;
}
i++;
}
allRollSets.sort(compareOrigIdx);
}
// Handle marking new successes/fails
if (groupConf.success.on || groupConf.fail.on) {
allRollSets.forEach((rs) => {
loopCountCheck();
if (!rs.dropped && !rs.rerolled) {
if (groupConf.success.on && groupConf.success.range.includes(rs.roll)) {
rs.success = true;
rs.matchLabel = 'S';
}
if (groupConf.fail.on && groupConf.fail.range.includes(rs.roll)) {
rs.fail = true;
rs.matchLabel = 'F';
}
}
});
}
// Handle separating the rollSets back out, recalculating the success/fail count, assigning them to the correct mathConf slots
executedRollArr.forEach(([rollGroupIdx, executedRoll]) => {
// Update flags on executedRoll
executedRoll.countSuccessOverride = executedRoll.countSuccessOverride || groupConf.success.on;
executedRoll.countFailOverride = executedRoll.countFailOverride || groupConf.fail.on;
executedRoll.rollSet = allRollSets.filter((rs) => rs.rollGrpIdx === rollGroupIdx);
const formattedRoll = formatRoll(executedRoll, modifiers);
mathConf[rollGroupIdx] = formattedRoll.solvedStep;
countDetails.push(formattedRoll.countDetails);
rollDists.push(formattedRoll.rollDistributions);
});
}
// Now that mathConf is parsed, send it into the solver // Now that mathConf is parsed, send it into the solver
const tempSolved = mathSolver(mathConf); const tempSolved = mathSolver(mathConf);