@dicelette/core 1.23.0 → 1.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -23,6 +23,7 @@ __export(index_exports, {
23
23
  COMMENT_REGEX: () => COMMENT_REGEX,
24
24
  DETECT_CRITICAL: () => DETECT_CRITICAL,
25
25
  DiceTypeError: () => DiceTypeError,
26
+ EXPLODING_SUCCESS_REGEX: () => EXPLODING_SUCCESS_REGEX,
26
27
  EmptyObjectError: () => EmptyObjectError,
27
28
  FormulaError: () => FormulaError,
28
29
  MaxGreater: () => MaxGreater,
@@ -31,9 +32,14 @@ __export(index_exports, {
31
32
  SIGN_REGEX: () => SIGN_REGEX,
32
33
  SIGN_REGEX_SPACE: () => SIGN_REGEX_SPACE,
33
34
  SYMBOL_DICE: () => SYMBOL_DICE,
35
+ SortOrder: () => SortOrder,
34
36
  TooManyDice: () => TooManyDice,
35
37
  TooManyStats: () => TooManyStats,
36
38
  calculator: () => calculator,
39
+ canComparisonFail: () => canComparisonFail,
40
+ canComparisonSucceed: () => canComparisonSucceed,
41
+ compareSignFormule: () => compareSignFormule,
42
+ countExplodingSuccesses: () => countExplodingSuccesses,
37
43
  createCriticalCustom: () => createCriticalCustom,
38
44
  diceRandomParse: () => diceRandomParse,
39
45
  diceTypeRandomParse: () => diceTypeRandomParse,
@@ -41,15 +47,29 @@ __export(index_exports, {
41
47
  evalCombinaison: () => evalCombinaison,
42
48
  evalOneCombinaison: () => evalOneCombinaison,
43
49
  evalStatsDice: () => evalStatsDice,
50
+ extractValuesFromOutput: () => extractValuesFromOutput,
51
+ fixParenthesis: () => fixParenthesis,
52
+ formatComment: () => formatComment,
44
53
  generateRandomStat: () => generateRandomStat,
45
54
  generateStatsDice: () => generateStatsDice,
55
+ getCompare: () => getCompare,
46
56
  getEngine: () => getEngine,
47
57
  getEngineId: () => getEngineId,
58
+ getModifier: () => getModifier,
59
+ getRollBounds: () => getRollBounds,
60
+ inverseSign: () => inverseSign,
48
61
  isNumber: () => isNumber,
62
+ isTrivialComparison: () => isTrivialComparison,
63
+ matchComparison: () => matchComparison,
64
+ normalizeExplodingSuccess: () => normalizeExplodingSuccess,
49
65
  randomInt: () => randomInt,
50
66
  replaceExpByRandom: () => replaceExpByRandom,
51
67
  replaceFormulaInDice: () => replaceFormulaInDice,
68
+ replaceInFormula: () => replaceInFormula,
69
+ replaceText: () => replaceText,
70
+ replaceUnwantedText: () => replaceUnwantedText,
52
71
  roll: () => roll,
72
+ rollCompare: () => rollCompare,
53
73
  standardizeDice: () => standardizeDice,
54
74
  templateSchema: () => templateSchema,
55
75
  testDiceRegistered: () => testDiceRegistered,
@@ -58,140 +78,178 @@ __export(index_exports, {
58
78
  });
59
79
  module.exports = __toCommonJS(index_exports);
60
80
 
61
- // src/dice.ts
62
- var import_rpg_dice_roller = require("@dice-roller/rpg-dice-roller");
81
+ // src/dice/calculator.ts
63
82
  var import_mathjs = require("mathjs");
64
- function isTrivialComparison(maxValue, minValue, compare) {
65
- const canSucceed = canComparisonSucceed(maxValue, compare, minValue);
66
- const canFail = canComparisonFail(maxValue, compare, minValue);
67
- return !canSucceed || !canFail;
68
- }
69
- function canComparisonFail(maxRollValue, compare, minRollValue = 1) {
70
- switch (compare.sign) {
71
- case ">":
72
- return minRollValue <= compare.value;
73
- // failure if roll <= value
74
- case ">=":
75
- return minRollValue < compare.value;
76
- // failure if roll < value
77
- case "<":
78
- return maxRollValue >= compare.value;
79
- // failure if roll >= value
80
- case "<=":
81
- return maxRollValue > compare.value;
82
- // failure if roll > value
83
- case "=":
84
- case "==":
85
- return minRollValue !== compare.value || maxRollValue !== compare.value;
86
- // can differ
87
- case "!=":
88
- return minRollValue <= compare.value && compare.value <= maxRollValue;
89
- // equality possible
90
- default:
91
- return true;
92
- }
93
- }
94
- function getCompare(dice, compareRegex, engine = import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto, pity) {
95
- if (dice.match(/((\{.*,(.*)+\}|([><=!]+\d+f))([><=]|!=)+\d+\}?)|\{(.*)(([><=]|!=)+).*\}/))
96
- return { dice, compare: void 0 };
97
- dice = dice.replace(SIGN_REGEX_SPACE, "");
98
- let compare;
99
- const calc = compareRegex[2];
100
- const sign = calc.match(/[+-/*^]/)?.[0];
101
- const compareSign = compareRegex[0].match(SIGN_REGEX)?.[0];
102
- if (sign) {
103
- const toCalc = calc.replace(SIGN_REGEX, "").replace(/\s/g, "").replace(/;(.*)/, "");
104
- const rCompare = rollCompare(toCalc, engine, pity);
105
- const total = (0, import_mathjs.evaluate)(rCompare.value.toString());
106
- dice = dice.replace(SIGN_REGEX_SPACE, `${compareSign}${total}`);
107
- compare = {
108
- sign: compareSign,
109
- value: total,
110
- originalDice: rCompare.dice,
111
- rollValue: rCompare.diceResult
112
- };
113
- } else {
114
- const rcompare = rollCompare(calc, engine, pity);
115
- compare = {
116
- sign: compareSign,
117
- value: rcompare.value,
118
- originalDice: rcompare.dice,
119
- rollValue: rcompare.diceResult
120
- };
121
- }
122
- return { dice, compare };
123
- }
124
- function rollCompare(value, engine = import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto, pity) {
125
- if (isNumber(value)) return { value: Number.parseInt(value, 10) };
126
- if (!value || typeof value === "string" && value.trim() === "") {
127
- return { value: 0, diceResult: value };
128
- }
129
- const rollComp = roll(value, engine, pity);
130
- if (!rollComp?.total) {
131
- try {
132
- return { value: (0, import_mathjs.evaluate)(value), diceResult: value };
133
- } catch (error) {
134
- return { value: 0, diceResult: value };
135
- }
136
- }
137
- return {
138
- dice: value,
139
- value: rollComp.total,
140
- diceResult: rollComp?.result
141
- };
142
- }
143
- function createCriticalCustom(dice, customCritical, template, engine = import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto) {
144
- const compareRegex = dice.match(SIGN_REGEX_SPACE);
145
- let customDice = dice;
146
- const compareValue = diceTypeRandomParse(customCritical.value, template, engine);
147
- if (compareValue.includes("$"))
148
- throw new DiceTypeError(compareValue, "createCriticalCustom");
149
- const comparaison = `${customCritical.sign}${compareValue}`;
150
- if (compareRegex) customDice = customDice.replace(SIGN_REGEX_SPACE, comparaison);
151
- else customDice += comparaison;
152
- return diceTypeRandomParse(customDice, template, engine);
153
- }
154
- function getModifier(dice) {
155
- const modifier = dice.matchAll(/(\+|-|%|\/|\^|\*|\*{2})(\d+)/gi);
156
- let modificator;
157
- for (const mod of modifier) {
158
- if (modificator) {
159
- const sign = modificator.sign;
160
- let value = modificator.value;
161
- if (sign) value = calculator(sign, value, Number.parseInt(mod[2], 10));
162
- modificator = {
163
- sign: mod[1],
164
- value
165
- };
166
- } else {
167
- modificator = {
168
- sign: mod[1],
169
- value: Number.parseInt(mod[2], 10)
170
- };
171
- }
172
- }
173
- return modificator;
83
+ function calculator(sign, value, total) {
84
+ if (sign === "^") sign = "**";
85
+ return (0, import_mathjs.evaluate)(`${total} ${sign} ${value}`);
174
86
  }
175
- function roll(dice, engine = import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto, pity) {
87
+
88
+ // src/dice/compare.ts
89
+ var import_rpg_dice_roller2 = require("@dice-roller/rpg-dice-roller");
90
+ var import_mathjs3 = require("mathjs");
91
+
92
+ // src/interfaces/constant.ts
93
+ var COMMENT_REGEX = /\s+(#|\/{2}|\[|\/\*)(?<comment>.*)/gi;
94
+ var SIGN_REGEX = /==|!=|(?<![!<>])>=|(?<![!<>])<=|(?<!!)(?<![<>])>|(?<!!)(?<![<>])<|(?<!!)(?<![<>])=/;
95
+ var SIGN_REGEX_SPACE = /(==|!=|(?<![!<>])>=|(?<![!<>])<=|(?<!!)(?<![<>])>|(?<!!)(?<![<>])<|(?<!!)(?<![<>])=)(\S+)/;
96
+ var SYMBOL_DICE = "&";
97
+ var DETECT_CRITICAL = /\{\*?c[fs]:([<>=]|!=)+(.+?)}/gim;
98
+ var OPTIONAL_COMMENT = /\s+(#|\/{2}|\[|\/\*)?(?<comment>.*)/gi;
99
+
100
+ // src/roll.ts
101
+ var import_rpg_dice_roller = require("@dice-roller/rpg-dice-roller");
102
+ var import_mathjs2 = require("mathjs");
103
+ function roll(dice, engine = import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto, pity, sort) {
104
+ if (sort === "none" /* None */) sort = void 0;
176
105
  dice = standardizeDice(replaceFormulaInDice(dice)).replace(/^\+/, "").replaceAll("=>", ">=").replaceAll("=<", "<=").trimStart();
177
106
  if (!dice.includes("d")) return void 0;
178
107
  dice = dice.replaceAll(DETECT_CRITICAL, "").trimEnd();
108
+ const explodingSuccess = normalizeExplodingSuccess(dice);
109
+ if (explodingSuccess) dice = explodingSuccess.dice;
110
+ let diceDisplay;
111
+ if (dice.includes(";")) {
112
+ const mainDice = dice.split(";")[0];
113
+ diceDisplay = explodingSuccess?.originalDice ?? mainDice;
114
+ } else diceDisplay = explodingSuccess?.originalDice ?? dice;
115
+ const curlyBulkMatch = dice.match(/^\{(\d+#.*)\}$/);
116
+ const isCurlyBulk = !!curlyBulkMatch;
117
+ let bulkContent = "";
118
+ if (isCurlyBulk) bulkContent = curlyBulkMatch[1];
179
119
  const compareRegex = dice.match(SIGN_REGEX_SPACE);
180
120
  let compare;
181
- if (dice.includes(";")) return sharedRolls(dice, engine);
121
+ if (dice.includes(";"))
122
+ return sharedRolls(dice, engine, pity, explodingSuccess, diceDisplay);
182
123
  dice = fixParenthesis(dice);
183
124
  const modificator = getModifier(dice);
184
- if (compareRegex) {
125
+ if (compareRegex && !isCurlyBulk) {
185
126
  const compareResult = getCompare(dice, compareRegex, engine, pity);
186
127
  dice = compareResult.dice;
187
128
  compare = compareResult.compare;
188
129
  }
189
- if (dice.match(/\d+?#(.*)/)) {
190
- const diceArray = dice.split("#");
130
+ const bulkProcessContent = isCurlyBulk ? bulkContent : dice;
131
+ if (bulkProcessContent.match(/\d+?#(.*)/)) {
132
+ const diceArray = bulkProcessContent.split("#");
191
133
  const numberOfDice = Number.parseInt(diceArray[0], 10);
192
- const diceToRoll = diceArray[1].replace(COMMENT_REGEX, "");
134
+ let diceToRoll = diceArray[1].replace(COMMENT_REGEX, "");
193
135
  const commentsMatch = diceArray[1].match(COMMENT_REGEX);
194
136
  const comments = commentsMatch ? commentsMatch[2] : void 0;
137
+ let curlyCompare;
138
+ if (isCurlyBulk) {
139
+ const curlyCompareRegex = diceToRoll.match(SIGN_REGEX_SPACE);
140
+ if (curlyCompareRegex) {
141
+ const compareSign = curlyCompareRegex[0].match(SIGN_REGEX)?.[0];
142
+ const compareValue = curlyCompareRegex[2];
143
+ if (compareSign && compareValue) {
144
+ curlyCompare = {
145
+ sign: compareSign,
146
+ value: Number.parseInt(compareValue, 10)
147
+ };
148
+ diceToRoll = diceToRoll.replace(SIGN_REGEX_SPACE, "");
149
+ }
150
+ }
151
+ }
152
+ if (sort) diceToRoll = `${diceToRoll}${sort}`;
153
+ const activeCompare = compare || curlyCompare || (explodingSuccess ? { sign: explodingSuccess.sign, value: explodingSuccess.value } : void 0);
154
+ let trivialComparisonDetected = false;
155
+ if (activeCompare) {
156
+ const results = [];
157
+ let successCount = 0;
158
+ const roller3 = new import_rpg_dice_roller.DiceRoller();
159
+ import_rpg_dice_roller.NumberGenerator.generator.engine = engine;
160
+ const formatOutput = (output, addStar) => {
161
+ const formatted = addStar && isCurlyBulk ? output.replace(
162
+ /\[([^\]]+)\]/,
163
+ (_m, content) => `[${content.split(",").map((d) => `${d.trim()}*`).join(", ")}]`
164
+ ) : output;
165
+ return curlyCompare ? formatted.replace(/^([^:]+):/, `$1${curlyCompare.sign}${curlyCompare.value}:`) : formatted;
166
+ };
167
+ for (let i = 0; i < numberOfDice; i++) {
168
+ try {
169
+ const individualRoll = roller3.roll(diceToRoll);
170
+ const rollInstance = Array.isArray(individualRoll) ? individualRoll[0] : individualRoll;
171
+ if (!trivialComparisonDetected && activeCompare) {
172
+ const { maxTotal, minTotal } = rollInstance;
173
+ trivialComparisonDetected = isTrivialComparison(
174
+ maxTotal,
175
+ minTotal,
176
+ activeCompare
177
+ );
178
+ }
179
+ const rollOutput = rollInstance.output;
180
+ if (explodingSuccess) {
181
+ const successesForRoll = countExplodingSuccesses(
182
+ rollInstance,
183
+ explodingSuccess.sign,
184
+ explodingSuccess.value
185
+ );
186
+ successCount += successesForRoll;
187
+ let formattedRollOutput = rollOutput.replace(
188
+ explodingSuccess.normalizedSegment,
189
+ explodingSuccess.originalSegment
190
+ ).replace(/=\s*-?\d+(?:\.\d+)?$/, `= ${successesForRoll}`);
191
+ formattedRollOutput = formatOutput(formattedRollOutput, false);
192
+ results.push(formattedRollOutput);
193
+ } else {
194
+ const rollTotal = rollInstance.total;
195
+ const isSuccess = (0, import_mathjs2.evaluate)(
196
+ `${rollTotal}${activeCompare.sign}${activeCompare.value}`
197
+ );
198
+ if (isSuccess) successCount++;
199
+ results.push(formatOutput(rollOutput, isSuccess));
200
+ }
201
+ } catch (error) {
202
+ throw new DiceTypeError(diceToRoll, "roll", error);
203
+ }
204
+ }
205
+ const signSource = explodingSuccess?.originalDice ?? diceDisplay ?? dice;
206
+ const explodingMatch = signSource.match(EXPLODING_SUCCESS_REGEX);
207
+ if (explodingMatch) {
208
+ const [, doubledSignRaw, valueStr] = explodingMatch;
209
+ let doubledSign;
210
+ if (doubledSignRaw === "!!==") doubledSign = "==";
211
+ else if (doubledSignRaw === "!==") doubledSign = "!==";
212
+ else doubledSign = doubledSignRaw.replace(/^!/, "");
213
+ const signMap = {
214
+ ">>": ">",
215
+ "<<": "<",
216
+ ">>=": ">=",
217
+ "<<=": "<=",
218
+ "==": "=",
219
+ "!==": "!=",
220
+ "!!==": "="
221
+ };
222
+ const mappedSign = signMap[doubledSign];
223
+ const mappedValue = Number.parseFloat(valueStr);
224
+ if (mappedSign && !Number.isNaN(mappedValue)) {
225
+ const resultsString = replaceUnwantedText(results.join("; "));
226
+ const flatValues = resultsString.split(";").flatMap((segment) => extractValuesFromOutput(segment));
227
+ if (mappedSign === "!=") {
228
+ const equalsCount = flatValues.filter((val) => val === mappedValue).length;
229
+ successCount = flatValues.length - equalsCount;
230
+ } else {
231
+ successCount = flatValues.filter(
232
+ (val) => matchComparison(mappedSign, val, mappedValue)
233
+ ).length;
234
+ }
235
+ }
236
+ }
237
+ if (compare && trivialComparisonDetected) compare.trivial = true;
238
+ const finalDice2 = isCurlyBulk ? `{${diceToRoll}${curlyCompare?.sign}${curlyCompare?.value}}` : diceToRoll;
239
+ const resultOutput2 = replaceUnwantedText(results.join("; "));
240
+ const finalTotal = explodingSuccess ? resultOutput2.split(";").flatMap((segment) => extractValuesFromOutput(segment)).filter(
241
+ (val) => matchComparison(explodingSuccess.sign, val, explodingSuccess.value)
242
+ ).length : successCount;
243
+ return {
244
+ dice: explodingSuccess ? diceDisplay : finalDice2,
245
+ result: resultOutput2,
246
+ comment: comments,
247
+ compare: isCurlyBulk ? void 0 : compare,
248
+ modifier: modificator,
249
+ total: finalTotal,
250
+ trivial: trivialComparisonDetected ? true : void 0
251
+ };
252
+ }
195
253
  const roller2 = new import_rpg_dice_roller.DiceRoller();
196
254
  import_rpg_dice_roller.NumberGenerator.generator.engine = engine;
197
255
  for (let i = 0; i < numberOfDice; i++) {
@@ -201,9 +259,10 @@ function roll(dice, engine = import_rpg_dice_roller.NumberGenerator.engines.node
201
259
  throw new DiceTypeError(diceToRoll, "roll", error);
202
260
  }
203
261
  }
262
+ const finalDice = isCurlyBulk ? `{${diceToRoll}}` : diceToRoll;
204
263
  return {
205
- dice: diceToRoll,
206
- result: roller2.output,
264
+ dice: finalDice,
265
+ result: replaceUnwantedText(roller2.output),
207
266
  comment: comments,
208
267
  compare: compare ? compare : void 0,
209
268
  modifier: modificator,
@@ -212,7 +271,8 @@ function roll(dice, engine = import_rpg_dice_roller.NumberGenerator.engines.node
212
271
  }
213
272
  const roller = new import_rpg_dice_roller.DiceRoller();
214
273
  import_rpg_dice_roller.NumberGenerator.generator.engine = engine;
215
- const diceWithoutComment = dice.replace(COMMENT_REGEX, "").trimEnd();
274
+ let diceWithoutComment = dice.replace(COMMENT_REGEX, "").trimEnd();
275
+ if (sort) diceWithoutComment = `${diceWithoutComment}${sort}`;
216
276
  let diceRoll;
217
277
  try {
218
278
  diceRoll = roller.roll(diceWithoutComment);
@@ -235,7 +295,7 @@ function roll(dice, engine = import_rpg_dice_roller.NumberGenerator.engines.node
235
295
  const maxPossible = currentRoll ? currentRoll.maxTotal : null;
236
296
  const isComparisonPossible = maxPossible === null || canComparisonSucceed(maxPossible, compare);
237
297
  if (isComparisonPossible) {
238
- let isFail = (0, import_mathjs.evaluate)(`${roller.total}${compare.sign}${compare.value}`);
298
+ let isFail = (0, import_mathjs2.evaluate)(`${roller.total}${compare.sign}${compare.value}`);
239
299
  if (!isFail) {
240
300
  const maxReroll = 100;
241
301
  while (!isFail && rerollCount < maxReroll) {
@@ -246,7 +306,7 @@ function roll(dice, engine = import_rpg_dice_roller.NumberGenerator.engines.node
246
306
  }
247
307
  rerollCount++;
248
308
  if (res && res.total !== void 0)
249
- isFail = (0, import_mathjs.evaluate)(`${res.total}${compare.sign}${compare.value}`);
309
+ isFail = (0, import_mathjs2.evaluate)(`${res.total}${compare.sign}${compare.value}`);
250
310
  }
251
311
  if (res) {
252
312
  return {
@@ -255,21 +315,262 @@ function roll(dice, engine = import_rpg_dice_roller.NumberGenerator.engines.node
255
315
  comment,
256
316
  compare,
257
317
  modifier: modificator,
258
- pityLogs: rerollCount
318
+ pityLogs: rerollCount,
319
+ trivial: res.trivial ?? (compare?.trivial ? true : void 0)
259
320
  };
260
321
  }
261
322
  }
262
- } else ;
323
+ }
324
+ }
325
+ let resultOutput = replaceUnwantedText(roller.output);
326
+ if (explodingSuccess) {
327
+ const successes = countExplodingSuccesses(
328
+ diceRoll,
329
+ explodingSuccess.sign,
330
+ explodingSuccess.value
331
+ );
332
+ resultOutput = resultOutput.replace(/=\s*-?\d+(?:\.\d+)?$/, `= ${successes}`).replace(explodingSuccess.normalizedSegment, explodingSuccess.originalSegment);
333
+ return {
334
+ dice: diceDisplay,
335
+ result: resultOutput,
336
+ comment,
337
+ compare: compare ? compare : void 0,
338
+ modifier: modificator,
339
+ total: successes,
340
+ pityLogs: rerollCount > 0 ? rerollCount : void 0,
341
+ trivial: compare?.trivial ? true : void 0
342
+ };
343
+ }
344
+ return {
345
+ dice,
346
+ result: resultOutput,
347
+ comment,
348
+ compare: compare ? compare : void 0,
349
+ modifier: modificator,
350
+ total: roller.total,
351
+ pityLogs: rerollCount > 0 ? rerollCount : void 0,
352
+ trivial: compare?.trivial ? true : void 0
353
+ };
354
+ }
355
+ function sharedRolls(dice, engine = import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto, pity, explodingSuccessMain, diceDisplay) {
356
+ if (!explodingSuccessMain) {
357
+ explodingSuccessMain = normalizeExplodingSuccess(dice.split(";")[0] ?? dice);
358
+ }
359
+ if (explodingSuccessMain) {
360
+ dice = dice.replace(explodingSuccessMain.originalSegment, "!");
361
+ }
362
+ if (dice.match(/\d+?#(.*?)/))
363
+ throw new DiceTypeError(
364
+ dice,
365
+ "noBulkRoll",
366
+ "bulk roll are not allowed in shared rolls"
367
+ );
368
+ const results = [];
369
+ const mainComment = /\s+#(?<comment>.*)/.exec(dice)?.groups?.comment?.trimEnd() ?? void 0;
370
+ const split = dice.split(";");
371
+ const displayDice = diceDisplay ?? explodingSuccessMain?.originalDice ?? split[0];
372
+ let diceMain = fixParenthesis(split[0]);
373
+ const commentsRegex = /\[(?<comments>.*?)\]/gi;
374
+ const comments = formatComment(diceMain);
375
+ const diceMainWithoutComments = diceMain.replace(commentsRegex, "").trim();
376
+ const toHideRegex = /\((?<dice>[^)]+)\)/;
377
+ const toHide = toHideRegex.exec(diceMainWithoutComments)?.groups;
378
+ let hidden = false;
379
+ if (toHide?.dice) {
380
+ diceMain = toHide.dice;
381
+ hidden = true;
382
+ } else if (toHide) {
383
+ diceMain = "1d1";
384
+ hidden = true;
385
+ } else {
386
+ diceMain = diceMainWithoutComments;
387
+ }
388
+ const rollBounds = getRollBounds(diceMain, engine);
389
+ let diceResult = roll(diceMain, engine, pity);
390
+ if (!diceResult || !diceResult.total) {
391
+ if (hidden) {
392
+ diceResult = roll(fixParenthesis(split[0]), engine, pity);
393
+ hidden = false;
394
+ } else return void 0;
395
+ }
396
+ if (!diceResult || !diceResult.total) return void 0;
397
+ if (explodingSuccessMain && diceResult.result) {
398
+ const values = extractValuesFromOutput(diceResult.result);
399
+ diceResult.total = values.filter(
400
+ (v) => matchComparison(explodingSuccessMain.sign, v, explodingSuccessMain.value)
401
+ ).length;
402
+ }
403
+ let aggregatedCompare = diceResult.compare;
404
+ let hasTrivialComparison = diceResult.compare?.trivial === true;
405
+ results.push(`\u203B ${comments}${diceResult.result}`);
406
+ let total = diceResult.total;
407
+ diceResult.comment = mainComment;
408
+ if (!total) {
409
+ return {
410
+ dice: displayDice,
411
+ result: results.join(";"),
412
+ comment: mainComment,
413
+ compare: aggregatedCompare,
414
+ modifier: diceResult.modifier,
415
+ total,
416
+ trivial: hasTrivialComparison ? true : void 0
417
+ };
418
+ }
419
+ for (let element of split.slice(1)) {
420
+ const comment = formatComment(element);
421
+ element = element.replaceAll(commentsRegex, "").replaceAll(OPTIONAL_COMMENT, "").trim();
422
+ let toRoll = element.replace(SYMBOL_DICE, `${diceResult.total}`);
423
+ const compareRegex = toRoll.match(SIGN_REGEX_SPACE);
424
+ if (compareRegex) {
425
+ const compareResult = compareSignFormule(
426
+ toRoll,
427
+ compareRegex,
428
+ element,
429
+ diceResult,
430
+ engine,
431
+ pity,
432
+ rollBounds
433
+ );
434
+ toRoll = compareResult.dice;
435
+ results.push(compareResult.results);
436
+ if (!aggregatedCompare && compareResult.compare)
437
+ aggregatedCompare = compareResult.compare;
438
+ if (compareResult.trivial) hasTrivialComparison = true;
439
+ } else {
440
+ const { formule, diceAll } = replaceText(
441
+ element,
442
+ diceResult.total,
443
+ diceResult.dice
444
+ );
445
+ try {
446
+ const evaluated = (0, import_mathjs2.evaluate)(toRoll);
447
+ results.push(`\u25C8 ${comment}${diceAll}: ${formule} = ${evaluated}`);
448
+ total += Number.parseInt(evaluated, 10);
449
+ } catch (error) {
450
+ const evaluated = roll(toRoll, engine, pity);
451
+ if (evaluated) {
452
+ results.push(
453
+ `\u25C8 ${comment}${diceAll}: ${evaluated.result.split(":").slice(1).join(":")}`
454
+ );
455
+ if (!aggregatedCompare && evaluated.compare)
456
+ aggregatedCompare = evaluated.compare;
457
+ if (evaluated.compare?.trivial) hasTrivialComparison = true;
458
+ } else results.push(`\u25C8 ${comment}${diceAll}: ${formule} = ${evaluated}`);
459
+ total += evaluated?.total ?? 0;
460
+ }
461
+ }
462
+ }
463
+ if (hidden)
464
+ results.shift();
465
+ return {
466
+ dice: displayDice,
467
+ result: results.join(";"),
468
+ comment: mainComment,
469
+ compare: hasTrivialComparison && aggregatedCompare ? { ...aggregatedCompare, trivial: true } : aggregatedCompare,
470
+ modifier: diceResult.modifier,
471
+ total,
472
+ trivial: hasTrivialComparison ? true : void 0
473
+ };
474
+ }
475
+ function replaceInFormula(element, diceResult, compareResult, res, engine = import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto, pity) {
476
+ const { formule, diceAll } = replaceText(
477
+ element,
478
+ diceResult.total ?? 0,
479
+ diceResult.dice
480
+ );
481
+ const validSign = res ? "\u2713" : "\u2715";
482
+ const invertedSign = res ? compareResult.compare.sign : inverseSign(compareResult.compare.sign);
483
+ let evaluateRoll;
484
+ try {
485
+ evaluateRoll = (0, import_mathjs2.evaluate)(compareResult.dice);
486
+ return `${validSign} ${diceAll}: ${formule} = ${evaluateRoll}${invertedSign}${compareResult.compare?.value}`;
487
+ } catch (error) {
488
+ const evaluateRoll2 = roll(compareResult.dice, engine, pity);
489
+ if (evaluateRoll2)
490
+ return `${validSign} ${diceAll}: ${evaluateRoll2.result.split(":").splice(1).join(":")}`;
491
+ return `${validSign} ${diceAll}: ${formule} = ${evaluateRoll2}${invertedSign}${compareResult.compare?.value}`;
492
+ }
493
+ }
494
+ function rollCompare(value, engine = import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto, pity) {
495
+ if (isNumber(value)) return { value: Number.parseInt(value, 10) };
496
+ if (!value || typeof value === "string" && value.trim() === "") {
497
+ return { value: 0, diceResult: value };
498
+ }
499
+ const rollComp = roll(value, engine, pity);
500
+ if (!rollComp?.total) {
501
+ try {
502
+ return { value: (0, import_mathjs2.evaluate)(value), diceResult: value };
503
+ } catch (error) {
504
+ return { value: 0, diceResult: value };
505
+ }
506
+ }
507
+ return {
508
+ dice: value,
509
+ value: rollComp.total,
510
+ diceResult: rollComp?.result
511
+ };
512
+ }
513
+
514
+ // src/dice/compare.ts
515
+ function isTrivialComparison(maxValue, minValue, compare) {
516
+ const canSucceed = canComparisonSucceed(maxValue, compare, minValue);
517
+ const canFail = canComparisonFail(maxValue, compare, minValue);
518
+ return !canSucceed || !canFail;
519
+ }
520
+ function canComparisonFail(maxRollValue, compare, minRollValue = 1) {
521
+ switch (compare.sign) {
522
+ case ">":
523
+ return minRollValue <= compare.value;
524
+ // failure if roll <= value
525
+ case ">=":
526
+ return minRollValue < compare.value;
527
+ // failure if roll < value
528
+ case "<":
529
+ return maxRollValue >= compare.value;
530
+ // failure if roll >= value
531
+ case "<=":
532
+ return maxRollValue > compare.value;
533
+ // failure if roll > value
534
+ case "=":
535
+ case "==":
536
+ return minRollValue !== compare.value || maxRollValue !== compare.value;
537
+ // can differ
538
+ case "!=":
539
+ return minRollValue <= compare.value && compare.value <= maxRollValue;
540
+ // equality possible
541
+ default:
542
+ return true;
543
+ }
544
+ }
545
+ function getCompare(dice, compareRegex, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto, pity) {
546
+ if (dice.match(/((\{.*,(.*)+\}|([><=!]+\d+f))([><=]|!=)+\d+\}?)|\{(.*)(([><=]|!=)+).*\}/))
547
+ return { dice, compare: void 0 };
548
+ dice = dice.replace(SIGN_REGEX_SPACE, "");
549
+ let compare;
550
+ const calc = compareRegex[2];
551
+ const sign = calc.match(/[+-/*^]/)?.[0];
552
+ const compareSign = compareRegex[0].match(SIGN_REGEX)?.[0];
553
+ if (sign) {
554
+ const toCalc = calc.replace(SIGN_REGEX, "").replace(/\s/g, "").replace(/;(.*)/, "");
555
+ const rCompare = rollCompare(toCalc, engine, pity);
556
+ const total = (0, import_mathjs3.evaluate)(rCompare.value.toString());
557
+ dice = dice.replace(SIGN_REGEX_SPACE, `${compareSign}${total}`);
558
+ compare = {
559
+ sign: compareSign,
560
+ value: total,
561
+ originalDice: rCompare.dice,
562
+ rollValue: rCompare.diceResult
563
+ };
564
+ } else {
565
+ const rcompare = rollCompare(calc, engine, pity);
566
+ compare = {
567
+ sign: compareSign,
568
+ value: rcompare.value,
569
+ originalDice: rcompare.dice,
570
+ rollValue: rcompare.diceResult
571
+ };
263
572
  }
264
- return {
265
- dice,
266
- result: roller.output,
267
- comment,
268
- compare: compare ? compare : void 0,
269
- modifier: modificator,
270
- total: roller.total,
271
- pityLogs: rerollCount > 0 ? rerollCount : void 0
272
- };
573
+ return { dice, compare };
273
574
  }
274
575
  function canComparisonSucceed(maxRollValue, compare, minRollValue) {
275
576
  switch (compare.sign) {
@@ -292,13 +593,62 @@ function canComparisonSucceed(maxRollValue, compare, minRollValue) {
292
593
  return true;
293
594
  }
294
595
  }
596
+
597
+ // src/dice/exploding.ts
598
+ var import_mathjs5 = require("mathjs");
599
+
600
+ // src/dice/signs.ts
601
+ var import_rpg_dice_roller3 = require("@dice-roller/rpg-dice-roller");
602
+ var import_mathjs4 = require("mathjs");
603
+
604
+ // src/dice/replace.ts
605
+ function replaceUnwantedText(dice) {
606
+ return dice.replaceAll(/[{}]/g, "").replaceAll(/s[ad]/gi, "");
607
+ }
295
608
  function fixParenthesis(dice) {
296
609
  const parenthesisRegex = /d\((\d+)\)/g;
297
610
  return dice.replaceAll(parenthesisRegex, (_match, p1) => `d${p1}`);
298
611
  }
299
- function calculator(sign, value, total) {
300
- if (sign === "^") sign = "**";
301
- return (0, import_mathjs.evaluate)(`${total} ${sign} ${value}`);
612
+ function replaceText(element, total, dice) {
613
+ return {
614
+ formule: element.replace(SYMBOL_DICE, `[${total}]`).replace(/%.*%/g, "").trim(),
615
+ diceAll: element.replace(SYMBOL_DICE, `[${dice.replace(COMMENT_REGEX, "")}]`).replace(/%.*%/g, "").trim()
616
+ };
617
+ }
618
+ function formatComment(dice) {
619
+ const commentsRegex = /\[(?<comments>.*?)\]/;
620
+ const commentsMatch = commentsRegex.exec(dice);
621
+ const comments = commentsMatch?.groups?.comments ? `${commentsMatch.groups.comments}` : "";
622
+ const diceWithoutBrackets = dice.replace(commentsRegex, "");
623
+ const optionalCommentsRegex = /\s+(#|\/\/)(?<comment>.*)/;
624
+ const optionalComments = optionalCommentsRegex.exec(diceWithoutBrackets);
625
+ const optional = optionalComments?.groups?.comment ? `${optionalComments.groups.comment.trim()}` : "";
626
+ let finalComment = "";
627
+ if (comments && optional) finalComment = `__${comments} ${optional}__ \u2014 `;
628
+ else if (comments) finalComment = `__${comments}__ \u2014 `;
629
+ else if (optional) finalComment = `__${optional}__ \u2014 `;
630
+ return finalComment;
631
+ }
632
+
633
+ // src/dice/signs.ts
634
+ function matchComparison(sign, val, value) {
635
+ switch (sign) {
636
+ case ">":
637
+ return val > value;
638
+ case ">=":
639
+ return val >= value;
640
+ case "<":
641
+ return val < value;
642
+ case "<=":
643
+ return val <= value;
644
+ case "=":
645
+ case "==":
646
+ return val === value;
647
+ case "!=":
648
+ return val !== value;
649
+ default:
650
+ return false;
651
+ }
302
652
  }
303
653
  function inverseSign(sign) {
304
654
  switch (sign) {
@@ -318,155 +668,169 @@ function inverseSign(sign) {
318
668
  return "==";
319
669
  }
320
670
  }
321
- function replaceInFormula(element, diceResult, compareResult, res, engine = import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto, pity) {
322
- const { formule, diceAll } = replaceText(
323
- element,
324
- diceResult.total ?? 0,
325
- diceResult.dice
326
- );
327
- const validSign = res ? "\u2713" : "\u2715";
328
- const invertedSign = res ? compareResult.compare.sign : inverseSign(compareResult.compare.sign);
329
- let evaluateRoll;
330
- try {
331
- evaluateRoll = (0, import_mathjs.evaluate)(compareResult.dice);
332
- return `${validSign} ${diceAll}: ${formule} = ${evaluateRoll}${invertedSign}${compareResult.compare?.value}`;
333
- } catch (error) {
334
- const evaluateRoll2 = roll(compareResult.dice, engine, pity);
335
- if (evaluateRoll2)
336
- return `${validSign} ${diceAll}: ${evaluateRoll2.result.split(":").splice(1).join(":")}`;
337
- return `${validSign} ${diceAll}: ${formule} = ${evaluateRoll2}${invertedSign}${compareResult.compare?.value}`;
338
- }
339
- }
340
- function compareSignFormule(toRoll, compareRegex, element, diceResult, engine = import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto, pity) {
671
+ function compareSignFormule(toRoll, compareRegex, element, diceResult, engine = import_rpg_dice_roller3.NumberGenerator.engines.nodeCrypto, pity, rollBounds) {
341
672
  let results = "";
673
+ let trivial = false;
342
674
  const compareResult = getCompare(toRoll, compareRegex, engine);
343
675
  const toCompare = `${compareResult.dice}${compareResult.compare?.sign}${compareResult.compare?.value}`;
344
676
  let res;
345
677
  try {
346
- res = (0, import_mathjs.evaluate)(toCompare);
678
+ res = (0, import_mathjs4.evaluate)(toCompare);
347
679
  } catch (error) {
348
680
  res = roll(toCompare, engine, pity);
349
681
  }
350
682
  if (typeof res === "boolean") {
683
+ const detectedTrivial = rollBounds && compareResult.compare ? isTrivialComparison(rollBounds.max, rollBounds.min, compareResult.compare) : false;
684
+ if (detectedTrivial && compareResult.compare) compareResult.compare.trivial = true;
685
+ if (detectedTrivial) trivial = true;
351
686
  results = replaceInFormula(element, diceResult, compareResult, res, engine, pity);
352
687
  } else if (res instanceof Object) {
353
688
  const diceResult2 = res;
354
689
  if (diceResult2.compare) {
355
- const toEvaluate = (0, import_mathjs.evaluate)(
690
+ const toEvaluate = (0, import_mathjs4.evaluate)(
356
691
  `${diceResult2.total}${diceResult2.compare.sign}${diceResult2.compare.value}`
357
692
  );
358
693
  const sign = toEvaluate ? "\u2713" : "\u2715";
359
694
  const invertedSign = toEvaluate ? diceResult2.compare.sign : inverseSign(diceResult2.compare.sign);
360
695
  const dice = replaceText(element, 0, diceResult2.dice).diceAll;
361
696
  results = `${sign} ${dice}: ${diceResult2.result.split(":").splice(1).join(":").trim()}${invertedSign}${diceResult2.compare.value}`;
697
+ if (diceResult2.compare.trivial) trivial = true;
362
698
  }
363
699
  }
364
- return { dice: compareResult.dice, results };
700
+ return { dice: compareResult.dice, results, compare: compareResult.compare, trivial };
365
701
  }
366
- function replaceText(element, total, dice) {
702
+
703
+ // src/dice/exploding.ts
704
+ var EXPLODING_SUCCESS_REGEX = /(!(?:>>=|<<=|!==|!!==|>>|<<|==|!=))(-?\d+(?:\.\d+)?)/;
705
+ function normalizeExplodingSuccess(dice) {
706
+ const match = dice.match(EXPLODING_SUCCESS_REGEX);
707
+ if (!match) return void 0;
708
+ const [, doubledSignRaw, valueStr] = match;
709
+ let doubledSign;
710
+ if (doubledSignRaw === "!!==") doubledSign = "==";
711
+ else if (doubledSignRaw === "!==") doubledSign = "!==";
712
+ else doubledSign = doubledSignRaw.replace(/^!/, "");
713
+ const signMap = {
714
+ ">>": ">",
715
+ "<<": "<",
716
+ ">>=": ">=",
717
+ "<<=": "<=",
718
+ "==": "=",
719
+ "!==": "!=",
720
+ "!!==": "="
721
+ };
722
+ const normalizedSign = signMap[doubledSign];
723
+ if (!normalizedSign) return void 0;
724
+ let parsedValue = Number.parseFloat(valueStr);
725
+ if (Number.isNaN(parsedValue)) {
726
+ try {
727
+ parsedValue = Number.parseFloat((0, import_mathjs5.evaluate)(valueStr));
728
+ } catch (_error) {
729
+ parsedValue = 0;
730
+ }
731
+ }
732
+ const normalizedSegment = "!";
733
+ const replacedDice = dice.replace(match[0], normalizedSegment);
367
734
  return {
368
- formule: element.replace(SYMBOL_DICE, `[${total}]`).replace(/%.*%/g, "").trim(),
369
- diceAll: element.replace(SYMBOL_DICE, `[${dice.replace(COMMENT_REGEX, "")}]`).replace(/%.*%/g, "").trim()
735
+ dice: replacedDice,
736
+ originalDice: dice,
737
+ sign: normalizedSign,
738
+ value: parsedValue,
739
+ normalizedSegment,
740
+ originalSegment: match[0]
370
741
  };
371
742
  }
372
- function formatComment(dice) {
373
- const commentsRegex = /\[(?<comments>.*?)\]/;
374
- const commentsMatch = commentsRegex.exec(dice);
375
- const comments = commentsMatch?.groups?.comments ? `${commentsMatch.groups.comments}` : "";
376
- const diceWithoutBrackets = dice.replace(commentsRegex, "");
377
- const optionalCommentsRegex = /\s+(#|\/\/)(?<comment>.*)/;
378
- const optionalComments = optionalCommentsRegex.exec(diceWithoutBrackets);
379
- const optional = optionalComments?.groups?.comment ? `${optionalComments.groups.comment.trim()}` : "";
380
- let finalComment = "";
381
- if (comments && optional) finalComment = `__${comments} ${optional}__ \u2014 `;
382
- else if (comments) finalComment = `__${comments}__ \u2014 `;
383
- else if (optional) finalComment = `__${optional}__ \u2014 `;
384
- return finalComment;
743
+ function countExplodingSuccesses(diceRoll, sign, value) {
744
+ const rollsArray = Array.isArray(diceRoll) ? diceRoll : [diceRoll];
745
+ const flatValues = [];
746
+ for (const dr of rollsArray) {
747
+ const groups = dr.rolls ?? [];
748
+ for (const group of groups) {
749
+ const innerRolls = group.rolls ?? [];
750
+ for (const roll2 of innerRolls) {
751
+ if (typeof roll2.value === "number") flatValues.push(roll2.value);
752
+ }
753
+ }
754
+ }
755
+ return flatValues.reduce(
756
+ (acc, current) => acc + (matchComparison(sign, current, value) ? 1 : 0),
757
+ 0
758
+ );
385
759
  }
386
- function sharedRolls(dice, engine = import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto, pity) {
387
- if (dice.match(/\d+?#(.*?)/))
388
- throw new DiceTypeError(
389
- dice,
390
- "noBulkRoll",
391
- "bulk roll are not allowed in shared rolls"
392
- );
393
- const results = [];
394
- const mainComment = /\s+#(?<comment>.*)/.exec(dice)?.groups?.comment?.trimEnd() ?? void 0;
395
- const split = dice.split(";");
396
- let diceMain = fixParenthesis(split[0]);
397
- const commentsRegex = /\[(?<comments>.*?)\]/gi;
398
- const comments = formatComment(diceMain);
399
- const diceMainWithoutComments = diceMain.replace(commentsRegex, "").trim();
400
- const toHideRegex = /\((?<dice>[^)]+)\)/;
401
- const toHide = toHideRegex.exec(diceMainWithoutComments)?.groups;
402
- let hidden = false;
403
- if (toHide?.dice) {
404
- diceMain = toHide.dice;
405
- hidden = true;
406
- } else if (toHide) {
407
- diceMain = "1d1";
408
- hidden = true;
409
- } else {
410
- diceMain = diceMainWithoutComments;
760
+
761
+ // src/dice/extract.ts
762
+ var import_rpg_dice_roller4 = require("@dice-roller/rpg-dice-roller");
763
+ function getModifier(dice) {
764
+ const modifier = dice.matchAll(/(\+|-|%|\/|\^|\*|\*{2})(\d+)/gi);
765
+ let modificator;
766
+ for (const mod of modifier) {
767
+ if (modificator) {
768
+ const sign = modificator.sign;
769
+ let value = modificator.value;
770
+ if (sign) value = calculator(sign, value, Number.parseInt(mod[2], 10));
771
+ modificator = {
772
+ sign: mod[1],
773
+ value
774
+ };
775
+ } else {
776
+ modificator = {
777
+ sign: mod[1],
778
+ value: Number.parseInt(mod[2], 10)
779
+ };
780
+ }
411
781
  }
412
- let diceResult = roll(diceMain, engine, pity);
413
- if (!diceResult || !diceResult.total) {
414
- if (hidden) {
415
- diceResult = roll(fixParenthesis(split[0]), engine, pity);
416
- hidden = false;
417
- } else return void 0;
782
+ return modificator;
783
+ }
784
+ function extractValuesFromOutput(output) {
785
+ const values = [];
786
+ const regex = /\[([^\]]+)\]/g;
787
+ let match;
788
+ while ((match = regex.exec(output)) !== null) {
789
+ const segmentValues = match[1].split(",").map((v) => Number.parseInt(v.replace(/[!*]/g, "").trim(), 10)).filter((v) => !Number.isNaN(v));
790
+ values.push(...segmentValues);
418
791
  }
419
- if (!diceResult || !diceResult.total) return void 0;
420
- results.push(`\u203B ${comments}${diceResult.result}`);
421
- let total = diceResult.total;
422
- diceResult.comment = mainComment;
423
- if (!total) return diceResult;
424
- for (let element of split.slice(1)) {
425
- const comment = formatComment(element);
426
- element = element.replaceAll(commentsRegex, "").replaceAll(OPTIONAL_COMMENT, "").trim();
427
- let toRoll = element.replace(SYMBOL_DICE, `${diceResult.total}`);
428
- const compareRegex = toRoll.match(SIGN_REGEX_SPACE);
429
- if (compareRegex) {
430
- const compareResult = compareSignFormule(
431
- toRoll,
432
- compareRegex,
433
- element,
434
- diceResult,
435
- engine
436
- );
437
- toRoll = compareResult.dice;
438
- results.push(compareResult.results);
439
- } else {
440
- const { formule, diceAll } = replaceText(
441
- element,
442
- diceResult.total,
443
- diceResult.dice
444
- );
445
- try {
446
- const evaluated = (0, import_mathjs.evaluate)(toRoll);
447
- results.push(`\u25C8 ${comment}${diceAll}: ${formule} = ${evaluated}`);
448
- total += Number.parseInt(evaluated, 10);
449
- } catch (error) {
450
- const evaluated = roll(toRoll, engine, pity);
451
- if (evaluated)
452
- results.push(
453
- `\u25C8 ${comment}${diceAll}: ${evaluated.result.split(":").slice(1).join(":")}`
454
- );
455
- else results.push(`\u25C8 ${comment}${diceAll}: ${formule} = ${evaluated}`);
456
- total += evaluated?.total ?? 0;
457
- }
792
+ return values;
793
+ }
794
+ function getRollBounds(dice, engine = import_rpg_dice_roller4.NumberGenerator.engines.nodeCrypto) {
795
+ try {
796
+ const roller = new import_rpg_dice_roller4.DiceRoller();
797
+ import_rpg_dice_roller4.NumberGenerator.generator.engine = engine;
798
+ const rollResult = roller.roll(dice);
799
+ const instance = Array.isArray(rollResult) ? rollResult[0] : rollResult;
800
+ const { minTotal, maxTotal } = instance;
801
+ return { min: minTotal, max: maxTotal };
802
+ } catch (error) {
803
+ }
804
+ return void 0;
805
+ }
806
+
807
+ // src/engine.ts
808
+ var import_rpg_dice_roller5 = require("@dice-roller/rpg-dice-roller");
809
+ function getEngineId(engine) {
810
+ if (engine === import_rpg_dice_roller5.NumberGenerator.engines.nodeCrypto) return "nodeCrypto";
811
+ if (engine === import_rpg_dice_roller5.NumberGenerator.engines.nativeMath) return "nativeMath";
812
+ if (engine === import_rpg_dice_roller5.NumberGenerator.engines.browserCrypto) return "browserCrypto";
813
+ try {
814
+ const e = engine;
815
+ if (e && typeof e === "object") {
816
+ if (typeof e.name === "string" && e.name) return e.name;
817
+ if (e.constructor?.name) return e.constructor.name;
458
818
  }
819
+ } catch {
820
+ }
821
+ return "unknown";
822
+ }
823
+ function getEngine(engine) {
824
+ switch (engine) {
825
+ case "nativeMath":
826
+ return import_rpg_dice_roller5.NumberGenerator.engines.nativeMath;
827
+ case "browserCrypto":
828
+ return import_rpg_dice_roller5.NumberGenerator.engines.browserCrypto;
829
+ case "nodeCrypto":
830
+ return import_rpg_dice_roller5.NumberGenerator.engines.nodeCrypto;
831
+ default:
832
+ return import_rpg_dice_roller5.NumberGenerator.engines.nativeMath;
459
833
  }
460
- if (hidden)
461
- results.shift();
462
- return {
463
- dice: diceMain,
464
- result: results.join(";"),
465
- comment: mainComment,
466
- compare: diceResult.compare,
467
- modifier: diceResult.modifier,
468
- total
469
- };
470
834
  }
471
835
 
472
836
  // src/errors.ts
@@ -534,13 +898,13 @@ var NoStatisticsError = class extends Error {
534
898
  }
535
899
  };
536
900
 
537
- // src/interfaces/constant.ts
538
- var COMMENT_REGEX = /\s+(#|\/{2}|\[|\/\*)(?<comment>.*)/gi;
539
- var SIGN_REGEX = /==|!=|(?<![!<>])>=|(?<![!<>])<=|(?<!!)(?<![<>])>|(?<!!)(?<![<>])<|(?<!!)(?<![<>])=/;
540
- var SIGN_REGEX_SPACE = /(==|!=|(?<![!<>])>=|(?<![!<>])<=|(?<!!)(?<![<>])>|(?<!!)(?<![<>])<|(?<!!)(?<![<>])=)(\S+)/;
541
- var SYMBOL_DICE = "&";
542
- var DETECT_CRITICAL = /\{\*?c[fs]:([<>=]|!=)+(.+?)}/gim;
543
- var OPTIONAL_COMMENT = /\s+(#|\/{2}|\[|\/\*)?(?<comment>.*)/gi;
901
+ // src/interfaces/index.ts
902
+ var SortOrder = /* @__PURE__ */ ((SortOrder2) => {
903
+ SortOrder2["Ascending"] = "sa";
904
+ SortOrder2["Descending"] = "sd";
905
+ SortOrder2["None"] = "none";
906
+ return SortOrder2;
907
+ })(SortOrder || {});
544
908
 
545
909
  // src/interfaces/zod.ts
546
910
  var import_zod = require("zod");
@@ -599,9 +963,9 @@ var templateSchema = import_zod.z.object({
599
963
  });
600
964
 
601
965
  // src/utils.ts
602
- var import_mathjs2 = require("mathjs");
966
+ var import_mathjs6 = require("mathjs");
603
967
  var import_uniformize = require("uniformize");
604
- var import_rpg_dice_roller2 = require("@dice-roller/rpg-dice-roller");
968
+ var import_rpg_dice_roller6 = require("@dice-roller/rpg-dice-roller");
605
969
  var import_random_js = require("random-js");
606
970
  function escapeRegex(string) {
607
971
  return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -636,11 +1000,29 @@ function generateStatsDice(originalDice, stats, dollarValue) {
636
1000
  result += outsideText.slice(lastIndex, tokenMatch.index);
637
1001
  const token = tokenMatch[0];
638
1002
  const tokenStd = token.standardize();
1003
+ const diceMatch = /^(\d*)d(.+)$/i.exec(tokenStd);
1004
+ if (diceMatch) {
1005
+ const diceCount = diceMatch[1] || "";
1006
+ const afterD = diceMatch[2];
1007
+ let foundStatAfterD = false;
1008
+ for (const key of statKeys) {
1009
+ const keyStd = key.standardize();
1010
+ if (afterD === keyStd) {
1011
+ result += `${diceCount}d${stats[key].toString()}`;
1012
+ foundStatAfterD = true;
1013
+ break;
1014
+ }
1015
+ }
1016
+ if (foundStatAfterD) {
1017
+ lastIndex = tokenRegex.lastIndex;
1018
+ continue;
1019
+ }
1020
+ }
639
1021
  let bestKey = null;
640
1022
  let bestScore = 0;
641
1023
  for (const key of statKeys) {
642
1024
  const keyStd = key.standardize();
643
- if (tokenStd === keyStd || keyStd.includes(tokenStd) || tokenStd.includes(keyStd) || keyStd.startsWith(tokenStd) || tokenStd.startsWith(keyStd)) {
1025
+ if (tokenStd === keyStd) {
644
1026
  bestKey = key;
645
1027
  bestScore = 1;
646
1028
  break;
@@ -674,7 +1056,7 @@ function replaceFormulaInDice(dice) {
674
1056
  if (match.groups?.formula) {
675
1057
  const formulae = match.groups.formula.replaceAll("{{", "").replaceAll("}}", "");
676
1058
  try {
677
- const result = (0, import_mathjs2.evaluate)(formulae);
1059
+ const result = (0, import_mathjs6.evaluate)(formulae);
678
1060
  modifiedDice = modifiedDice.replace(match.groups.formula, result.toString());
679
1061
  } catch (error) {
680
1062
  throw new FormulaError(match.groups.formula, "replaceFormulasInDice", error);
@@ -689,43 +1071,17 @@ function cleanedDice(dice) {
689
1071
  function isNumber(value) {
690
1072
  return value !== void 0 && (typeof value === "number" || !Number.isNaN(Number(value)) && typeof value === "string" && value.trim().length > 0);
691
1073
  }
692
- function replaceExpByRandom(dice, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto) {
1074
+ function replaceExpByRandom(dice, engine = import_rpg_dice_roller6.NumberGenerator.engines.nodeCrypto) {
693
1075
  const diceRegex = /\{exp( ?\|\| ?(?<default>\d+))?}/gi;
694
1076
  return dice.replace(diceRegex, (_match, _p1, _p2, _offset, _string, groups) => {
695
1077
  const defaultValue = groups?.default;
696
1078
  return defaultValue ?? randomInt(1, 999, engine).toString();
697
1079
  });
698
1080
  }
699
- function randomInt(min, max, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto, rng) {
1081
+ function randomInt(min, max, engine = import_rpg_dice_roller6.NumberGenerator.engines.nodeCrypto, rng) {
700
1082
  if (!rng) rng = new import_random_js.Random(engine || void 0);
701
1083
  return rng.integer(min, max);
702
1084
  }
703
- function getEngineId(engine) {
704
- if (engine === import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto) return "nodeCrypto";
705
- if (engine === import_rpg_dice_roller2.NumberGenerator.engines.nativeMath) return "nativeMath";
706
- if (engine === import_rpg_dice_roller2.NumberGenerator.engines.browserCrypto) return "browserCrypto";
707
- try {
708
- const e = engine;
709
- if (e && typeof e === "object") {
710
- if (typeof e.name === "string" && e.name) return e.name;
711
- if (e.constructor?.name) return e.constructor.name;
712
- }
713
- } catch {
714
- }
715
- return "unknown";
716
- }
717
- function getEngine(engine) {
718
- switch (engine) {
719
- case "nativeMath":
720
- return import_rpg_dice_roller2.NumberGenerator.engines.nativeMath;
721
- case "browserCrypto":
722
- return import_rpg_dice_roller2.NumberGenerator.engines.browserCrypto;
723
- case "nodeCrypto":
724
- return import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto;
725
- default:
726
- return import_rpg_dice_roller2.NumberGenerator.engines.nativeMath;
727
- }
728
- }
729
1085
  function levenshteinDistance(a, b) {
730
1086
  if (a === b) return 0;
731
1087
  const al = a.length;
@@ -753,13 +1109,24 @@ function similarityScore(a, b) {
753
1109
  const max = Math.max(la, lb);
754
1110
  return 1 - dist / max;
755
1111
  }
1112
+ function createCriticalCustom(dice, customCritical, template, engine = import_rpg_dice_roller6.NumberGenerator.engines.nodeCrypto) {
1113
+ const compareRegex = dice.match(SIGN_REGEX_SPACE);
1114
+ let customDice = dice;
1115
+ const compareValue = diceTypeRandomParse(customCritical.value, template, engine);
1116
+ if (compareValue.includes("$"))
1117
+ throw new DiceTypeError(compareValue, "createCriticalCustom");
1118
+ const comparaison = `${customCritical.sign}${compareValue}`;
1119
+ if (compareRegex) customDice = customDice.replace(SIGN_REGEX_SPACE, comparaison);
1120
+ else customDice += comparaison;
1121
+ return diceTypeRandomParse(customDice, template, engine);
1122
+ }
756
1123
 
757
1124
  // src/verify_template.ts
758
- var import_mathjs3 = require("mathjs");
1125
+ var import_mathjs7 = require("mathjs");
759
1126
  var import_random_js2 = require("random-js");
760
1127
  var import_uniformize2 = require("uniformize");
761
- var import_rpg_dice_roller3 = require("@dice-roller/rpg-dice-roller");
762
- function evalStatsDice(testDice, allStats, engine = import_rpg_dice_roller3.NumberGenerator.engines.nodeCrypto, pity) {
1128
+ var import_rpg_dice_roller7 = require("@dice-roller/rpg-dice-roller");
1129
+ function evalStatsDice(testDice, allStats, engine = import_rpg_dice_roller7.NumberGenerator.engines.nodeCrypto, pity) {
763
1130
  let dice = testDice.trimEnd();
764
1131
  if (allStats && Object.keys(allStats).length > 0) {
765
1132
  const names = Object.keys(allStats);
@@ -779,7 +1146,7 @@ function evalStatsDice(testDice, allStats, engine = import_rpg_dice_roller3.Numb
779
1146
  throw new DiceTypeError(dice, "evalStatsDice", error);
780
1147
  }
781
1148
  }
782
- function diceRandomParse(value, template, engine = import_rpg_dice_roller3.NumberGenerator.engines.nodeCrypto) {
1149
+ function diceRandomParse(value, template, engine = import_rpg_dice_roller7.NumberGenerator.engines.nodeCrypto) {
783
1150
  if (!template.statistics) return replaceFormulaInDice(value.standardize());
784
1151
  value = value.standardize();
785
1152
  const statNames = Object.keys(template.statistics);
@@ -801,7 +1168,7 @@ function diceRandomParse(value, template, engine = import_rpg_dice_roller3.Numbe
801
1168
  }
802
1169
  return replaceFormulaInDice(newDice);
803
1170
  }
804
- function diceTypeRandomParse(dice, template, engine = import_rpg_dice_roller3.NumberGenerator.engines.nodeCrypto) {
1171
+ function diceTypeRandomParse(dice, template, engine = import_rpg_dice_roller7.NumberGenerator.engines.nodeCrypto) {
805
1172
  dice = replaceExpByRandom(dice);
806
1173
  if (!template.statistics) return dice;
807
1174
  const firstStatNotcombinaison = Object.keys(template.statistics).find(
@@ -823,7 +1190,7 @@ function evalCombinaison(combinaison, stats) {
823
1190
  formula = formula.replace(regex, value.toString());
824
1191
  }
825
1192
  try {
826
- newStats[stat] = (0, import_mathjs3.evaluate)(formula);
1193
+ newStats[stat] = (0, import_mathjs7.evaluate)(formula);
827
1194
  } catch (error) {
828
1195
  throw new FormulaError(stat, "evalCombinaison", error);
829
1196
  }
@@ -837,7 +1204,7 @@ function evalOneCombinaison(combinaison, stats) {
837
1204
  formula = formula.replace(regex, value.toString());
838
1205
  }
839
1206
  try {
840
- return (0, import_mathjs3.evaluate)(formula);
1207
+ return (0, import_mathjs7.evaluate)(formula);
841
1208
  } catch (error) {
842
1209
  throw new FormulaError(combinaison, "evalOneCombinaison", error);
843
1210
  }
@@ -849,7 +1216,7 @@ function convertNumber(number) {
849
1216
  if (isNumber(number)) return Number.parseInt(number.toString(), 10);
850
1217
  return void 0;
851
1218
  }
852
- function verifyTemplateValue(template, verify = true, engine = import_rpg_dice_roller3.NumberGenerator.engines.nodeCrypto) {
1219
+ function verifyTemplateValue(template, verify = true, engine = import_rpg_dice_roller7.NumberGenerator.engines.nodeCrypto) {
853
1220
  const parsedTemplate = templateSchema.parse(template);
854
1221
  const { success, failure } = parsedTemplate.critical ?? {};
855
1222
  const criticicalVal = {
@@ -904,7 +1271,7 @@ function verifyTemplateValue(template, verify = true, engine = import_rpg_dice_r
904
1271
  testStatCombinaison(statistiqueTemplate, engine);
905
1272
  return statistiqueTemplate;
906
1273
  }
907
- function testDiceRegistered(template, engine = import_rpg_dice_roller3.NumberGenerator.engines.nodeCrypto) {
1274
+ function testDiceRegistered(template, engine = import_rpg_dice_roller7.NumberGenerator.engines.nodeCrypto) {
908
1275
  if (!template.damage) return;
909
1276
  if (Object.keys(template.damage).length === 0) throw new EmptyObjectError();
910
1277
  if (Object.keys(template.damage).length > 25) throw new TooManyDice();
@@ -920,7 +1287,7 @@ function testDiceRegistered(template, engine = import_rpg_dice_roller3.NumberGen
920
1287
  }
921
1288
  }
922
1289
  }
923
- function testStatCombinaison(template, engine = import_rpg_dice_roller3.NumberGenerator.engines.nodeCrypto) {
1290
+ function testStatCombinaison(template, engine = import_rpg_dice_roller7.NumberGenerator.engines.nodeCrypto) {
924
1291
  if (!template.statistics) return;
925
1292
  const onlycombinaisonStats = Object.fromEntries(
926
1293
  Object.entries(template.statistics).filter(
@@ -946,7 +1313,7 @@ function testStatCombinaison(template, engine = import_rpg_dice_roller3.NumberGe
946
1313
  formula = formula.replace(regex, randomStatValue.toString());
947
1314
  }
948
1315
  try {
949
- (0, import_mathjs3.evaluate)(formula);
1316
+ (0, import_mathjs7.evaluate)(formula);
950
1317
  } catch (e) {
951
1318
  error.push(stat);
952
1319
  }
@@ -954,9 +1321,9 @@ function testStatCombinaison(template, engine = import_rpg_dice_roller3.NumberGe
954
1321
  if (error.length > 0) throw new FormulaError(error.join(", "), "testStatCombinaison");
955
1322
  return;
956
1323
  }
957
- function generateRandomStat(total = 100, max, min, engine = import_rpg_dice_roller3.NumberGenerator.engines.nodeCrypto) {
1324
+ function generateRandomStat(total = 100, max, min, engine = import_rpg_dice_roller7.NumberGenerator.engines.nodeCrypto) {
958
1325
  let randomStatValue = total + 1;
959
- const random = new import_random_js2.Random(engine || import_rpg_dice_roller3.NumberGenerator.engines.nodeCrypto);
1326
+ const random = new import_random_js2.Random(engine || import_rpg_dice_roller7.NumberGenerator.engines.nodeCrypto);
960
1327
  while (randomStatValue >= total || randomStatValue === 0) {
961
1328
  if (max && min) randomStatValue = randomInt(min, max, engine, random);
962
1329
  else if (max) randomStatValue = randomInt(1, max, engine, random);
@@ -970,6 +1337,7 @@ function generateRandomStat(total = 100, max, min, engine = import_rpg_dice_roll
970
1337
  COMMENT_REGEX,
971
1338
  DETECT_CRITICAL,
972
1339
  DiceTypeError,
1340
+ EXPLODING_SUCCESS_REGEX,
973
1341
  EmptyObjectError,
974
1342
  FormulaError,
975
1343
  MaxGreater,
@@ -978,9 +1346,14 @@ function generateRandomStat(total = 100, max, min, engine = import_rpg_dice_roll
978
1346
  SIGN_REGEX,
979
1347
  SIGN_REGEX_SPACE,
980
1348
  SYMBOL_DICE,
1349
+ SortOrder,
981
1350
  TooManyDice,
982
1351
  TooManyStats,
983
1352
  calculator,
1353
+ canComparisonFail,
1354
+ canComparisonSucceed,
1355
+ compareSignFormule,
1356
+ countExplodingSuccesses,
984
1357
  createCriticalCustom,
985
1358
  diceRandomParse,
986
1359
  diceTypeRandomParse,
@@ -988,15 +1361,29 @@ function generateRandomStat(total = 100, max, min, engine = import_rpg_dice_roll
988
1361
  evalCombinaison,
989
1362
  evalOneCombinaison,
990
1363
  evalStatsDice,
1364
+ extractValuesFromOutput,
1365
+ fixParenthesis,
1366
+ formatComment,
991
1367
  generateRandomStat,
992
1368
  generateStatsDice,
1369
+ getCompare,
993
1370
  getEngine,
994
1371
  getEngineId,
1372
+ getModifier,
1373
+ getRollBounds,
1374
+ inverseSign,
995
1375
  isNumber,
1376
+ isTrivialComparison,
1377
+ matchComparison,
1378
+ normalizeExplodingSuccess,
996
1379
  randomInt,
997
1380
  replaceExpByRandom,
998
1381
  replaceFormulaInDice,
1382
+ replaceInFormula,
1383
+ replaceText,
1384
+ replaceUnwantedText,
999
1385
  roll,
1386
+ rollCompare,
1000
1387
  standardizeDice,
1001
1388
  templateSchema,
1002
1389
  testDiceRegistered,