@dicelette/core 1.24.0 → 1.24.2

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