@dicelette/core 1.23.1 → 1.24.1

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.mjs CHANGED
@@ -1,6 +1,341 @@
1
- // src/dice.ts
2
- import { DiceRoller, NumberGenerator } from "@dice-roller/rpg-dice-roller";
1
+ // src/engine.ts
2
+ import { NumberGenerator } from "@dice-roller/rpg-dice-roller";
3
+ function getEngineId(engine) {
4
+ if (engine === NumberGenerator.engines.nodeCrypto) return "nodeCrypto";
5
+ if (engine === NumberGenerator.engines.nativeMath) return "nativeMath";
6
+ if (engine === NumberGenerator.engines.browserCrypto) return "browserCrypto";
7
+ try {
8
+ const e = engine;
9
+ if (e && typeof e === "object") {
10
+ if (typeof e.name === "string" && e.name) return e.name;
11
+ if (e.constructor?.name) return e.constructor.name;
12
+ }
13
+ } catch {
14
+ }
15
+ return "unknown";
16
+ }
17
+ function getEngine(engine) {
18
+ switch (engine) {
19
+ case "nativeMath":
20
+ return NumberGenerator.engines.nativeMath;
21
+ case "browserCrypto":
22
+ return NumberGenerator.engines.browserCrypto;
23
+ case "nodeCrypto":
24
+ return NumberGenerator.engines.nodeCrypto;
25
+ default:
26
+ return NumberGenerator.engines.nativeMath;
27
+ }
28
+ }
29
+
30
+ // src/errors.ts
31
+ var DiceTypeError = class extends Error {
32
+ dice;
33
+ cause;
34
+ method;
35
+ constructor(dice, cause, method) {
36
+ super(dice);
37
+ this.name = "Invalid_Dice_Type";
38
+ this.dice = dice;
39
+ this.cause = cause;
40
+ this.method = method;
41
+ }
42
+ };
43
+ var FormulaError = class extends Error {
44
+ formula;
45
+ cause;
46
+ method;
47
+ constructor(formula, cause, method) {
48
+ super(formula);
49
+ this.name = "Invalid_Formula";
50
+ this.formula = formula;
51
+ this.cause = cause;
52
+ this.method = method;
53
+ }
54
+ };
55
+ var MaxGreater = class extends Error {
56
+ name;
57
+ value;
58
+ max;
59
+ constructor(value, max) {
60
+ super(value.toString());
61
+ this.name = "Max_Greater";
62
+ this.value = value;
63
+ this.max = max;
64
+ }
65
+ };
66
+ var EmptyObjectError = class extends Error {
67
+ name;
68
+ constructor() {
69
+ super();
70
+ this.name = "Empty_Object";
71
+ }
72
+ };
73
+ var TooManyDice = class extends Error {
74
+ name;
75
+ constructor() {
76
+ super();
77
+ this.name = "Too_Many_Dice";
78
+ }
79
+ };
80
+ var TooManyStats = class extends Error {
81
+ name;
82
+ constructor() {
83
+ super();
84
+ this.name = "Too_Many_Stats";
85
+ }
86
+ };
87
+ var NoStatisticsError = class extends Error {
88
+ name;
89
+ constructor() {
90
+ super();
91
+ this.name = "No_Statistics";
92
+ }
93
+ };
94
+
95
+ // src/interfaces/index.ts
96
+ var SortOrder = /* @__PURE__ */ ((SortOrder2) => {
97
+ SortOrder2["Ascending"] = "sa";
98
+ SortOrder2["Descending"] = "sd";
99
+ SortOrder2["None"] = "none";
100
+ return SortOrder2;
101
+ })(SortOrder || {});
102
+
103
+ // src/interfaces/constant.ts
104
+ var COMMENT_REGEX = /\s+(#|\/{2}|\[|\/\*)(?<comment>.*)/gi;
105
+ var SIGN_REGEX = /==|!=|(?<![!<>])>=|(?<![!<>])<=|(?<!!)(?<![<>])>|(?<!!)(?<![<>])<|(?<!!)(?<![<>])=/;
106
+ var SIGN_REGEX_SPACE = /(==|!=|(?<![!<>])>=|(?<![!<>])<=|(?<!!)(?<![<>])>|(?<!!)(?<![<>])<|(?<!!)(?<![<>])=)(\S+)/;
107
+ var SYMBOL_DICE = "&";
108
+ var DETECT_CRITICAL = /\{\*?c[fs]:([<>=]|!=)+(.+?)}/gim;
109
+ var OPTIONAL_COMMENT = /\s+(#|\/{2}|\[|\/\*)?(?<comment>.*)/gi;
110
+
111
+ // src/interfaces/zod.ts
112
+ import { z } from "zod";
113
+ var statisticValueSchema = z.object({
114
+ max: z.number().transform((val) => val === 0 ? void 0 : val).optional(),
115
+ min: z.number().transform(
116
+ (val) => Number.isNaN(Number.parseInt(val, 10)) ? void 0 : val
117
+ ).optional(),
118
+ combinaison: z.string().transform((str) => str.trim() || void 0).optional(),
119
+ exclude: z.boolean().optional()
120
+ }).superRefine((data, ctx) => {
121
+ if (data.max !== void 0 && data.min !== void 0 && data.max <= data.min) {
122
+ ctx.addIssue({
123
+ code: "custom",
124
+ message: `Max_Greater; ${data.min}; ${data.max}`,
125
+ path: ["max"]
126
+ });
127
+ }
128
+ });
129
+ var statisticSchema = z.record(z.string(), statisticValueSchema).optional().refine((stats) => !stats || Object.keys(stats).length <= 25, {
130
+ message: "TooManyStats"
131
+ });
132
+ var criticalSchema = z.object({
133
+ success: z.string().or(z.number().min(0)).optional(),
134
+ failure: z.string().or(z.number().min(0)).optional()
135
+ }).transform((values) => {
136
+ if (values.success === "") values.success = void 0;
137
+ if (values.failure === "") values.failure = void 0;
138
+ if (values.failure === 0) values.failure = void 0;
139
+ if (values.success === 0) values.success = void 0;
140
+ values.success = Number.parseInt(values.success, 10);
141
+ values.failure = Number.parseInt(values.failure, 10);
142
+ return values;
143
+ });
144
+ var criticalValueSchema = z.object({
145
+ sign: z.enum(["<", ">", "<=", ">=", "!=", "=="]),
146
+ value: z.string(),
147
+ onNaturalDice: z.boolean().optional(),
148
+ affectSkill: z.boolean().optional()
149
+ });
150
+ var damageSchema = z.record(z.string(), z.string()).optional().refine((stats) => !stats || Object.keys(stats).length <= 25, {
151
+ message: "TooManyDice"
152
+ });
153
+ var customCriticalSchema = z.record(z.string(), criticalValueSchema).optional().refine((stats) => !stats || Object.keys(stats).length <= 22, {
154
+ message: "TooManyDice"
155
+ });
156
+ var templateSchema = z.object({
157
+ charName: z.boolean().optional(),
158
+ statistics: statisticSchema,
159
+ total: z.number().min(0).transform((val) => val === 0 ? void 0 : val).optional(),
160
+ forceDistrib: z.boolean().optional(),
161
+ diceType: z.string().optional(),
162
+ critical: criticalSchema.optional(),
163
+ customCritical: customCriticalSchema,
164
+ damage: damageSchema
165
+ });
166
+
167
+ // src/roll.ts
168
+ import { DiceRoller as DiceRoller3, NumberGenerator as NumberGenerator7 } from "@dice-roller/rpg-dice-roller";
169
+ import { evaluate as evaluate8 } from "mathjs";
170
+
171
+ // src/dice/bulk.ts
172
+ import { DiceRoller as DiceRoller2, NumberGenerator as NumberGenerator6 } from "@dice-roller/rpg-dice-roller";
173
+ import { evaluate as evaluate6 } from "mathjs";
174
+
175
+ // src/dice/compare.ts
176
+ import { NumberGenerator as NumberGenerator3 } from "@dice-roller/rpg-dice-roller";
177
+ import { evaluate as evaluate2 } from "mathjs";
178
+
179
+ // src/utils.ts
3
180
  import { evaluate } from "mathjs";
181
+ import "uniformize";
182
+ import { NumberGenerator as NumberGenerator2 } from "@dice-roller/rpg-dice-roller";
183
+ import { Random } from "random-js";
184
+ function escapeRegex(string) {
185
+ return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
186
+ }
187
+ function standardizeDice(dice) {
188
+ return dice.replace(
189
+ /(\[[^\]]+])|([^[]+)/g,
190
+ (_match, insideBrackets, outsideText) => insideBrackets ? insideBrackets : outsideText.standardize().replaceAll("df", "dF")
191
+ );
192
+ }
193
+ function generateStatsDice(originalDice, stats, dollarValue) {
194
+ let dice = originalDice.standardize();
195
+ if (stats && Object.keys(stats).length > 0) {
196
+ const statKeys = Object.keys(stats);
197
+ const partsRegex = /(\[[^\]]+])|([^[]+)/g;
198
+ let result = "";
199
+ let match;
200
+ while ((match = partsRegex.exec(dice)) !== null) {
201
+ const insideBrackets = match[1];
202
+ const outsideText = match[2];
203
+ if (insideBrackets) {
204
+ result += insideBrackets;
205
+ continue;
206
+ }
207
+ if (!outsideText) {
208
+ continue;
209
+ }
210
+ const tokenRegex = /([\p{L}\p{N}_]+)/gu;
211
+ let lastIndex = 0;
212
+ let tokenMatch;
213
+ while ((tokenMatch = tokenRegex.exec(outsideText)) !== null) {
214
+ result += outsideText.slice(lastIndex, tokenMatch.index);
215
+ const token = tokenMatch[0];
216
+ const tokenStd = token.standardize();
217
+ const diceMatch = /^(\d*)d(.+)$/i.exec(tokenStd);
218
+ if (diceMatch) {
219
+ const diceCount = diceMatch[1] || "";
220
+ const afterD = diceMatch[2];
221
+ let foundStatAfterD = false;
222
+ for (const key of statKeys) {
223
+ const keyStd = key.standardize();
224
+ if (afterD === keyStd) {
225
+ result += `${diceCount}d${stats[key].toString()}`;
226
+ foundStatAfterD = true;
227
+ break;
228
+ }
229
+ }
230
+ if (foundStatAfterD) {
231
+ lastIndex = tokenRegex.lastIndex;
232
+ continue;
233
+ }
234
+ }
235
+ let bestKey = null;
236
+ let bestScore = 0;
237
+ for (const key of statKeys) {
238
+ const keyStd = key.standardize();
239
+ if (tokenStd === keyStd) {
240
+ bestKey = key;
241
+ bestScore = 1;
242
+ break;
243
+ }
244
+ const score = similarityScore(tokenStd, keyStd);
245
+ if (score > bestScore) {
246
+ bestScore = score;
247
+ bestKey = key;
248
+ }
249
+ }
250
+ if (bestKey && bestScore >= 0.6) {
251
+ const statValue = stats[bestKey];
252
+ result += statValue.toString();
253
+ } else {
254
+ result += token;
255
+ }
256
+ lastIndex = tokenRegex.lastIndex;
257
+ }
258
+ result += outsideText.slice(lastIndex);
259
+ }
260
+ dice = result;
261
+ }
262
+ if (dollarValue) dice = dice.replaceAll("$", dollarValue);
263
+ return replaceFormulaInDice(dice);
264
+ }
265
+ function replaceFormulaInDice(dice) {
266
+ const formula = /(?<formula>\{{2}(.+?)}{2})/gim;
267
+ let match;
268
+ let modifiedDice = dice;
269
+ while ((match = formula.exec(dice)) !== null) {
270
+ if (match.groups?.formula) {
271
+ const formulae = match.groups.formula.replaceAll("{{", "").replaceAll("}}", "");
272
+ try {
273
+ const result = evaluate(formulae);
274
+ modifiedDice = modifiedDice.replace(match.groups.formula, result.toString());
275
+ } catch (error) {
276
+ throw new FormulaError(match.groups.formula, "replaceFormulasInDice", error);
277
+ }
278
+ }
279
+ }
280
+ return cleanedDice(modifiedDice);
281
+ }
282
+ function cleanedDice(dice) {
283
+ return dice.replaceAll("+-", "-").replaceAll("--", "+").replaceAll("++", "+").replaceAll("=>", ">=").replaceAll("=<", "<=").trimEnd();
284
+ }
285
+ function isNumber(value) {
286
+ return value !== void 0 && (typeof value === "number" || !Number.isNaN(Number(value)) && typeof value === "string" && value.trim().length > 0);
287
+ }
288
+ function replaceExpByRandom(dice, engine = NumberGenerator2.engines.nodeCrypto) {
289
+ const diceRegex = /\{exp( ?\|\| ?(?<default>\d+))?}/gi;
290
+ return dice.replace(diceRegex, (_match, _p1, _p2, _offset, _string, groups) => {
291
+ const defaultValue = groups?.default;
292
+ return defaultValue ?? randomInt(1, 999, engine).toString();
293
+ });
294
+ }
295
+ function randomInt(min, max, engine = NumberGenerator2.engines.nodeCrypto, rng) {
296
+ if (!rng) rng = new Random(engine || void 0);
297
+ return rng.integer(min, max);
298
+ }
299
+ function levenshteinDistance(a, b) {
300
+ if (a === b) return 0;
301
+ const al = a.length;
302
+ const bl = b.length;
303
+ if (al === 0) return bl;
304
+ if (bl === 0) return al;
305
+ const v0 = new Array(bl + 1);
306
+ const v1 = new Array(bl + 1);
307
+ for (let i = 0; i <= bl; i++) v0[i] = i;
308
+ for (let i = 0; i < al; i++) {
309
+ v1[0] = i + 1;
310
+ for (let j = 0; j < bl; j++) {
311
+ const cost = a[i] === b[j] ? 0 : 1;
312
+ v1[j + 1] = Math.min(v1[j] + 1, v0[j + 1] + 1, v0[j] + cost);
313
+ }
314
+ for (let j = 0; j <= bl; j++) v0[j] = v1[j];
315
+ }
316
+ return v1[bl];
317
+ }
318
+ function similarityScore(a, b) {
319
+ const la = a.length;
320
+ const lb = b.length;
321
+ if (la === 0 && lb === 0) return 1;
322
+ const dist = levenshteinDistance(a, b);
323
+ const max = Math.max(la, lb);
324
+ return 1 - dist / max;
325
+ }
326
+ function createCriticalCustom(dice, customCritical, template, engine = NumberGenerator2.engines.nodeCrypto) {
327
+ const compareRegex = dice.match(SIGN_REGEX_SPACE);
328
+ let customDice = dice;
329
+ const compareValue = diceTypeRandomParse(customCritical.value, template, engine);
330
+ if (compareValue.includes("$"))
331
+ throw new DiceTypeError(compareValue, "createCriticalCustom");
332
+ const comparaison = `${customCritical.sign}${compareValue}`;
333
+ if (compareRegex) customDice = customDice.replace(SIGN_REGEX_SPACE, comparaison);
334
+ else customDice += comparaison;
335
+ return diceTypeRandomParse(customDice, template, engine);
336
+ }
337
+
338
+ // src/dice/compare.ts
4
339
  function isTrivialComparison(maxValue, minValue, compare) {
5
340
  const canSucceed = canComparisonSucceed(maxValue, compare, minValue);
6
341
  const canFail = canComparisonFail(maxValue, compare, minValue);
@@ -31,7 +366,26 @@ function canComparisonFail(maxRollValue, compare, minRollValue = 1) {
31
366
  return true;
32
367
  }
33
368
  }
34
- function getCompare(dice, compareRegex, engine = NumberGenerator.engines.nodeCrypto, pity) {
369
+ function rollCompare(value, engine = NumberGenerator3.engines.nodeCrypto, pity) {
370
+ if (isNumber(value)) return { value: Number.parseInt(value, 10) };
371
+ if (!value || typeof value === "string" && value.trim() === "") {
372
+ return { value: 0, diceResult: value };
373
+ }
374
+ const rollComp = roll(value, engine, pity);
375
+ if (!rollComp?.total) {
376
+ try {
377
+ return { value: evaluate2(value), diceResult: value };
378
+ } catch (error) {
379
+ return { value: 0, diceResult: value };
380
+ }
381
+ }
382
+ return {
383
+ dice: value,
384
+ value: rollComp.total,
385
+ diceResult: rollComp?.result
386
+ };
387
+ }
388
+ function getCompare(dice, compareRegex, engine = NumberGenerator3.engines.nodeCrypto, pity) {
35
389
  if (dice.match(/((\{.*,(.*)+\}|([><=!]+\d+f))([><=]|!=)+\d+\}?)|\{(.*)(([><=]|!=)+).*\}/))
36
390
  return { dice, compare: void 0 };
37
391
  dice = dice.replace(SIGN_REGEX_SPACE, "");
@@ -42,7 +396,7 @@ function getCompare(dice, compareRegex, engine = NumberGenerator.engines.nodeCry
42
396
  if (sign) {
43
397
  const toCalc = calc.replace(SIGN_REGEX, "").replace(/\s/g, "").replace(/;(.*)/, "");
44
398
  const rCompare = rollCompare(toCalc, engine, pity);
45
- const total = evaluate(rCompare.value.toString());
399
+ const total = evaluate2(rCompare.value.toString());
46
400
  dice = dice.replace(SIGN_REGEX_SPACE, `${compareSign}${total}`);
47
401
  compare = {
48
402
  sign: compareSign,
@@ -61,156 +415,6 @@ function getCompare(dice, compareRegex, engine = NumberGenerator.engines.nodeCry
61
415
  }
62
416
  return { dice, compare };
63
417
  }
64
- function rollCompare(value, engine = NumberGenerator.engines.nodeCrypto, pity) {
65
- if (isNumber(value)) return { value: Number.parseInt(value, 10) };
66
- if (!value || typeof value === "string" && value.trim() === "") {
67
- return { value: 0, diceResult: value };
68
- }
69
- const rollComp = roll(value, engine, pity);
70
- if (!rollComp?.total) {
71
- try {
72
- return { value: evaluate(value), diceResult: value };
73
- } catch (error) {
74
- return { value: 0, diceResult: value };
75
- }
76
- }
77
- return {
78
- dice: value,
79
- value: rollComp.total,
80
- diceResult: rollComp?.result
81
- };
82
- }
83
- function createCriticalCustom(dice, customCritical, template, engine = NumberGenerator.engines.nodeCrypto) {
84
- const compareRegex = dice.match(SIGN_REGEX_SPACE);
85
- let customDice = dice;
86
- const compareValue = diceTypeRandomParse(customCritical.value, template, engine);
87
- if (compareValue.includes("$"))
88
- throw new DiceTypeError(compareValue, "createCriticalCustom");
89
- const comparaison = `${customCritical.sign}${compareValue}`;
90
- if (compareRegex) customDice = customDice.replace(SIGN_REGEX_SPACE, comparaison);
91
- else customDice += comparaison;
92
- return diceTypeRandomParse(customDice, template, engine);
93
- }
94
- function getModifier(dice) {
95
- const modifier = dice.matchAll(/(\+|-|%|\/|\^|\*|\*{2})(\d+)/gi);
96
- let modificator;
97
- for (const mod of modifier) {
98
- if (modificator) {
99
- const sign = modificator.sign;
100
- let value = modificator.value;
101
- if (sign) value = calculator(sign, value, Number.parseInt(mod[2], 10));
102
- modificator = {
103
- sign: mod[1],
104
- value
105
- };
106
- } else {
107
- modificator = {
108
- sign: mod[1],
109
- value: Number.parseInt(mod[2], 10)
110
- };
111
- }
112
- }
113
- return modificator;
114
- }
115
- function roll(dice, engine = NumberGenerator.engines.nodeCrypto, pity) {
116
- dice = standardizeDice(replaceFormulaInDice(dice)).replace(/^\+/, "").replaceAll("=>", ">=").replaceAll("=<", "<=").trimStart();
117
- if (!dice.includes("d")) return void 0;
118
- dice = dice.replaceAll(DETECT_CRITICAL, "").trimEnd();
119
- const compareRegex = dice.match(SIGN_REGEX_SPACE);
120
- let compare;
121
- if (dice.includes(";")) return sharedRolls(dice, engine);
122
- dice = fixParenthesis(dice);
123
- const modificator = getModifier(dice);
124
- if (compareRegex) {
125
- const compareResult = getCompare(dice, compareRegex, engine, pity);
126
- dice = compareResult.dice;
127
- compare = compareResult.compare;
128
- }
129
- if (dice.match(/\d+?#(.*)/)) {
130
- const diceArray = dice.split("#");
131
- const numberOfDice = Number.parseInt(diceArray[0], 10);
132
- const diceToRoll = diceArray[1].replace(COMMENT_REGEX, "");
133
- const commentsMatch = diceArray[1].match(COMMENT_REGEX);
134
- const comments = commentsMatch ? commentsMatch[2] : void 0;
135
- const roller2 = new DiceRoller();
136
- NumberGenerator.generator.engine = engine;
137
- for (let i = 0; i < numberOfDice; i++) {
138
- try {
139
- roller2.roll(diceToRoll);
140
- } catch (error) {
141
- throw new DiceTypeError(diceToRoll, "roll", error);
142
- }
143
- }
144
- return {
145
- dice: diceToRoll,
146
- result: roller2.output,
147
- comment: comments,
148
- compare: compare ? compare : void 0,
149
- modifier: modificator,
150
- total: roller2.total
151
- };
152
- }
153
- const roller = new DiceRoller();
154
- NumberGenerator.generator.engine = engine;
155
- const diceWithoutComment = dice.replace(COMMENT_REGEX, "").trimEnd();
156
- let diceRoll;
157
- try {
158
- diceRoll = roller.roll(diceWithoutComment);
159
- } catch (error) {
160
- throw new DiceTypeError(diceWithoutComment, "roll", error);
161
- }
162
- if (compare && diceRoll) {
163
- const currentRoll = Array.isArray(diceRoll) ? diceRoll[0] : diceRoll;
164
- const maxDiceValue = currentRoll.maxTotal;
165
- const minDiceValue = currentRoll.minTotal;
166
- const trivial = isTrivialComparison(maxDiceValue, minDiceValue, compare);
167
- compare.trivial = trivial ? true : void 0;
168
- }
169
- const commentMatch = dice.match(COMMENT_REGEX);
170
- const comment = commentMatch ? commentMatch[2] : void 0;
171
- let rerollCount = 0;
172
- let res;
173
- if (pity && compare) {
174
- const currentRoll = Array.isArray(diceRoll) ? diceRoll[0] : diceRoll;
175
- const maxPossible = currentRoll ? currentRoll.maxTotal : null;
176
- const isComparisonPossible = maxPossible === null || canComparisonSucceed(maxPossible, compare);
177
- if (isComparisonPossible) {
178
- let isFail = evaluate(`${roller.total}${compare.sign}${compare.value}`);
179
- if (!isFail) {
180
- const maxReroll = 100;
181
- while (!isFail && rerollCount < maxReroll) {
182
- try {
183
- res = roll(diceWithoutComment, engine, false);
184
- } catch (error) {
185
- throw new DiceTypeError(diceWithoutComment, "roll", error);
186
- }
187
- rerollCount++;
188
- if (res && res.total !== void 0)
189
- isFail = evaluate(`${res.total}${compare.sign}${compare.value}`);
190
- }
191
- if (res) {
192
- return {
193
- ...res,
194
- dice,
195
- comment,
196
- compare,
197
- modifier: modificator,
198
- pityLogs: rerollCount
199
- };
200
- }
201
- }
202
- } else ;
203
- }
204
- return {
205
- dice,
206
- result: roller.output,
207
- comment,
208
- compare: compare ? compare : void 0,
209
- modifier: modificator,
210
- total: roller.total,
211
- pityLogs: rerollCount > 0 ? rerollCount : void 0
212
- };
213
- }
214
418
  function canComparisonSucceed(maxRollValue, compare, minRollValue) {
215
419
  switch (compare.sign) {
216
420
  case ">":
@@ -232,81 +436,22 @@ function canComparisonSucceed(maxRollValue, compare, minRollValue) {
232
436
  return true;
233
437
  }
234
438
  }
439
+
440
+ // src/dice/exploding.ts
441
+ import { evaluate as evaluate4 } from "mathjs";
442
+
443
+ // src/dice/signs.ts
444
+ import { NumberGenerator as NumberGenerator4 } from "@dice-roller/rpg-dice-roller";
445
+ import { evaluate as evaluate3 } from "mathjs";
446
+
447
+ // src/dice/replace.ts
448
+ function replaceUnwantedText(dice) {
449
+ return dice.replaceAll(/[{}]/g, "").replaceAll(/s[ad]/gi, "");
450
+ }
235
451
  function fixParenthesis(dice) {
236
452
  const parenthesisRegex = /d\((\d+)\)/g;
237
453
  return dice.replaceAll(parenthesisRegex, (_match, p1) => `d${p1}`);
238
454
  }
239
- function calculator(sign, value, total) {
240
- if (sign === "^") sign = "**";
241
- return evaluate(`${total} ${sign} ${value}`);
242
- }
243
- function inverseSign(sign) {
244
- switch (sign) {
245
- case "<":
246
- return ">";
247
- case ">":
248
- return "<";
249
- case "<=":
250
- return ">=";
251
- case ">=":
252
- return "<=";
253
- case "=":
254
- return "!=";
255
- case "==":
256
- return "!=";
257
- case "!=":
258
- return "==";
259
- }
260
- }
261
- function replaceInFormula(element, diceResult, compareResult, res, engine = NumberGenerator.engines.nodeCrypto, pity) {
262
- const { formule, diceAll } = replaceText(
263
- element,
264
- diceResult.total ?? 0,
265
- diceResult.dice
266
- );
267
- const validSign = res ? "\u2713" : "\u2715";
268
- const invertedSign = res ? compareResult.compare.sign : inverseSign(compareResult.compare.sign);
269
- let evaluateRoll;
270
- try {
271
- evaluateRoll = evaluate(compareResult.dice);
272
- return `${validSign} ${diceAll}: ${formule} = ${evaluateRoll}${invertedSign}${compareResult.compare?.value}`;
273
- } catch (error) {
274
- const evaluateRoll2 = roll(compareResult.dice, engine, pity);
275
- if (evaluateRoll2)
276
- return `${validSign} ${diceAll}: ${evaluateRoll2.result.split(":").splice(1).join(":")}`;
277
- return `${validSign} ${diceAll}: ${formule} = ${evaluateRoll2}${invertedSign}${compareResult.compare?.value}`;
278
- }
279
- }
280
- function compareSignFormule(toRoll, compareRegex, element, diceResult, engine = NumberGenerator.engines.nodeCrypto, pity) {
281
- let results = "";
282
- let trivial = false;
283
- const compareResult = getCompare(toRoll, compareRegex, engine);
284
- const toCompare = `${compareResult.dice}${compareResult.compare?.sign}${compareResult.compare?.value}`;
285
- let res;
286
- try {
287
- res = evaluate(toCompare);
288
- } catch (error) {
289
- res = roll(toCompare, engine, pity);
290
- }
291
- if (typeof res === "boolean") {
292
- trivial = true;
293
- if (compareResult.compare) compareResult.compare.trivial = true;
294
- results = replaceInFormula(element, diceResult, compareResult, res, engine, pity);
295
- } else if (res instanceof Object) {
296
- const diceResult2 = res;
297
- if (diceResult2.compare) {
298
- const toEvaluate = evaluate(
299
- `${diceResult2.total}${diceResult2.compare.sign}${diceResult2.compare.value}`
300
- );
301
- const sign = toEvaluate ? "\u2713" : "\u2715";
302
- const invertedSign = toEvaluate ? diceResult2.compare.sign : inverseSign(diceResult2.compare.sign);
303
- const dice = replaceText(element, 0, diceResult2.dice).diceAll;
304
- results = `${sign} ${dice}: ${diceResult2.result.split(":").splice(1).join(":").trim()}${invertedSign}${diceResult2.compare.value}`;
305
- if (diceResult2.compare.trivial) trivial = true;
306
- }
307
- }
308
- return { dice: compareResult.dice, results, compare: compareResult.compare, trivial };
309
- }
310
455
  function replaceText(element, total, dice) {
311
456
  return {
312
457
  formule: element.replace(SYMBOL_DICE, `[${total}]`).replace(/%.*%/g, "").trim(),
@@ -327,390 +472,726 @@ function formatComment(dice) {
327
472
  else if (optional) finalComment = `__${optional}__ \u2014 `;
328
473
  return finalComment;
329
474
  }
330
- function sharedRolls(dice, engine = NumberGenerator.engines.nodeCrypto, pity) {
331
- if (dice.match(/\d+?#(.*?)/))
332
- throw new DiceTypeError(
333
- dice,
334
- "noBulkRoll",
335
- "bulk roll are not allowed in shared rolls"
336
- );
337
- const results = [];
338
- const mainComment = /\s+#(?<comment>.*)/.exec(dice)?.groups?.comment?.trimEnd() ?? void 0;
339
- const split = dice.split(";");
340
- let diceMain = fixParenthesis(split[0]);
341
- const commentsRegex = /\[(?<comments>.*?)\]/gi;
342
- const comments = formatComment(diceMain);
343
- const diceMainWithoutComments = diceMain.replace(commentsRegex, "").trim();
344
- const toHideRegex = /\((?<dice>[^)]+)\)/;
345
- const toHide = toHideRegex.exec(diceMainWithoutComments)?.groups;
346
- let hidden = false;
347
- if (toHide?.dice) {
348
- diceMain = toHide.dice;
349
- hidden = true;
350
- } else if (toHide) {
351
- diceMain = "1d1";
352
- hidden = true;
353
- } else {
354
- diceMain = diceMainWithoutComments;
355
- }
356
- let diceResult = roll(diceMain, engine, pity);
357
- if (!diceResult || !diceResult.total) {
358
- if (hidden) {
359
- diceResult = roll(fixParenthesis(split[0]), engine, pity);
360
- hidden = false;
361
- } else return void 0;
475
+
476
+ // src/dice/signs.ts
477
+ function matchComparison(sign, val, value) {
478
+ switch (sign) {
479
+ case ">":
480
+ return val > value;
481
+ case ">=":
482
+ return val >= value;
483
+ case "<":
484
+ return val < value;
485
+ case "<=":
486
+ return val <= value;
487
+ case "=":
488
+ case "==":
489
+ return val === value;
490
+ case "!=":
491
+ return val !== value;
492
+ default:
493
+ return false;
362
494
  }
363
- if (!diceResult || !diceResult.total) return void 0;
364
- let aggregatedCompare = diceResult.compare;
365
- let hasTrivialComparison = diceResult.compare?.trivial === true;
366
- results.push(`\u203B ${comments}${diceResult.result}`);
367
- let total = diceResult.total;
368
- diceResult.comment = mainComment;
369
- if (!total) return diceResult;
370
- for (let element of split.slice(1)) {
371
- const comment = formatComment(element);
372
- element = element.replaceAll(commentsRegex, "").replaceAll(OPTIONAL_COMMENT, "").trim();
373
- let toRoll = element.replace(SYMBOL_DICE, `${diceResult.total}`);
374
- const compareRegex = toRoll.match(SIGN_REGEX_SPACE);
375
- if (compareRegex) {
376
- const compareResult = compareSignFormule(
377
- toRoll,
378
- compareRegex,
379
- element,
380
- diceResult,
381
- engine
382
- );
383
- toRoll = compareResult.dice;
384
- results.push(compareResult.results);
385
- if (!aggregatedCompare && compareResult.compare) aggregatedCompare = compareResult.compare;
386
- if (compareResult.trivial) hasTrivialComparison = true;
387
- } else {
388
- const { formule, diceAll } = replaceText(
389
- element,
390
- diceResult.total,
391
- diceResult.dice
495
+ }
496
+ function inverseSign(sign) {
497
+ switch (sign) {
498
+ case "<":
499
+ return ">";
500
+ case ">":
501
+ return "<";
502
+ case "<=":
503
+ return ">=";
504
+ case ">=":
505
+ return "<=";
506
+ case "=":
507
+ return "!=";
508
+ case "==":
509
+ return "!=";
510
+ case "!=":
511
+ return "==";
512
+ }
513
+ }
514
+ function compareSignFormule(toRoll, compareRegex, element, diceResult, engine = NumberGenerator4.engines.nodeCrypto, pity, rollBounds) {
515
+ let results = "";
516
+ let trivial = false;
517
+ const compareResult = getCompare(toRoll, compareRegex, engine);
518
+ const toCompare = `${compareResult.dice}${compareResult.compare?.sign}${compareResult.compare?.value}`;
519
+ let res;
520
+ try {
521
+ res = evaluate3(toCompare);
522
+ } catch (error) {
523
+ res = roll(toCompare, engine, pity);
524
+ }
525
+ if (typeof res === "boolean") {
526
+ const detectedTrivial = rollBounds && compareResult.compare ? isTrivialComparison(rollBounds.max, rollBounds.min, compareResult.compare) : false;
527
+ if (detectedTrivial && compareResult.compare) compareResult.compare.trivial = true;
528
+ if (detectedTrivial) trivial = true;
529
+ results = replaceInFormula(element, diceResult, compareResult, res, engine, pity);
530
+ } else if (res instanceof Object) {
531
+ const diceResult2 = res;
532
+ if (diceResult2.compare) {
533
+ const toEvaluate = evaluate3(
534
+ `${diceResult2.total}${diceResult2.compare.sign}${diceResult2.compare.value}`
392
535
  );
393
- try {
394
- const evaluated = evaluate(toRoll);
395
- results.push(`\u25C8 ${comment}${diceAll}: ${formule} = ${evaluated}`);
396
- total += Number.parseInt(evaluated, 10);
397
- } catch (error) {
398
- const evaluated = roll(toRoll, engine, pity);
399
- if (evaluated) {
400
- results.push(
401
- `\u25C8 ${comment}${diceAll}: ${evaluated.result.split(":").slice(1).join(":")}`
402
- );
403
- if (!aggregatedCompare && evaluated.compare)
404
- aggregatedCompare = evaluated.compare;
405
- if (evaluated.compare?.trivial) hasTrivialComparison = true;
406
- } else results.push(`\u25C8 ${comment}${diceAll}: ${formule} = ${evaluated}`);
407
- total += evaluated?.total ?? 0;
408
- }
536
+ const sign = toEvaluate ? "\u2713" : "\u2715";
537
+ const invertedSign = toEvaluate ? diceResult2.compare.sign : inverseSign(diceResult2.compare.sign);
538
+ const dice = replaceText(element, 0, diceResult2.dice).diceAll;
539
+ results = `${sign} ${dice}: ${diceResult2.result.split(":").splice(1).join(":").trim()}${invertedSign}${diceResult2.compare.value}`;
540
+ if (diceResult2.compare.trivial) trivial = true;
409
541
  }
410
542
  }
411
- if (hidden)
412
- results.shift();
543
+ return { dice: compareResult.dice, results, compare: compareResult.compare, trivial };
544
+ }
545
+
546
+ // src/dice/exploding.ts
547
+ var EXPLODING_SUCCESS_REGEX = /(!(?:>>=|<<=|!==|!!==|>>|<<|==|!=))(-?\d+(?:\.\d+)?)/;
548
+ function normalizeExplodingSuccess(dice) {
549
+ const match = dice.match(EXPLODING_SUCCESS_REGEX);
550
+ if (!match) return void 0;
551
+ const [, doubledSignRaw, valueStr] = match;
552
+ let doubledSign;
553
+ if (doubledSignRaw === "!!==") doubledSign = "==";
554
+ else if (doubledSignRaw === "!==") doubledSign = "!==";
555
+ else doubledSign = doubledSignRaw.replace(/^!/, "");
556
+ const signMap = {
557
+ ">>": ">",
558
+ "<<": "<",
559
+ ">>=": ">=",
560
+ "<<=": "<=",
561
+ "==": "=",
562
+ "!==": "!=",
563
+ "!!==": "="
564
+ };
565
+ const normalizedSign = signMap[doubledSign];
566
+ if (!normalizedSign) return void 0;
567
+ let parsedValue = Number.parseFloat(valueStr);
568
+ if (Number.isNaN(parsedValue)) {
569
+ try {
570
+ parsedValue = Number.parseFloat(evaluate4(valueStr));
571
+ } catch (_error) {
572
+ parsedValue = 0;
573
+ }
574
+ }
575
+ const normalizedSegment = "!";
576
+ const replacedDice = dice.replace(match[0], normalizedSegment);
413
577
  return {
414
- dice: diceMain,
415
- result: results.join(";"),
416
- comment: mainComment,
417
- compare: hasTrivialComparison && aggregatedCompare ? { ...aggregatedCompare, trivial: true } : aggregatedCompare,
418
- modifier: diceResult.modifier,
419
- total
578
+ dice: replacedDice,
579
+ originalDice: dice,
580
+ sign: normalizedSign,
581
+ value: parsedValue,
582
+ normalizedSegment,
583
+ originalSegment: match[0]
420
584
  };
421
585
  }
586
+ function countExplodingSuccesses(diceRoll, sign, value) {
587
+ const rollsArray = Array.isArray(diceRoll) ? diceRoll : [diceRoll];
588
+ const flatValues = [];
589
+ for (const dr of rollsArray) {
590
+ const groups = dr.rolls ?? [];
591
+ for (const group of groups) {
592
+ const innerRolls = group.rolls ?? [];
593
+ for (const roll2 of innerRolls) {
594
+ if (typeof roll2.value === "number") flatValues.push(roll2.value);
595
+ }
596
+ }
597
+ }
598
+ return flatValues.reduce(
599
+ (acc, current) => acc + (matchComparison(sign, current, value) ? 1 : 0),
600
+ 0
601
+ );
602
+ }
422
603
 
423
- // src/errors.ts
424
- var DiceTypeError = class extends Error {
425
- dice;
426
- cause;
427
- method;
428
- constructor(dice, cause, method) {
429
- super(dice);
430
- this.name = "Invalid_Dice_Type";
431
- this.dice = dice;
432
- this.cause = cause;
433
- this.method = method;
604
+ // src/dice/extract.ts
605
+ import { DiceRoller, NumberGenerator as NumberGenerator5 } from "@dice-roller/rpg-dice-roller";
606
+
607
+ // src/dice/calculator.ts
608
+ import { evaluate as evaluate5 } from "mathjs";
609
+ function calculator(sign, value, total) {
610
+ if (sign === "^") sign = "**";
611
+ return evaluate5(`${total} ${sign} ${value}`);
612
+ }
613
+
614
+ // src/dice/extract.ts
615
+ function getModifier(dice) {
616
+ const modifier = dice.matchAll(/(\+|-|%|\/|\^|\*|\*{2})(\d+)/gi);
617
+ let modificator;
618
+ for (const mod of modifier) {
619
+ if (modificator) {
620
+ const sign = modificator.sign;
621
+ let value = modificator.value;
622
+ if (sign) value = calculator(sign, value, Number.parseInt(mod[2], 10));
623
+ modificator = {
624
+ sign: mod[1],
625
+ value
626
+ };
627
+ } else {
628
+ modificator = {
629
+ sign: mod[1],
630
+ value: Number.parseInt(mod[2], 10)
631
+ };
632
+ }
434
633
  }
435
- };
436
- var FormulaError = class extends Error {
437
- formula;
438
- cause;
439
- method;
440
- constructor(formula, cause, method) {
441
- super(formula);
442
- this.name = "Invalid_Formula";
443
- this.formula = formula;
444
- this.cause = cause;
445
- this.method = method;
634
+ return modificator;
635
+ }
636
+ function extractValuesFromOutput(output) {
637
+ const values = [];
638
+ const regex = /\[([^\]]+)\]/g;
639
+ let match;
640
+ while ((match = regex.exec(output)) !== null) {
641
+ const segmentValues = match[1].split(",").map((v) => Number.parseInt(v.replace(/[!*]/g, "").trim(), 10)).filter((v) => !Number.isNaN(v));
642
+ values.push(...segmentValues);
446
643
  }
447
- };
448
- var MaxGreater = class extends Error {
449
- name;
450
- value;
451
- max;
452
- constructor(value, max) {
453
- super(value.toString());
454
- this.name = "Max_Greater";
455
- this.value = value;
456
- this.max = max;
644
+ return values;
645
+ }
646
+ function getRollBounds(dice, engine = NumberGenerator5.engines.nodeCrypto) {
647
+ try {
648
+ const roller = new DiceRoller();
649
+ NumberGenerator5.generator.engine = engine;
650
+ const rollResult = roller.roll(dice);
651
+ const instance = Array.isArray(rollResult) ? rollResult[0] : rollResult;
652
+ const { minTotal, maxTotal } = instance;
653
+ return { min: minTotal, max: maxTotal };
654
+ } catch (error) {
457
655
  }
458
- };
459
- var EmptyObjectError = class extends Error {
460
- name;
461
- constructor() {
462
- super();
463
- this.name = "Empty_Object";
656
+ return void 0;
657
+ }
658
+ function setSortOrder(toRoll, sort) {
659
+ const sortRegex = /(sa|sd|s)/i;
660
+ if (sort && !toRoll.match(sortRegex)) {
661
+ const modifierComparisonRegex = /([+\-*/%^]\d+|([><=!]+\d+f)|([><=]|!=)+\d+)$/;
662
+ const match = toRoll.match(modifierComparisonRegex);
663
+ if (match) {
664
+ const index = match.index;
665
+ toRoll = `${toRoll.slice(0, index)}${sort}${toRoll.slice(index)}`;
666
+ } else {
667
+ toRoll += sort;
668
+ }
464
669
  }
465
- };
466
- var TooManyDice = class extends Error {
467
- name;
468
- constructor() {
469
- super();
470
- this.name = "Too_Many_Dice";
670
+ return toRoll;
671
+ }
672
+ function prepareDice(diceInput) {
673
+ let dice = standardizeDice(replaceFormulaInDice(diceInput)).replace(/^\+/, "").replaceAll("=>", ">=").replaceAll("=<", "<=").trimStart();
674
+ dice = dice.replaceAll(DETECT_CRITICAL, "").trimEnd();
675
+ const explodingSuccess = normalizeExplodingSuccess(dice);
676
+ if (explodingSuccess) dice = explodingSuccess.dice;
677
+ let diceDisplay;
678
+ if (dice.includes(";")) {
679
+ const mainDice = dice.split(";")[0];
680
+ diceDisplay = explodingSuccess?.originalDice ?? mainDice;
681
+ } else {
682
+ diceDisplay = explodingSuccess?.originalDice ?? dice;
471
683
  }
472
- };
473
- var TooManyStats = class extends Error {
474
- name;
475
- constructor() {
476
- super();
477
- this.name = "Too_Many_Stats";
684
+ const curlyBulkMatch = dice.match(/^\{(\d+#.*)\}$/);
685
+ const isCurlyBulk = !!curlyBulkMatch;
686
+ const bulkContent = isCurlyBulk ? curlyBulkMatch[1] : "";
687
+ const isSharedRoll = dice.includes(";");
688
+ let isSharedCurly = false;
689
+ if (isSharedRoll && dice.match(/^\{.*;\s*.*\}$/)) {
690
+ dice = dice.slice(1, -1);
691
+ isSharedCurly = true;
692
+ diceDisplay = diceDisplay.slice(1);
478
693
  }
479
- };
480
- var NoStatisticsError = class extends Error {
481
- name;
482
- constructor() {
483
- super();
484
- this.name = "No_Statistics";
694
+ let isSimpleCurly = false;
695
+ if (!isCurlyBulk && !isSharedRoll && dice.match(/^\{.*\}$/)) {
696
+ const innerContent = dice.slice(1, -1);
697
+ const hasModifiers = innerContent.match(/[+\-*/%^]/);
698
+ const hasComparison = innerContent.match(/(([><=!]+\d+f)|([><=]|!=)+\d+)/);
699
+ if (!(hasComparison && !hasModifiers)) {
700
+ dice = innerContent;
701
+ isSimpleCurly = true;
702
+ }
485
703
  }
486
- };
487
-
488
- // src/interfaces/constant.ts
489
- var COMMENT_REGEX = /\s+(#|\/{2}|\[|\/\*)(?<comment>.*)/gi;
490
- var SIGN_REGEX = /==|!=|(?<![!<>])>=|(?<![!<>])<=|(?<!!)(?<![<>])>|(?<!!)(?<![<>])<|(?<!!)(?<![<>])=/;
491
- var SIGN_REGEX_SPACE = /(==|!=|(?<![!<>])>=|(?<![!<>])<=|(?<!!)(?<![<>])>|(?<!!)(?<![<>])<|(?<!!)(?<![<>])=)(\S+)/;
492
- var SYMBOL_DICE = "&";
493
- var DETECT_CRITICAL = /\{\*?c[fs]:([<>=]|!=)+(.+?)}/gim;
494
- var OPTIONAL_COMMENT = /\s+(#|\/{2}|\[|\/\*)?(?<comment>.*)/gi;
704
+ return {
705
+ dice,
706
+ diceDisplay,
707
+ explodingSuccess,
708
+ isSharedRoll,
709
+ isSharedCurly,
710
+ isCurlyBulk,
711
+ bulkContent,
712
+ isSimpleCurly
713
+ };
714
+ }
495
715
 
496
- // src/interfaces/zod.ts
497
- import { z } from "zod";
498
- var statisticValueSchema = z.object({
499
- max: z.number().transform((val) => val === 0 ? void 0 : val).optional(),
500
- min: z.number().transform(
501
- (val) => Number.isNaN(Number.parseInt(val, 10)) ? void 0 : val
502
- ).optional(),
503
- combinaison: z.string().transform((str) => str.trim() || void 0).optional(),
504
- exclude: z.boolean().optional()
505
- }).superRefine((data, ctx) => {
506
- if (data.max !== void 0 && data.min !== void 0 && data.max <= data.min) {
507
- ctx.addIssue({
508
- code: "custom",
509
- message: `Max_Greater; ${data.min}; ${data.max}`,
510
- path: ["max"]
511
- });
716
+ // src/dice/bulk.ts
717
+ function handleBulkRolls(dice, isCurlyBulk, bulkContent, compare, explodingSuccess, diceDisplay, engine, sort) {
718
+ const bulkProcessContent = isCurlyBulk ? bulkContent : dice;
719
+ const diceArray = bulkProcessContent.split("#");
720
+ const numberOfDice = Number.parseInt(diceArray[0], 10);
721
+ let diceToRoll = diceArray[1].replace(COMMENT_REGEX, "");
722
+ const commentsMatch = diceArray[1].match(COMMENT_REGEX);
723
+ const comments = commentsMatch ? commentsMatch[2] : void 0;
724
+ let curlyCompare;
725
+ if (isCurlyBulk) {
726
+ const curlyCompareRegex = diceToRoll.match(SIGN_REGEX_SPACE);
727
+ if (curlyCompareRegex) {
728
+ const compareSign = curlyCompareRegex[0].match(SIGN_REGEX)?.[0];
729
+ const compareValue = curlyCompareRegex[2];
730
+ if (compareSign && compareValue) {
731
+ curlyCompare = {
732
+ sign: compareSign,
733
+ value: Number.parseInt(compareValue, 10)
734
+ };
735
+ diceToRoll = diceToRoll.replace(SIGN_REGEX_SPACE, "");
736
+ }
737
+ }
512
738
  }
513
- });
514
- var statisticSchema = z.record(z.string(), statisticValueSchema).optional().refine((stats) => !stats || Object.keys(stats).length <= 25, {
515
- message: "TooManyStats"
516
- });
517
- var criticalSchema = z.object({
518
- success: z.string().or(z.number().min(0)).optional(),
519
- failure: z.string().or(z.number().min(0)).optional()
520
- }).transform((values) => {
521
- if (values.success === "") values.success = void 0;
522
- if (values.failure === "") values.failure = void 0;
523
- if (values.failure === 0) values.failure = void 0;
524
- if (values.success === 0) values.success = void 0;
525
- values.success = Number.parseInt(values.success, 10);
526
- values.failure = Number.parseInt(values.failure, 10);
527
- return values;
528
- });
529
- var criticalValueSchema = z.object({
530
- sign: z.enum(["<", ">", "<=", ">=", "!=", "=="]),
531
- value: z.string(),
532
- onNaturalDice: z.boolean().optional(),
533
- affectSkill: z.boolean().optional()
534
- });
535
- var damageSchema = z.record(z.string(), z.string()).optional().refine((stats) => !stats || Object.keys(stats).length <= 25, {
536
- message: "TooManyDice"
537
- });
538
- var customCriticalSchema = z.record(z.string(), criticalValueSchema).optional().refine((stats) => !stats || Object.keys(stats).length <= 22, {
539
- message: "TooManyDice"
540
- });
541
- var templateSchema = z.object({
542
- charName: z.boolean().optional(),
543
- statistics: statisticSchema,
544
- total: z.number().min(0).transform((val) => val === 0 ? void 0 : val).optional(),
545
- forceDistrib: z.boolean().optional(),
546
- diceType: z.string().optional(),
547
- critical: criticalSchema.optional(),
548
- customCritical: customCriticalSchema,
549
- damage: damageSchema
550
- });
551
-
552
- // src/utils.ts
553
- import { evaluate as evaluate2 } from "mathjs";
554
- import "uniformize";
555
- import { NumberGenerator as NumberGenerator2 } from "@dice-roller/rpg-dice-roller";
556
- import { Random } from "random-js";
557
- function escapeRegex(string) {
558
- return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
559
- }
560
- function standardizeDice(dice) {
561
- return dice.replace(
562
- /(\[[^\]]+])|([^[]+)/g,
563
- (_match, insideBrackets, outsideText) => insideBrackets ? insideBrackets : outsideText.standardize().replaceAll("df", "dF")
564
- );
565
- }
566
- function generateStatsDice(originalDice, stats, dollarValue) {
567
- let dice = originalDice.standardize();
568
- if (stats && Object.keys(stats).length > 0) {
569
- const statKeys = Object.keys(stats);
570
- const partsRegex = /(\[[^\]]+])|([^[]+)/g;
571
- let result = "";
572
- let match;
573
- while ((match = partsRegex.exec(dice)) !== null) {
574
- const insideBrackets = match[1];
575
- const outsideText = match[2];
576
- if (insideBrackets) {
577
- result += insideBrackets;
578
- continue;
739
+ diceToRoll = setSortOrder(diceToRoll, sort);
740
+ const activeCompare = compare || curlyCompare || (explodingSuccess ? { sign: explodingSuccess.sign, value: explodingSuccess.value } : void 0);
741
+ if (activeCompare) {
742
+ return handleBulkRollsWithComparison(
743
+ numberOfDice,
744
+ diceToRoll,
745
+ comments,
746
+ activeCompare,
747
+ explodingSuccess,
748
+ diceDisplay,
749
+ isCurlyBulk,
750
+ curlyCompare,
751
+ compare,
752
+ engine
753
+ );
754
+ }
755
+ const roller = new DiceRoller2();
756
+ NumberGenerator6.generator.engine = engine;
757
+ for (let i = 0; i < numberOfDice; i++) {
758
+ try {
759
+ roller.roll(diceToRoll);
760
+ } catch (error) {
761
+ throw new DiceTypeError(diceToRoll, "roll", error);
762
+ }
763
+ }
764
+ const finalDice = isCurlyBulk ? `{${diceToRoll}}` : diceToRoll;
765
+ const modificator = getModifier(dice);
766
+ return {
767
+ dice: finalDice,
768
+ result: replaceUnwantedText(roller.output),
769
+ comment: comments,
770
+ compare: compare ? compare : void 0,
771
+ modifier: modificator,
772
+ total: roller.total
773
+ };
774
+ }
775
+ function handleBulkRollsWithComparison(numberOfDice, diceToRoll, comments, activeCompare, explodingSuccess, diceDisplay, isCurlyBulk, curlyCompare, compare, engine) {
776
+ const results = [];
777
+ let successCount = 0;
778
+ const roller = new DiceRoller2();
779
+ NumberGenerator6.generator.engine = engine;
780
+ let trivialComparisonDetected = false;
781
+ const formatOutput = (output, addStar) => {
782
+ const formatted = addStar && isCurlyBulk ? output.replace(
783
+ /\[([^\]]+)\]/,
784
+ (_m, content) => `[${content.split(",").map((d) => `${d.trim()}*`).join(", ")}]`
785
+ ) : output;
786
+ return curlyCompare ? formatted.replace(/^([^:]+):/, `$1${curlyCompare.sign}${curlyCompare.value}:`) : formatted;
787
+ };
788
+ for (let i = 0; i < numberOfDice; i++) {
789
+ try {
790
+ const individualRoll = roller.roll(diceToRoll);
791
+ const rollInstance = Array.isArray(individualRoll) ? individualRoll[0] : individualRoll;
792
+ if (!trivialComparisonDetected && activeCompare) {
793
+ const { maxTotal, minTotal } = rollInstance;
794
+ trivialComparisonDetected = isTrivialComparison(
795
+ maxTotal,
796
+ minTotal,
797
+ activeCompare
798
+ );
579
799
  }
580
- if (!outsideText) {
581
- continue;
800
+ const rollOutput = rollInstance.output;
801
+ if (explodingSuccess) {
802
+ const successesForRoll = countExplodingSuccesses(
803
+ rollInstance,
804
+ explodingSuccess.sign,
805
+ explodingSuccess.value
806
+ );
807
+ successCount += successesForRoll;
808
+ let formattedRollOutput = rollOutput.replace(explodingSuccess.normalizedSegment, explodingSuccess.originalSegment).replace(/=\s*-?\d+(?:\.\d+)?$/, `= ${successesForRoll}`);
809
+ formattedRollOutput = formatOutput(formattedRollOutput, false);
810
+ results.push(formattedRollOutput);
811
+ } else {
812
+ const rollTotal = rollInstance.total;
813
+ const isSuccess = evaluate6(
814
+ `${rollTotal}${activeCompare.sign}${activeCompare.value}`
815
+ );
816
+ if (isSuccess) successCount++;
817
+ results.push(formatOutput(rollOutput, isSuccess));
582
818
  }
583
- const tokenRegex = /([\p{L}\p{N}_]+)/gu;
584
- let lastIndex = 0;
585
- let tokenMatch;
586
- while ((tokenMatch = tokenRegex.exec(outsideText)) !== null) {
587
- result += outsideText.slice(lastIndex, tokenMatch.index);
588
- const token = tokenMatch[0];
589
- const tokenStd = token.standardize();
590
- let bestKey = null;
591
- let bestScore = 0;
592
- for (const key of statKeys) {
593
- const keyStd = key.standardize();
594
- if (tokenStd === keyStd || keyStd.includes(tokenStd) || tokenStd.includes(keyStd) || keyStd.startsWith(tokenStd) || tokenStd.startsWith(keyStd)) {
595
- bestKey = key;
596
- bestScore = 1;
597
- break;
598
- }
599
- const score = similarityScore(tokenStd, keyStd);
600
- if (score > bestScore) {
601
- bestScore = score;
602
- bestKey = key;
603
- }
604
- }
605
- if (bestKey && bestScore >= 0.6) {
606
- const statValue = stats[bestKey];
607
- result += statValue.toString();
819
+ } catch (error) {
820
+ throw new DiceTypeError(diceToRoll, "roll", error);
821
+ }
822
+ }
823
+ if (explodingSuccess) {
824
+ const signSource = explodingSuccess?.originalDice ?? diceDisplay;
825
+ const explodingMatch = signSource.match(EXPLODING_SUCCESS_REGEX);
826
+ if (explodingMatch) {
827
+ const [, doubledSignRaw, valueStr] = explodingMatch;
828
+ let doubledSign;
829
+ if (doubledSignRaw === "!!==") doubledSign = "==";
830
+ else if (doubledSignRaw === "!==") doubledSign = "!==";
831
+ else doubledSign = doubledSignRaw.replace(/^!/, "");
832
+ const signMap = {
833
+ ">>": ">",
834
+ "<<": "<",
835
+ ">=": ">=",
836
+ "<=": "<=",
837
+ "==": "=",
838
+ "!==": "!=",
839
+ "!!==": "="
840
+ };
841
+ const mappedSign = signMap[doubledSign];
842
+ const mappedValue = Number.parseFloat(valueStr);
843
+ if (mappedSign && !Number.isNaN(mappedValue)) {
844
+ const resultsString = replaceUnwantedText(results.join("; "));
845
+ const flatValues = resultsString.split(";").flatMap((segment) => extractValuesFromOutput(segment));
846
+ if (mappedSign === "!=") {
847
+ const equalsCount = flatValues.filter((val) => val === mappedValue).length;
848
+ successCount = flatValues.length - equalsCount;
608
849
  } else {
609
- result += token;
850
+ successCount = flatValues.filter(
851
+ (val) => matchComparison(mappedSign, val, mappedValue)
852
+ ).length;
610
853
  }
611
- lastIndex = tokenRegex.lastIndex;
612
854
  }
613
- result += outsideText.slice(lastIndex);
614
855
  }
615
- dice = result;
616
856
  }
617
- if (dollarValue) dice = dice.replaceAll("$", dollarValue);
618
- return replaceFormulaInDice(dice);
857
+ if (compare && trivialComparisonDetected) compare.trivial = true;
858
+ const finalDice = isCurlyBulk ? `{${diceToRoll}${curlyCompare?.sign}${curlyCompare?.value}}` : diceToRoll;
859
+ const resultOutput = replaceUnwantedText(results.join("; "));
860
+ const finalTotal = explodingSuccess ? resultOutput.split(";").flatMap((segment) => extractValuesFromOutput(segment)).filter(
861
+ (val) => matchComparison(explodingSuccess.sign, val, explodingSuccess.value)
862
+ ).length : successCount;
863
+ const modificator = getModifier(diceDisplay);
864
+ return {
865
+ dice: explodingSuccess ? diceDisplay : finalDice,
866
+ result: resultOutput,
867
+ comment: comments,
868
+ compare: isCurlyBulk ? void 0 : compare,
869
+ modifier: modificator,
870
+ total: finalTotal,
871
+ trivial: trivialComparisonDetected ? true : void 0
872
+ };
619
873
  }
620
- function replaceFormulaInDice(dice) {
621
- const formula = /(?<formula>\{{2}(.+?)}{2})/gim;
622
- let match;
623
- let modifiedDice = dice;
624
- while ((match = formula.exec(dice)) !== null) {
625
- if (match.groups?.formula) {
626
- const formulae = match.groups.formula.replaceAll("{{", "").replaceAll("}}", "");
627
- try {
628
- const result = evaluate2(formulae);
629
- modifiedDice = modifiedDice.replace(match.groups.formula, result.toString());
630
- } catch (error) {
631
- throw new FormulaError(match.groups.formula, "replaceFormulasInDice", error);
632
- }
874
+
875
+ // src/dice/pity.ts
876
+ import { evaluate as evaluate7 } from "mathjs";
877
+ function handlePitySystem(dice, compare, diceRoll, roller, engine) {
878
+ const currentRoll = Array.isArray(diceRoll) ? diceRoll[0] : diceRoll;
879
+ const maxPossible = currentRoll ? currentRoll.maxTotal : null;
880
+ const isComparisonPossible = maxPossible === null || canComparisonSucceed(maxPossible, compare);
881
+ if (!isComparisonPossible) {
882
+ return { rerollCount: 0 };
883
+ }
884
+ let isFail = evaluate7(`${roller.total}${compare.sign}${compare.value}`);
885
+ if (isFail) {
886
+ return { rerollCount: 0 };
887
+ }
888
+ const maxReroll = 100;
889
+ let rerollCount = 0;
890
+ let res;
891
+ while (!isFail && rerollCount < maxReroll) {
892
+ try {
893
+ res = roll(dice, engine, false);
894
+ } catch (error) {
895
+ throw new DiceTypeError(dice, "roll", error);
896
+ }
897
+ rerollCount++;
898
+ if (res && res.total !== void 0) {
899
+ isFail = evaluate7(`${res.total}${compare.sign}${compare.value}`);
633
900
  }
634
901
  }
635
- return cleanedDice(modifiedDice);
636
- }
637
- function cleanedDice(dice) {
638
- return dice.replaceAll("+-", "-").replaceAll("--", "+").replaceAll("++", "+").replaceAll("=>", ">=").replaceAll("=<", "<=").trimEnd();
639
- }
640
- function isNumber(value) {
641
- return value !== void 0 && (typeof value === "number" || !Number.isNaN(Number(value)) && typeof value === "string" && value.trim().length > 0);
642
- }
643
- function replaceExpByRandom(dice, engine = NumberGenerator2.engines.nodeCrypto) {
644
- const diceRegex = /\{exp( ?\|\| ?(?<default>\d+))?}/gi;
645
- return dice.replace(diceRegex, (_match, _p1, _p2, _offset, _string, groups) => {
646
- const defaultValue = groups?.default;
647
- return defaultValue ?? randomInt(1, 999, engine).toString();
648
- });
902
+ return { rerollCount, result: res };
649
903
  }
650
- function randomInt(min, max, engine = NumberGenerator2.engines.nodeCrypto, rng) {
651
- if (!rng) rng = new Random(engine || void 0);
652
- return rng.integer(min, max);
653
- }
654
- function getEngineId(engine) {
655
- if (engine === NumberGenerator2.engines.nodeCrypto) return "nodeCrypto";
656
- if (engine === NumberGenerator2.engines.nativeMath) return "nativeMath";
657
- if (engine === NumberGenerator2.engines.browserCrypto) return "browserCrypto";
904
+
905
+ // src/roll.ts
906
+ function roll(dice, engine = NumberGenerator7.engines.nodeCrypto, pity, sort) {
907
+ if (sort === "none" /* None */) sort = void 0;
908
+ const prepared = prepareDice(dice);
909
+ if (!prepared.dice.includes("d")) return void 0;
910
+ if (prepared.isSharedRoll) {
911
+ return sharedRolls(
912
+ prepared.dice,
913
+ engine,
914
+ pity,
915
+ prepared.explodingSuccess,
916
+ prepared.diceDisplay,
917
+ prepared.isSharedCurly
918
+ );
919
+ }
920
+ let processedDice = fixParenthesis(prepared.dice);
921
+ const modificator = getModifier(processedDice);
922
+ const compareRegex = processedDice.match(SIGN_REGEX_SPACE);
923
+ let compare;
924
+ if (compareRegex && !prepared.isCurlyBulk) {
925
+ const compareResult = getCompare(processedDice, compareRegex, engine, pity);
926
+ processedDice = compareResult.dice;
927
+ compare = compareResult.compare;
928
+ }
929
+ let finalDiceDisplay = prepared.diceDisplay;
930
+ if (prepared.isSimpleCurly && !prepared.diceDisplay.startsWith("{")) {
931
+ finalDiceDisplay = `{${prepared.diceDisplay}}`;
932
+ }
933
+ const bulkProcessContent = prepared.isCurlyBulk ? prepared.bulkContent : processedDice;
934
+ if (bulkProcessContent.match(/\d+?#(.*)/)) {
935
+ return handleBulkRolls(
936
+ processedDice,
937
+ prepared.isCurlyBulk,
938
+ prepared.bulkContent,
939
+ compare,
940
+ prepared.explodingSuccess,
941
+ prepared.diceDisplay,
942
+ engine,
943
+ sort
944
+ );
945
+ }
946
+ const roller = new DiceRoller3();
947
+ NumberGenerator7.generator.engine = engine;
948
+ let diceWithoutComment = processedDice.replace(COMMENT_REGEX, "").trimEnd();
949
+ diceWithoutComment = setSortOrder(diceWithoutComment, sort);
950
+ let diceRoll;
658
951
  try {
659
- const e = engine;
660
- if (e && typeof e === "object") {
661
- if (typeof e.name === "string" && e.name) return e.name;
662
- if (e.constructor?.name) return e.constructor.name;
952
+ diceRoll = roller.roll(diceWithoutComment);
953
+ } catch (error) {
954
+ throw new DiceTypeError(diceWithoutComment, "roll", error);
955
+ }
956
+ if (compare && diceRoll) {
957
+ const currentRoll = Array.isArray(diceRoll) ? diceRoll[0] : diceRoll;
958
+ const trivial = isTrivialComparison(
959
+ currentRoll.maxTotal,
960
+ currentRoll.minTotal,
961
+ compare
962
+ );
963
+ compare.trivial = trivial ? true : void 0;
964
+ }
965
+ const commentMatch = processedDice.match(COMMENT_REGEX);
966
+ const comment = commentMatch ? commentMatch[2] : void 0;
967
+ let rerollCount = 0;
968
+ let pityResult;
969
+ if (pity && compare) {
970
+ const pityData = handlePitySystem(
971
+ diceWithoutComment,
972
+ compare,
973
+ diceRoll,
974
+ roller,
975
+ engine
976
+ );
977
+ rerollCount = pityData.rerollCount;
978
+ pityResult = pityData.result;
979
+ if (pityResult) {
980
+ return {
981
+ ...pityResult,
982
+ dice: prepared.isSimpleCurly ? finalDiceDisplay : processedDice,
983
+ comment,
984
+ compare,
985
+ modifier: modificator,
986
+ pityLogs: rerollCount,
987
+ trivial: pityResult.trivial ?? (compare?.trivial ? true : void 0)
988
+ };
663
989
  }
664
- } catch {
665
990
  }
666
- return "unknown";
667
- }
668
- function getEngine(engine) {
669
- switch (engine) {
670
- case "nativeMath":
671
- return NumberGenerator2.engines.nativeMath;
672
- case "browserCrypto":
673
- return NumberGenerator2.engines.browserCrypto;
674
- case "nodeCrypto":
675
- return NumberGenerator2.engines.nodeCrypto;
676
- default:
677
- return NumberGenerator2.engines.nativeMath;
991
+ let resultOutput = replaceUnwantedText(roller.output);
992
+ if (prepared.explodingSuccess) {
993
+ const successes = countExplodingSuccesses(
994
+ diceRoll,
995
+ prepared.explodingSuccess.sign,
996
+ prepared.explodingSuccess.value
997
+ );
998
+ resultOutput = resultOutput.replace(/=\s*-?\d+(?:\.\d+)?$/, `= ${successes}`).replace(
999
+ prepared.explodingSuccess.normalizedSegment,
1000
+ prepared.explodingSuccess.originalSegment
1001
+ );
1002
+ return {
1003
+ dice: prepared.isSimpleCurly ? finalDiceDisplay : prepared.diceDisplay,
1004
+ result: resultOutput,
1005
+ comment,
1006
+ compare: compare ? compare : void 0,
1007
+ modifier: modificator,
1008
+ total: successes,
1009
+ pityLogs: rerollCount > 0 ? rerollCount : void 0,
1010
+ trivial: compare?.trivial ? true : void 0
1011
+ };
678
1012
  }
1013
+ return {
1014
+ dice: prepared.isSimpleCurly ? finalDiceDisplay : processedDice,
1015
+ result: resultOutput,
1016
+ comment,
1017
+ compare: compare ? compare : void 0,
1018
+ modifier: modificator,
1019
+ total: roller.total,
1020
+ pityLogs: rerollCount > 0 ? rerollCount : void 0,
1021
+ trivial: compare?.trivial ? true : void 0
1022
+ };
679
1023
  }
680
- function levenshteinDistance(a, b) {
681
- if (a === b) return 0;
682
- const al = a.length;
683
- const bl = b.length;
684
- if (al === 0) return bl;
685
- if (bl === 0) return al;
686
- const v0 = new Array(bl + 1);
687
- const v1 = new Array(bl + 1);
688
- for (let i = 0; i <= bl; i++) v0[i] = i;
689
- for (let i = 0; i < al; i++) {
690
- v1[0] = i + 1;
691
- for (let j = 0; j < bl; j++) {
692
- const cost = a[i] === b[j] ? 0 : 1;
693
- v1[j + 1] = Math.min(v1[j] + 1, v0[j + 1] + 1, v0[j] + cost);
1024
+ function sharedRolls(dice, engine = NumberGenerator7.engines.nodeCrypto, pity, explodingSuccessMain, diceDisplay, isSharedCurly) {
1025
+ if (!explodingSuccessMain)
1026
+ explodingSuccessMain = normalizeExplodingSuccess(dice.split(";")[0] ?? dice);
1027
+ if (explodingSuccessMain) {
1028
+ dice = dice.replace(explodingSuccessMain.originalSegment, "!");
1029
+ }
1030
+ if (dice.match(/\d+?#(.*?)/))
1031
+ throw new DiceTypeError(
1032
+ dice,
1033
+ "noBulkRoll",
1034
+ "bulk roll are not allowed in shared rolls"
1035
+ );
1036
+ const results = [];
1037
+ const mainComment = /\s+#(?<comment>.*)/.exec(dice)?.groups?.comment?.trimEnd() ?? void 0;
1038
+ const split = dice.split(";");
1039
+ const displayDice = diceDisplay ?? explodingSuccessMain?.originalDice ?? split[0];
1040
+ let diceMain = fixParenthesis(split[0]);
1041
+ const commentsRegex = /\[(?<comments>.*?)\]/gi;
1042
+ const comments = formatComment(diceMain);
1043
+ const diceMainWithoutComments = diceMain.replace(commentsRegex, "").trim();
1044
+ const toHideRegex = /\((?<dice>[^)]+)\)/;
1045
+ const toHide = toHideRegex.exec(diceMainWithoutComments)?.groups;
1046
+ let hidden = false;
1047
+ if (toHide?.dice) {
1048
+ diceMain = toHide.dice;
1049
+ hidden = true;
1050
+ } else if (toHide) {
1051
+ diceMain = "1d1";
1052
+ hidden = true;
1053
+ } else {
1054
+ diceMain = diceMainWithoutComments;
1055
+ }
1056
+ const rollBounds = getRollBounds(diceMain, engine);
1057
+ let diceResult = roll(diceMain, engine, pity);
1058
+ if (!diceResult || !diceResult.total) {
1059
+ if (hidden) {
1060
+ diceResult = roll(fixParenthesis(split[0]), engine, pity);
1061
+ hidden = false;
1062
+ } else return void 0;
1063
+ }
1064
+ if (!diceResult || !diceResult.total) return void 0;
1065
+ if (explodingSuccessMain && diceResult.result) {
1066
+ const values = extractValuesFromOutput(diceResult.result);
1067
+ diceResult.total = values.filter(
1068
+ (v) => matchComparison(explodingSuccessMain.sign, v, explodingSuccessMain.value)
1069
+ ).length;
1070
+ }
1071
+ let aggregatedCompare = diceResult.compare;
1072
+ let hasTrivialComparison = diceResult.compare?.trivial === true;
1073
+ results.push(`\u203B ${comments}${diceResult.result}`);
1074
+ let total = diceResult.total;
1075
+ diceResult.comment = mainComment;
1076
+ if (!total) {
1077
+ return {
1078
+ dice: displayDice,
1079
+ result: results.join(";"),
1080
+ comment: mainComment,
1081
+ compare: aggregatedCompare,
1082
+ modifier: diceResult.modifier,
1083
+ total,
1084
+ trivial: hasTrivialComparison ? true : void 0
1085
+ };
1086
+ }
1087
+ for (let element of split.slice(1)) {
1088
+ const comment = formatComment(element);
1089
+ element = element.replaceAll(commentsRegex, "").replaceAll(OPTIONAL_COMMENT, "").trim();
1090
+ let toRoll = element.replace(SYMBOL_DICE, `${diceResult.total}`);
1091
+ const compareRegex = toRoll.match(SIGN_REGEX_SPACE);
1092
+ if (compareRegex) {
1093
+ if (isSharedCurly) {
1094
+ const compareResult = compareSignFormule(
1095
+ toRoll,
1096
+ compareRegex,
1097
+ element,
1098
+ diceResult,
1099
+ engine,
1100
+ pity,
1101
+ rollBounds
1102
+ );
1103
+ const { diceAll } = replaceText(element, diceResult.total, diceResult.dice);
1104
+ let successCount = 0;
1105
+ try {
1106
+ const evaluated = evaluate8(toRoll);
1107
+ successCount = evaluated ? 1 : 0;
1108
+ } catch (error) {
1109
+ const evaluated = roll(toRoll, engine, pity);
1110
+ successCount = evaluated?.total ?? 0 ? 1 : 0;
1111
+ }
1112
+ results.push(`\u203B ${comment}${diceAll}: ${successCount}`);
1113
+ total += successCount;
1114
+ if (!aggregatedCompare && compareResult.compare)
1115
+ aggregatedCompare = compareResult.compare;
1116
+ if (compareResult.trivial) hasTrivialComparison = true;
1117
+ } else {
1118
+ const compareResult = compareSignFormule(
1119
+ toRoll,
1120
+ compareRegex,
1121
+ element,
1122
+ diceResult,
1123
+ engine,
1124
+ pity,
1125
+ rollBounds
1126
+ );
1127
+ toRoll = compareResult.dice;
1128
+ results.push(compareResult.results);
1129
+ if (!aggregatedCompare && compareResult.compare)
1130
+ aggregatedCompare = compareResult.compare;
1131
+ if (compareResult.trivial) hasTrivialComparison = true;
1132
+ }
1133
+ } else {
1134
+ const { formule, diceAll } = replaceText(
1135
+ element,
1136
+ diceResult.total,
1137
+ diceResult.dice
1138
+ );
1139
+ try {
1140
+ const evaluated = evaluate8(toRoll);
1141
+ results.push(`\u25C8 ${comment}${diceAll}: ${formule} = ${evaluated}`);
1142
+ total += Number.parseInt(evaluated, 10);
1143
+ } catch (error) {
1144
+ const evaluated = roll(toRoll, engine, pity);
1145
+ if (evaluated) {
1146
+ results.push(
1147
+ `\u25C8 ${comment}${diceAll}: ${evaluated.result.split(":").slice(1).join(":")}`
1148
+ );
1149
+ if (!aggregatedCompare && evaluated.compare)
1150
+ aggregatedCompare = evaluated.compare;
1151
+ if (evaluated.compare?.trivial) hasTrivialComparison = true;
1152
+ } else results.push(`\u25C8 ${comment}${diceAll}: ${formule} = ${evaluated}`);
1153
+ total += evaluated?.total ?? 0;
1154
+ }
694
1155
  }
695
- for (let j = 0; j <= bl; j++) v0[j] = v1[j];
696
1156
  }
697
- return v1[bl];
1157
+ if (hidden)
1158
+ results.shift();
1159
+ return {
1160
+ dice: displayDice,
1161
+ result: results.join(";"),
1162
+ comment: mainComment,
1163
+ compare: hasTrivialComparison && aggregatedCompare ? { ...aggregatedCompare, trivial: true } : aggregatedCompare,
1164
+ modifier: diceResult.modifier,
1165
+ total,
1166
+ trivial: hasTrivialComparison ? true : void 0
1167
+ };
698
1168
  }
699
- function similarityScore(a, b) {
700
- const la = a.length;
701
- const lb = b.length;
702
- if (la === 0 && lb === 0) return 1;
703
- const dist = levenshteinDistance(a, b);
704
- const max = Math.max(la, lb);
705
- return 1 - dist / max;
1169
+ function replaceInFormula(element, diceResult, compareResult, res, engine = NumberGenerator7.engines.nodeCrypto, pity) {
1170
+ const { formule, diceAll } = replaceText(
1171
+ element,
1172
+ diceResult.total ?? 0,
1173
+ diceResult.dice
1174
+ );
1175
+ const validSign = res ? "\u2713" : "\u2715";
1176
+ const invertedSign = res ? compareResult.compare.sign : inverseSign(compareResult.compare.sign);
1177
+ let evaluateRoll;
1178
+ try {
1179
+ evaluateRoll = evaluate8(compareResult.dice);
1180
+ return `${validSign} ${diceAll}: ${formule} = ${evaluateRoll}${invertedSign}${compareResult.compare?.value}`;
1181
+ } catch (error) {
1182
+ const evaluateRoll2 = roll(compareResult.dice, engine, pity);
1183
+ if (evaluateRoll2)
1184
+ return `${validSign} ${diceAll}: ${evaluateRoll2.result.split(":").splice(1).join(":")}`;
1185
+ return `${validSign} ${diceAll}: ${formule} = ${evaluateRoll2}${invertedSign}${compareResult.compare?.value}`;
1186
+ }
706
1187
  }
707
1188
 
708
1189
  // src/verify_template.ts
709
- import { evaluate as evaluate3 } from "mathjs";
1190
+ import { evaluate as evaluate9 } from "mathjs";
710
1191
  import { Random as Random2 } from "random-js";
711
1192
  import "uniformize";
712
- import { NumberGenerator as NumberGenerator3 } from "@dice-roller/rpg-dice-roller";
713
- function evalStatsDice(testDice, allStats, engine = NumberGenerator3.engines.nodeCrypto, pity) {
1193
+ import { NumberGenerator as NumberGenerator8 } from "@dice-roller/rpg-dice-roller";
1194
+ function evalStatsDice(testDice, allStats, engine = NumberGenerator8.engines.nodeCrypto, pity) {
714
1195
  let dice = testDice.trimEnd();
715
1196
  if (allStats && Object.keys(allStats).length > 0) {
716
1197
  const names = Object.keys(allStats);
@@ -730,7 +1211,7 @@ function evalStatsDice(testDice, allStats, engine = NumberGenerator3.engines.nod
730
1211
  throw new DiceTypeError(dice, "evalStatsDice", error);
731
1212
  }
732
1213
  }
733
- function diceRandomParse(value, template, engine = NumberGenerator3.engines.nodeCrypto) {
1214
+ function diceRandomParse(value, template, engine = NumberGenerator8.engines.nodeCrypto) {
734
1215
  if (!template.statistics) return replaceFormulaInDice(value.standardize());
735
1216
  value = value.standardize();
736
1217
  const statNames = Object.keys(template.statistics);
@@ -752,7 +1233,7 @@ function diceRandomParse(value, template, engine = NumberGenerator3.engines.node
752
1233
  }
753
1234
  return replaceFormulaInDice(newDice);
754
1235
  }
755
- function diceTypeRandomParse(dice, template, engine = NumberGenerator3.engines.nodeCrypto) {
1236
+ function diceTypeRandomParse(dice, template, engine = NumberGenerator8.engines.nodeCrypto) {
756
1237
  dice = replaceExpByRandom(dice);
757
1238
  if (!template.statistics) return dice;
758
1239
  const firstStatNotcombinaison = Object.keys(template.statistics).find(
@@ -774,7 +1255,7 @@ function evalCombinaison(combinaison, stats) {
774
1255
  formula = formula.replace(regex, value.toString());
775
1256
  }
776
1257
  try {
777
- newStats[stat] = evaluate3(formula);
1258
+ newStats[stat] = evaluate9(formula);
778
1259
  } catch (error) {
779
1260
  throw new FormulaError(stat, "evalCombinaison", error);
780
1261
  }
@@ -788,7 +1269,7 @@ function evalOneCombinaison(combinaison, stats) {
788
1269
  formula = formula.replace(regex, value.toString());
789
1270
  }
790
1271
  try {
791
- return evaluate3(formula);
1272
+ return evaluate9(formula);
792
1273
  } catch (error) {
793
1274
  throw new FormulaError(combinaison, "evalOneCombinaison", error);
794
1275
  }
@@ -800,7 +1281,7 @@ function convertNumber(number) {
800
1281
  if (isNumber(number)) return Number.parseInt(number.toString(), 10);
801
1282
  return void 0;
802
1283
  }
803
- function verifyTemplateValue(template, verify = true, engine = NumberGenerator3.engines.nodeCrypto) {
1284
+ function verifyTemplateValue(template, verify = true, engine = NumberGenerator8.engines.nodeCrypto) {
804
1285
  const parsedTemplate = templateSchema.parse(template);
805
1286
  const { success, failure } = parsedTemplate.critical ?? {};
806
1287
  const criticicalVal = {
@@ -855,7 +1336,7 @@ function verifyTemplateValue(template, verify = true, engine = NumberGenerator3.
855
1336
  testStatCombinaison(statistiqueTemplate, engine);
856
1337
  return statistiqueTemplate;
857
1338
  }
858
- function testDiceRegistered(template, engine = NumberGenerator3.engines.nodeCrypto) {
1339
+ function testDiceRegistered(template, engine = NumberGenerator8.engines.nodeCrypto) {
859
1340
  if (!template.damage) return;
860
1341
  if (Object.keys(template.damage).length === 0) throw new EmptyObjectError();
861
1342
  if (Object.keys(template.damage).length > 25) throw new TooManyDice();
@@ -871,7 +1352,7 @@ function testDiceRegistered(template, engine = NumberGenerator3.engines.nodeCryp
871
1352
  }
872
1353
  }
873
1354
  }
874
- function testStatCombinaison(template, engine = NumberGenerator3.engines.nodeCrypto) {
1355
+ function testStatCombinaison(template, engine = NumberGenerator8.engines.nodeCrypto) {
875
1356
  if (!template.statistics) return;
876
1357
  const onlycombinaisonStats = Object.fromEntries(
877
1358
  Object.entries(template.statistics).filter(
@@ -897,7 +1378,7 @@ function testStatCombinaison(template, engine = NumberGenerator3.engines.nodeCry
897
1378
  formula = formula.replace(regex, randomStatValue.toString());
898
1379
  }
899
1380
  try {
900
- evaluate3(formula);
1381
+ evaluate9(formula);
901
1382
  } catch (e) {
902
1383
  error.push(stat);
903
1384
  }
@@ -905,9 +1386,9 @@ function testStatCombinaison(template, engine = NumberGenerator3.engines.nodeCry
905
1386
  if (error.length > 0) throw new FormulaError(error.join(", "), "testStatCombinaison");
906
1387
  return;
907
1388
  }
908
- function generateRandomStat(total = 100, max, min, engine = NumberGenerator3.engines.nodeCrypto) {
1389
+ function generateRandomStat(total = 100, max, min, engine = NumberGenerator8.engines.nodeCrypto) {
909
1390
  let randomStatValue = total + 1;
910
- const random = new Random2(engine || NumberGenerator3.engines.nodeCrypto);
1391
+ const random = new Random2(engine || NumberGenerator8.engines.nodeCrypto);
911
1392
  while (randomStatValue >= total || randomStatValue === 0) {
912
1393
  if (max && min) randomStatValue = randomInt(min, max, engine, random);
913
1394
  else if (max) randomStatValue = randomInt(1, max, engine, random);
@@ -928,9 +1409,9 @@ export {
928
1409
  SIGN_REGEX,
929
1410
  SIGN_REGEX_SPACE,
930
1411
  SYMBOL_DICE,
1412
+ SortOrder,
931
1413
  TooManyDice,
932
1414
  TooManyStats,
933
- calculator,
934
1415
  createCriticalCustom,
935
1416
  diceRandomParse,
936
1417
  diceTypeRandomParse,
@@ -946,6 +1427,7 @@ export {
946
1427
  randomInt,
947
1428
  replaceExpByRandom,
948
1429
  replaceFormulaInDice,
1430
+ replaceInFormula,
949
1431
  roll,
950
1432
  standardizeDice,
951
1433
  templateSchema,