285 lines
11 KiB
TypeScript
285 lines
11 KiB
TypeScript
import { log, LogTypes as LT } from '@Log4Deno';
|
|
|
|
import { ReturnData } from 'artigen/artigen.d.ts';
|
|
|
|
import { CountDetails, GroupConf, GroupResultFlags, RollDistributionMap, RollModifiers } from 'artigen/dice/dice.d.ts';
|
|
|
|
import { loopCountCheck } from 'artigen/managers/loopManager.ts';
|
|
|
|
import { tokenizeMath } from 'artigen/math/mathTokenizer.ts';
|
|
|
|
import { closeInternalGrp, internalGrpWrapRegex, mathSplitRegex, openInternalGrp } from 'artigen/utils/escape.ts';
|
|
import { loggingEnabled } from 'artigen/utils/logFlag.ts';
|
|
import { getMatchingGroupIdx, getMatchingInternalGrpIdx } from 'artigen/utils/parenBalance.ts';
|
|
import { getGroupConf } from 'artigen/dice/getGroupConf.ts';
|
|
import { compareOrigIdx, compareTotalRolls } from 'artigen/utils/sortFuncs.ts';
|
|
import { applyFlags } from 'artigen/utils/groupResultFlagger.ts';
|
|
|
|
export const handleGroup = (
|
|
groupParts: string[],
|
|
groupModifiers: string,
|
|
modifiers: RollModifiers,
|
|
previousResults: number[],
|
|
): [ReturnData[], CountDetails[], RollDistributionMap[]] => {
|
|
let retData: ReturnData;
|
|
const returnData: ReturnData[] = [];
|
|
const countDetails: CountDetails[] = [];
|
|
const rollDists: RollDistributionMap[] = [];
|
|
const groupConf: GroupConf = getGroupConf(groupModifiers, groupParts.join(''));
|
|
const prevGrpReturnData: ReturnData[] = [];
|
|
|
|
// Nested groups still exist, unwrap them
|
|
while (groupParts.includes('{')) {
|
|
loopCountCheck();
|
|
|
|
loggingEnabled && log(LT.LOG, `Handling Nested Groups | Current cmd: ${JSON.stringify(groupParts)}`);
|
|
|
|
const openIdx = groupParts.indexOf('{');
|
|
const closeIdx = getMatchingGroupIdx(groupParts, openIdx);
|
|
|
|
const currentGrp = groupParts.slice(openIdx + 1, closeIdx);
|
|
|
|
// Try to find and "eat" any modifiers from the next groupPart
|
|
let thisGrpMods = '';
|
|
const possibleMods = groupParts[closeIdx + 1]?.trim() ?? '';
|
|
if (possibleMods.match(/^[dk<>=f].*/g)) {
|
|
const items = groupParts[closeIdx + 1].split(mathSplitRegex).filter((x) => x);
|
|
thisGrpMods = items.shift() ?? '';
|
|
groupParts[closeIdx + 1] = items.join('');
|
|
}
|
|
|
|
const [tempData, tempCounts, tempDists] = handleGroup(currentGrp, thisGrpMods, modifiers, previousResults);
|
|
const data = tempData[0];
|
|
loggingEnabled && log(LT.LOG, `Solved Nested Group is back ${JSON.stringify(data)} | ${JSON.stringify(tempCounts)} ${JSON.stringify(tempDists)}`);
|
|
|
|
countDetails.push(...tempCounts);
|
|
rollDists.push(...tempDists);
|
|
|
|
// Merge result back into groupParts
|
|
groupParts.splice(openIdx, closeIdx - openIdx + 1, `${openInternalGrp}${prevGrpReturnData.length}${closeInternalGrp}`);
|
|
prevGrpReturnData.push(data);
|
|
}
|
|
|
|
// Handle the items in the groups
|
|
const commaParts = groupParts
|
|
.join('')
|
|
.split(',')
|
|
.filter((x) => x);
|
|
|
|
if (commaParts.length > 1) {
|
|
loggingEnabled && log(LT.LOG, `In multi-mode ${JSON.stringify(commaParts)} ${groupModifiers} ${JSON.stringify(groupConf)}`);
|
|
// Handle "normal operation" of group
|
|
const groupResults: ReturnData[] = [];
|
|
|
|
for (const part of commaParts) {
|
|
loopCountCheck();
|
|
|
|
loggingEnabled && log(LT.LOG, `Solving commaPart: ${part}`);
|
|
const [tempData, tempCounts, tempDists] = tokenizeMath(part, modifiers, previousResults, prevGrpReturnData);
|
|
const data = tempData[0];
|
|
|
|
loggingEnabled && log(LT.LOG, `Solved Math for Group is back ${JSON.stringify(data)} | ${JSON.stringify(tempCounts)} ${JSON.stringify(tempDists)}`);
|
|
|
|
countDetails.push(...tempCounts);
|
|
rollDists.push(...tempDists);
|
|
groupResults.push(data);
|
|
}
|
|
|
|
if (groupModifiers.trim()) {
|
|
// Handle the provided modifiers
|
|
const getTemplateFlags = (): GroupResultFlags => ({ dropped: false, success: false, failed: false });
|
|
|
|
// Assign original indexes
|
|
const resultFlags: GroupResultFlags[] = [];
|
|
groupResults.forEach((rd, idx) => {
|
|
rd.origIdx = idx;
|
|
resultFlags.push(getTemplateFlags());
|
|
});
|
|
|
|
// Handle drop/keep options
|
|
if (groupConf.drop.on || groupConf.keep.on || groupConf.dropHigh.on || groupConf.keepLow.on) {
|
|
groupResults.sort(compareTotalRolls);
|
|
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 > groupResults.length) {
|
|
dropCount = groupResults.length;
|
|
}
|
|
} else if (groupConf.keep.on) {
|
|
dropCount = groupResults.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) {
|
|
groupResults.reverse();
|
|
dropCount = groupConf.dropHigh.count;
|
|
if (dropCount > groupResults.length) {
|
|
dropCount = groupResults.length;
|
|
}
|
|
} else if (groupConf.keepLow.on) {
|
|
groupResults.reverse();
|
|
dropCount = groupResults.length - groupConf.keepLow.count;
|
|
if (dropCount < 0) {
|
|
dropCount = 0;
|
|
}
|
|
}
|
|
|
|
let i = 0;
|
|
while (dropCount > 0 && i < groupResults.length) {
|
|
loopCountCheck();
|
|
|
|
loggingEnabled && log(LT.LOG, `Handling group dropping | Dropping ${dropCount}`);
|
|
|
|
resultFlags[groupResults[i].origIdx ?? -1].dropped = true;
|
|
|
|
dropCount--;
|
|
i++;
|
|
}
|
|
|
|
groupResults.sort(compareOrigIdx);
|
|
}
|
|
|
|
let successCnt = 0;
|
|
let failCnt = 0;
|
|
if (groupConf.success.on || groupConf.fail.on) {
|
|
groupResults.forEach((rd, idx) => {
|
|
loopCountCheck();
|
|
|
|
if (!resultFlags[idx].dropped) {
|
|
if (
|
|
groupConf.success.on &&
|
|
(groupConf.success.range.includes(rd.rollTotal) ||
|
|
(groupConf.success.minValue !== null && rd.rollTotal >= groupConf.success.minValue) ||
|
|
(groupConf.success.maxValue !== null && rd.rollTotal <= groupConf.success.maxValue))
|
|
) {
|
|
successCnt++;
|
|
resultFlags[idx].success = true;
|
|
}
|
|
if (
|
|
groupConf.fail.on &&
|
|
(groupConf.fail.range.includes(rd.rollTotal) ||
|
|
(groupConf.fail.minValue !== null && rd.rollTotal >= groupConf.fail.minValue) ||
|
|
(groupConf.fail.maxValue !== null && rd.rollTotal <= groupConf.fail.maxValue))
|
|
) {
|
|
failCnt++;
|
|
resultFlags[idx].failed = true;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
loggingEnabled && log(LT.LOG, `Current Group Results: ${JSON.stringify(groupResults)}`);
|
|
loggingEnabled && log(LT.LOG, `Applying group flags: ${JSON.stringify(resultFlags)}`);
|
|
const data = groupResults.reduce(
|
|
(prev, cur, idx) => ({
|
|
rollTotal: resultFlags[idx].dropped ? prev.rollTotal : prev.rollTotal + cur.rollTotal,
|
|
rollPreFormat: '',
|
|
rollPostFormat: '',
|
|
rollDetails: `${prev.rollDetails}${prev.rollDetails ? ', ' : ''}${applyFlags(cur.rollDetails, resultFlags[idx])}`,
|
|
containsCrit: resultFlags[idx].dropped ? prev.containsCrit : prev.containsCrit || cur.containsCrit,
|
|
containsFail: resultFlags[idx].dropped ? prev.containsFail : prev.containsFail || cur.containsFail,
|
|
initConfig: `${prev.initConfig}${prev.initConfig ? ', ' : ''}${cur.initConfig}`,
|
|
isComplex: prev.isComplex || cur.isComplex,
|
|
}),
|
|
{
|
|
rollTotal: 0,
|
|
rollPreFormat: '',
|
|
rollPostFormat: '',
|
|
rollDetails: '',
|
|
containsCrit: false,
|
|
containsFail: false,
|
|
initConfig: '',
|
|
isComplex: false,
|
|
},
|
|
);
|
|
data.initConfig = `{${data.initConfig}}${groupModifiers.replaceAll(' ', '')}`;
|
|
|
|
if (groupConf.success.on || groupConf.fail.on) {
|
|
data.rollTotal = 0;
|
|
}
|
|
if (groupConf.success.on) {
|
|
data.rollTotal += successCnt;
|
|
data.rollDetails += `, ${successCnt} Success${successCnt !== 1 ? 'es' : ''}`;
|
|
}
|
|
if (groupConf.fail.on) {
|
|
data.rollTotal -= failCnt;
|
|
data.rollDetails += `, ${failCnt} Fail${failCnt !== 1 ? 's' : ''}`;
|
|
}
|
|
|
|
data.rollDetails = `{${data.rollDetails}}`;
|
|
retData = data;
|
|
} else {
|
|
// Sum mode
|
|
const data = groupResults.reduce(
|
|
(prev, cur) => ({
|
|
rollTotal: prev.rollTotal + cur.rollTotal,
|
|
rollPreFormat: '',
|
|
rollPostFormat: '',
|
|
rollDetails: `${prev.rollDetails}${prev.rollDetails ? ' + ' : ''}${cur.rollDetails}`,
|
|
containsCrit: prev.containsCrit || cur.containsCrit,
|
|
containsFail: prev.containsFail || cur.containsFail,
|
|
initConfig: `${prev.initConfig}${prev.initConfig ? ', ' : ''}${cur.initConfig}`,
|
|
isComplex: prev.isComplex || cur.isComplex,
|
|
}),
|
|
{
|
|
rollTotal: 0,
|
|
rollPreFormat: '',
|
|
rollPostFormat: '',
|
|
rollDetails: '',
|
|
containsCrit: false,
|
|
containsFail: false,
|
|
initConfig: '',
|
|
isComplex: false,
|
|
},
|
|
);
|
|
data.initConfig = `{${data.initConfig}}`;
|
|
data.rollDetails = `{${data.rollDetails}}`;
|
|
retData = data;
|
|
}
|
|
} else {
|
|
loggingEnabled && log(LT.LOG, `In single-mode ${JSON.stringify(commaParts)} ${groupModifiers} ${JSON.stringify(groupConf)}`);
|
|
const [tempData, tempCounts, tempDists] = tokenizeMath(
|
|
commaParts[0],
|
|
modifiers,
|
|
previousResults,
|
|
prevGrpReturnData,
|
|
groupModifiers.trim() ? groupConf : null,
|
|
);
|
|
const data = tempData[0];
|
|
|
|
loggingEnabled && log(LT.LOG, `Solved Math for Group is back ${JSON.stringify(data)} | ${JSON.stringify(tempCounts)} ${JSON.stringify(tempDists)}`);
|
|
|
|
countDetails.push(...tempCounts);
|
|
rollDists.push(...tempDists);
|
|
data.initConfig = `{${data.initConfig}}${groupModifiers.trim() ? groupModifiers.replaceAll(' ', '') : ''}`;
|
|
data.rollDetails = `{${data.rollDetails}}`;
|
|
retData = data;
|
|
}
|
|
|
|
// Handle merging back any nested groups to prevent an internalGrp marker from sneaking out
|
|
const initConf = retData.initConfig.split(internalGrpWrapRegex).filter((x) => x);
|
|
loggingEnabled && log(LT.LOG, `Split retData into initConf ${JSON.stringify(initConf)}`);
|
|
while (initConf.includes(openInternalGrp)) {
|
|
loopCountCheck();
|
|
|
|
const openIdx = initConf.indexOf(openInternalGrp);
|
|
const closeIdx = getMatchingInternalGrpIdx(initConf, openIdx);
|
|
|
|
// Take first groupResult out of array
|
|
const dataToMerge = prevGrpReturnData.shift();
|
|
|
|
// Replace the found pair with the nested initConfig and result
|
|
initConf.splice(openIdx, closeIdx - openIdx + 1, `${dataToMerge?.initConfig}`);
|
|
loggingEnabled && log(LT.LOG, `Current initConf state ${JSON.stringify(initConf)}`);
|
|
}
|
|
|
|
retData.initConfig = initConf.join('');
|
|
returnData.push(retData);
|
|
return [returnData, countDetails, rollDists];
|
|
};
|