@dicelette/core 1.24.0 → 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.js CHANGED
@@ -23,7 +23,6 @@ __export(index_exports, {
23
23
  COMMENT_REGEX: () => COMMENT_REGEX,
24
24
  DETECT_CRITICAL: () => DETECT_CRITICAL,
25
25
  DiceTypeError: () => DiceTypeError,
26
- EXPLODING_SUCCESS_REGEX: () => EXPLODING_SUCCESS_REGEX,
27
26
  EmptyObjectError: () => EmptyObjectError,
28
27
  FormulaError: () => FormulaError,
29
28
  MaxGreater: () => MaxGreater,
@@ -35,11 +34,6 @@ __export(index_exports, {
35
34
  SortOrder: () => SortOrder,
36
35
  TooManyDice: () => TooManyDice,
37
36
  TooManyStats: () => TooManyStats,
38
- calculator: () => calculator,
39
- canComparisonFail: () => canComparisonFail,
40
- canComparisonSucceed: () => canComparisonSucceed,
41
- compareSignFormule: () => compareSignFormule,
42
- countExplodingSuccesses: () => countExplodingSuccesses,
43
37
  createCriticalCustom: () => createCriticalCustom,
44
38
  diceRandomParse: () => diceRandomParse,
45
39
  diceTypeRandomParse: () => diceTypeRandomParse,
@@ -47,29 +41,16 @@ __export(index_exports, {
47
41
  evalCombinaison: () => evalCombinaison,
48
42
  evalOneCombinaison: () => evalOneCombinaison,
49
43
  evalStatsDice: () => evalStatsDice,
50
- extractValuesFromOutput: () => extractValuesFromOutput,
51
- fixParenthesis: () => fixParenthesis,
52
- formatComment: () => formatComment,
53
44
  generateRandomStat: () => generateRandomStat,
54
45
  generateStatsDice: () => generateStatsDice,
55
- getCompare: () => getCompare,
56
46
  getEngine: () => getEngine,
57
47
  getEngineId: () => getEngineId,
58
- getModifier: () => getModifier,
59
- getRollBounds: () => getRollBounds,
60
- inverseSign: () => inverseSign,
61
48
  isNumber: () => isNumber,
62
- isTrivialComparison: () => isTrivialComparison,
63
- matchComparison: () => matchComparison,
64
- normalizeExplodingSuccess: () => normalizeExplodingSuccess,
65
49
  randomInt: () => randomInt,
66
50
  replaceExpByRandom: () => replaceExpByRandom,
67
51
  replaceFormulaInDice: () => replaceFormulaInDice,
68
52
  replaceInFormula: () => replaceInFormula,
69
- replaceText: () => replaceText,
70
- replaceUnwantedText: () => replaceUnwantedText,
71
53
  roll: () => roll,
72
- rollCompare: () => rollCompare,
73
54
  standardizeDice: () => standardizeDice,
74
55
  templateSchema: () => templateSchema,
75
56
  testDiceRegistered: () => testDiceRegistered,
@@ -78,16 +59,107 @@ __export(index_exports, {
78
59
  });
79
60
  module.exports = __toCommonJS(index_exports);
80
61
 
81
- // src/dice/calculator.ts
82
- var import_mathjs = require("mathjs");
83
- function calculator(sign, value, total) {
84
- if (sign === "^") sign = "**";
85
- return (0, import_mathjs.evaluate)(`${total} ${sign} ${value}`);
62
+ // src/engine.ts
63
+ var import_rpg_dice_roller = require("@dice-roller/rpg-dice-roller");
64
+ function getEngineId(engine) {
65
+ if (engine === import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto) return "nodeCrypto";
66
+ if (engine === import_rpg_dice_roller.NumberGenerator.engines.nativeMath) return "nativeMath";
67
+ if (engine === import_rpg_dice_roller.NumberGenerator.engines.browserCrypto) return "browserCrypto";
68
+ try {
69
+ const e = engine;
70
+ if (e && typeof e === "object") {
71
+ if (typeof e.name === "string" && e.name) return e.name;
72
+ if (e.constructor?.name) return e.constructor.name;
73
+ }
74
+ } catch {
75
+ }
76
+ return "unknown";
77
+ }
78
+ function getEngine(engine) {
79
+ switch (engine) {
80
+ case "nativeMath":
81
+ return import_rpg_dice_roller.NumberGenerator.engines.nativeMath;
82
+ case "browserCrypto":
83
+ return import_rpg_dice_roller.NumberGenerator.engines.browserCrypto;
84
+ case "nodeCrypto":
85
+ return import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto;
86
+ default:
87
+ return import_rpg_dice_roller.NumberGenerator.engines.nativeMath;
88
+ }
86
89
  }
87
90
 
88
- // src/dice/compare.ts
89
- var import_rpg_dice_roller2 = require("@dice-roller/rpg-dice-roller");
90
- var import_mathjs3 = require("mathjs");
91
+ // src/errors.ts
92
+ var DiceTypeError = class extends Error {
93
+ dice;
94
+ cause;
95
+ method;
96
+ constructor(dice, cause, method) {
97
+ super(dice);
98
+ this.name = "Invalid_Dice_Type";
99
+ this.dice = dice;
100
+ this.cause = cause;
101
+ this.method = method;
102
+ }
103
+ };
104
+ var FormulaError = class extends Error {
105
+ formula;
106
+ cause;
107
+ method;
108
+ constructor(formula, cause, method) {
109
+ super(formula);
110
+ this.name = "Invalid_Formula";
111
+ this.formula = formula;
112
+ this.cause = cause;
113
+ this.method = method;
114
+ }
115
+ };
116
+ var MaxGreater = class extends Error {
117
+ name;
118
+ value;
119
+ max;
120
+ constructor(value, max) {
121
+ super(value.toString());
122
+ this.name = "Max_Greater";
123
+ this.value = value;
124
+ this.max = max;
125
+ }
126
+ };
127
+ var EmptyObjectError = class extends Error {
128
+ name;
129
+ constructor() {
130
+ super();
131
+ this.name = "Empty_Object";
132
+ }
133
+ };
134
+ var TooManyDice = class extends Error {
135
+ name;
136
+ constructor() {
137
+ super();
138
+ this.name = "Too_Many_Dice";
139
+ }
140
+ };
141
+ var TooManyStats = class extends Error {
142
+ name;
143
+ constructor() {
144
+ super();
145
+ this.name = "Too_Many_Stats";
146
+ }
147
+ };
148
+ var NoStatisticsError = class extends Error {
149
+ name;
150
+ constructor() {
151
+ super();
152
+ this.name = "No_Statistics";
153
+ }
154
+ };
155
+
156
+ // src/interfaces/index.ts
157
+ var SortOrder = /* @__PURE__ */ ((SortOrder2) => {
158
+ SortOrder2["Ascending"] = "sa";
159
+ SortOrder2["Descending"] = "sd";
160
+ SortOrder2["None"] = "none";
161
+ return SortOrder2;
162
+ })(SortOrder || {});
91
163
 
92
164
  // src/interfaces/constant.ts
93
165
  var COMMENT_REGEX = /\s+(#|\/{2}|\[|\/\*)(?<comment>.*)/gi;
@@ -97,418 +169,231 @@ var SYMBOL_DICE = "&";
97
169
  var DETECT_CRITICAL = /\{\*?c[fs]:([<>=]|!=)+(.+?)}/gim;
98
170
  var OPTIONAL_COMMENT = /\s+(#|\/{2}|\[|\/\*)?(?<comment>.*)/gi;
99
171
 
172
+ // src/interfaces/zod.ts
173
+ var import_zod = require("zod");
174
+ var statisticValueSchema = import_zod.z.object({
175
+ max: import_zod.z.number().transform((val) => val === 0 ? void 0 : val).optional(),
176
+ min: import_zod.z.number().transform(
177
+ (val) => Number.isNaN(Number.parseInt(val, 10)) ? void 0 : val
178
+ ).optional(),
179
+ combinaison: import_zod.z.string().transform((str) => str.trim() || void 0).optional(),
180
+ exclude: import_zod.z.boolean().optional()
181
+ }).superRefine((data, ctx) => {
182
+ if (data.max !== void 0 && data.min !== void 0 && data.max <= data.min) {
183
+ ctx.addIssue({
184
+ code: "custom",
185
+ message: `Max_Greater; ${data.min}; ${data.max}`,
186
+ path: ["max"]
187
+ });
188
+ }
189
+ });
190
+ var statisticSchema = import_zod.z.record(import_zod.z.string(), statisticValueSchema).optional().refine((stats) => !stats || Object.keys(stats).length <= 25, {
191
+ message: "TooManyStats"
192
+ });
193
+ var criticalSchema = import_zod.z.object({
194
+ success: import_zod.z.string().or(import_zod.z.number().min(0)).optional(),
195
+ failure: import_zod.z.string().or(import_zod.z.number().min(0)).optional()
196
+ }).transform((values) => {
197
+ if (values.success === "") values.success = void 0;
198
+ if (values.failure === "") values.failure = void 0;
199
+ if (values.failure === 0) values.failure = void 0;
200
+ if (values.success === 0) values.success = void 0;
201
+ values.success = Number.parseInt(values.success, 10);
202
+ values.failure = Number.parseInt(values.failure, 10);
203
+ return values;
204
+ });
205
+ var criticalValueSchema = import_zod.z.object({
206
+ sign: import_zod.z.enum(["<", ">", "<=", ">=", "!=", "=="]),
207
+ value: import_zod.z.string(),
208
+ onNaturalDice: import_zod.z.boolean().optional(),
209
+ affectSkill: import_zod.z.boolean().optional()
210
+ });
211
+ var damageSchema = import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional().refine((stats) => !stats || Object.keys(stats).length <= 25, {
212
+ message: "TooManyDice"
213
+ });
214
+ var customCriticalSchema = import_zod.z.record(import_zod.z.string(), criticalValueSchema).optional().refine((stats) => !stats || Object.keys(stats).length <= 22, {
215
+ message: "TooManyDice"
216
+ });
217
+ var templateSchema = import_zod.z.object({
218
+ charName: import_zod.z.boolean().optional(),
219
+ statistics: statisticSchema,
220
+ total: import_zod.z.number().min(0).transform((val) => val === 0 ? void 0 : val).optional(),
221
+ forceDistrib: import_zod.z.boolean().optional(),
222
+ diceType: import_zod.z.string().optional(),
223
+ critical: criticalSchema.optional(),
224
+ customCritical: customCriticalSchema,
225
+ damage: damageSchema
226
+ });
227
+
100
228
  // src/roll.ts
101
- var import_rpg_dice_roller = require("@dice-roller/rpg-dice-roller");
229
+ var import_rpg_dice_roller7 = require("@dice-roller/rpg-dice-roller");
230
+ var import_mathjs8 = require("mathjs");
231
+
232
+ // src/dice/bulk.ts
233
+ var import_rpg_dice_roller6 = require("@dice-roller/rpg-dice-roller");
234
+ var import_mathjs6 = require("mathjs");
235
+
236
+ // src/dice/compare.ts
237
+ var import_rpg_dice_roller3 = require("@dice-roller/rpg-dice-roller");
102
238
  var import_mathjs2 = require("mathjs");
103
- function roll(dice, engine = import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto, pity, sort) {
104
- if (sort === "none" /* None */) sort = void 0;
105
- dice = standardizeDice(replaceFormulaInDice(dice)).replace(/^\+/, "").replaceAll("=>", ">=").replaceAll("=<", "<=").trimStart();
106
- if (!dice.includes("d")) return void 0;
107
- dice = dice.replaceAll(DETECT_CRITICAL, "").trimEnd();
108
- const explodingSuccess = normalizeExplodingSuccess(dice);
109
- if (explodingSuccess) dice = explodingSuccess.dice;
110
- let diceDisplay;
111
- if (dice.includes(";")) {
112
- const mainDice = dice.split(";")[0];
113
- diceDisplay = explodingSuccess?.originalDice ?? mainDice;
114
- } else diceDisplay = explodingSuccess?.originalDice ?? dice;
115
- const curlyBulkMatch = dice.match(/^\{(\d+#.*)\}$/);
116
- const isCurlyBulk = !!curlyBulkMatch;
117
- let bulkContent = "";
118
- if (isCurlyBulk) bulkContent = curlyBulkMatch[1];
119
- const compareRegex = dice.match(SIGN_REGEX_SPACE);
120
- let compare;
121
- if (dice.includes(";"))
122
- return sharedRolls(dice, engine, pity, explodingSuccess, diceDisplay);
123
- dice = fixParenthesis(dice);
124
- const modificator = getModifier(dice);
125
- if (compareRegex && !isCurlyBulk) {
126
- const compareResult = getCompare(dice, compareRegex, engine, pity);
127
- dice = compareResult.dice;
128
- compare = compareResult.compare;
129
- }
130
- const bulkProcessContent = isCurlyBulk ? bulkContent : dice;
131
- if (bulkProcessContent.match(/\d+?#(.*)/)) {
132
- const diceArray = bulkProcessContent.split("#");
133
- const numberOfDice = Number.parseInt(diceArray[0], 10);
134
- let diceToRoll = diceArray[1].replace(COMMENT_REGEX, "");
135
- const commentsMatch = diceArray[1].match(COMMENT_REGEX);
136
- const comments = commentsMatch ? commentsMatch[2] : void 0;
137
- let curlyCompare;
138
- if (isCurlyBulk) {
139
- const curlyCompareRegex = diceToRoll.match(SIGN_REGEX_SPACE);
140
- if (curlyCompareRegex) {
141
- const compareSign = curlyCompareRegex[0].match(SIGN_REGEX)?.[0];
142
- const compareValue = curlyCompareRegex[2];
143
- if (compareSign && compareValue) {
144
- curlyCompare = {
145
- sign: compareSign,
146
- value: Number.parseInt(compareValue, 10)
147
- };
148
- diceToRoll = diceToRoll.replace(SIGN_REGEX_SPACE, "");
149
- }
239
+
240
+ // src/utils.ts
241
+ var import_mathjs = require("mathjs");
242
+ var import_uniformize = require("uniformize");
243
+ var import_rpg_dice_roller2 = require("@dice-roller/rpg-dice-roller");
244
+ var import_random_js = require("random-js");
245
+ function escapeRegex(string) {
246
+ return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
247
+ }
248
+ function standardizeDice(dice) {
249
+ return dice.replace(
250
+ /(\[[^\]]+])|([^[]+)/g,
251
+ (_match, insideBrackets, outsideText) => insideBrackets ? insideBrackets : outsideText.standardize().replaceAll("df", "dF")
252
+ );
253
+ }
254
+ function generateStatsDice(originalDice, stats, dollarValue) {
255
+ let dice = originalDice.standardize();
256
+ if (stats && Object.keys(stats).length > 0) {
257
+ const statKeys = Object.keys(stats);
258
+ const partsRegex = /(\[[^\]]+])|([^[]+)/g;
259
+ let result = "";
260
+ let match;
261
+ while ((match = partsRegex.exec(dice)) !== null) {
262
+ const insideBrackets = match[1];
263
+ const outsideText = match[2];
264
+ if (insideBrackets) {
265
+ result += insideBrackets;
266
+ continue;
150
267
  }
151
- }
152
- if (sort) diceToRoll = `${diceToRoll}${sort}`;
153
- const activeCompare = compare || curlyCompare || (explodingSuccess ? { sign: explodingSuccess.sign, value: explodingSuccess.value } : void 0);
154
- let trivialComparisonDetected = false;
155
- if (activeCompare) {
156
- const results = [];
157
- let successCount = 0;
158
- const roller3 = new import_rpg_dice_roller.DiceRoller();
159
- import_rpg_dice_roller.NumberGenerator.generator.engine = engine;
160
- const formatOutput = (output, addStar) => {
161
- const formatted = addStar && isCurlyBulk ? output.replace(
162
- /\[([^\]]+)\]/,
163
- (_m, content) => `[${content.split(",").map((d) => `${d.trim()}*`).join(", ")}]`
164
- ) : output;
165
- return curlyCompare ? formatted.replace(/^([^:]+):/, `$1${curlyCompare.sign}${curlyCompare.value}:`) : formatted;
166
- };
167
- for (let i = 0; i < numberOfDice; i++) {
168
- try {
169
- const individualRoll = roller3.roll(diceToRoll);
170
- const rollInstance = Array.isArray(individualRoll) ? individualRoll[0] : individualRoll;
171
- if (!trivialComparisonDetected && activeCompare) {
172
- const { maxTotal, minTotal } = rollInstance;
173
- trivialComparisonDetected = isTrivialComparison(
174
- maxTotal,
175
- minTotal,
176
- activeCompare
177
- );
268
+ if (!outsideText) {
269
+ continue;
270
+ }
271
+ const tokenRegex = /([\p{L}\p{N}_]+)/gu;
272
+ let lastIndex = 0;
273
+ let tokenMatch;
274
+ while ((tokenMatch = tokenRegex.exec(outsideText)) !== null) {
275
+ result += outsideText.slice(lastIndex, tokenMatch.index);
276
+ const token = tokenMatch[0];
277
+ const tokenStd = token.standardize();
278
+ const diceMatch = /^(\d*)d(.+)$/i.exec(tokenStd);
279
+ if (diceMatch) {
280
+ const diceCount = diceMatch[1] || "";
281
+ const afterD = diceMatch[2];
282
+ let foundStatAfterD = false;
283
+ for (const key of statKeys) {
284
+ const keyStd = key.standardize();
285
+ if (afterD === keyStd) {
286
+ result += `${diceCount}d${stats[key].toString()}`;
287
+ foundStatAfterD = true;
288
+ break;
289
+ }
178
290
  }
179
- const rollOutput = rollInstance.output;
180
- if (explodingSuccess) {
181
- const successesForRoll = countExplodingSuccesses(
182
- rollInstance,
183
- explodingSuccess.sign,
184
- explodingSuccess.value
185
- );
186
- successCount += successesForRoll;
187
- let formattedRollOutput = rollOutput.replace(
188
- explodingSuccess.normalizedSegment,
189
- explodingSuccess.originalSegment
190
- ).replace(/=\s*-?\d+(?:\.\d+)?$/, `= ${successesForRoll}`);
191
- formattedRollOutput = formatOutput(formattedRollOutput, false);
192
- results.push(formattedRollOutput);
193
- } else {
194
- const rollTotal = rollInstance.total;
195
- const isSuccess = (0, import_mathjs2.evaluate)(
196
- `${rollTotal}${activeCompare.sign}${activeCompare.value}`
197
- );
198
- if (isSuccess) successCount++;
199
- results.push(formatOutput(rollOutput, isSuccess));
291
+ if (foundStatAfterD) {
292
+ lastIndex = tokenRegex.lastIndex;
293
+ continue;
200
294
  }
201
- } catch (error) {
202
- throw new DiceTypeError(diceToRoll, "roll", error);
203
295
  }
204
- }
205
- const signSource = explodingSuccess?.originalDice ?? diceDisplay ?? dice;
206
- const explodingMatch = signSource.match(EXPLODING_SUCCESS_REGEX);
207
- if (explodingMatch) {
208
- const [, doubledSignRaw, valueStr] = explodingMatch;
209
- let doubledSign;
210
- if (doubledSignRaw === "!!==") doubledSign = "==";
211
- else if (doubledSignRaw === "!==") doubledSign = "!==";
212
- else doubledSign = doubledSignRaw.replace(/^!/, "");
213
- const signMap = {
214
- ">>": ">",
215
- "<<": "<",
216
- ">>=": ">=",
217
- "<<=": "<=",
218
- "==": "=",
219
- "!==": "!=",
220
- "!!==": "="
221
- };
222
- const mappedSign = signMap[doubledSign];
223
- const mappedValue = Number.parseFloat(valueStr);
224
- if (mappedSign && !Number.isNaN(mappedValue)) {
225
- const resultsString = replaceUnwantedText(results.join("; "));
226
- const flatValues = resultsString.split(";").flatMap((segment) => extractValuesFromOutput(segment));
227
- if (mappedSign === "!=") {
228
- const equalsCount = flatValues.filter((val) => val === mappedValue).length;
229
- successCount = flatValues.length - equalsCount;
230
- } else {
231
- successCount = flatValues.filter(
232
- (val) => matchComparison(mappedSign, val, mappedValue)
233
- ).length;
296
+ let bestKey = null;
297
+ let bestScore = 0;
298
+ for (const key of statKeys) {
299
+ const keyStd = key.standardize();
300
+ if (tokenStd === keyStd) {
301
+ bestKey = key;
302
+ bestScore = 1;
303
+ break;
304
+ }
305
+ const score = similarityScore(tokenStd, keyStd);
306
+ if (score > bestScore) {
307
+ bestScore = score;
308
+ bestKey = key;
234
309
  }
235
310
  }
236
- }
237
- if (compare && trivialComparisonDetected) compare.trivial = true;
238
- const finalDice2 = isCurlyBulk ? `{${diceToRoll}${curlyCompare?.sign}${curlyCompare?.value}}` : diceToRoll;
239
- const resultOutput2 = replaceUnwantedText(results.join("; "));
240
- const finalTotal = explodingSuccess ? resultOutput2.split(";").flatMap((segment) => extractValuesFromOutput(segment)).filter(
241
- (val) => matchComparison(explodingSuccess.sign, val, explodingSuccess.value)
242
- ).length : successCount;
243
- return {
244
- dice: explodingSuccess ? diceDisplay : finalDice2,
245
- result: resultOutput2,
246
- comment: comments,
247
- compare: isCurlyBulk ? void 0 : compare,
248
- modifier: modificator,
249
- total: finalTotal,
250
- trivial: trivialComparisonDetected ? true : void 0
251
- };
252
- }
253
- const roller2 = new import_rpg_dice_roller.DiceRoller();
254
- import_rpg_dice_roller.NumberGenerator.generator.engine = engine;
255
- for (let i = 0; i < numberOfDice; i++) {
256
- try {
257
- roller2.roll(diceToRoll);
258
- } catch (error) {
259
- throw new DiceTypeError(diceToRoll, "roll", error);
260
- }
261
- }
262
- const finalDice = isCurlyBulk ? `{${diceToRoll}}` : diceToRoll;
263
- return {
264
- dice: finalDice,
265
- result: replaceUnwantedText(roller2.output),
266
- comment: comments,
267
- compare: compare ? compare : void 0,
268
- modifier: modificator,
269
- total: roller2.total
270
- };
271
- }
272
- const roller = new import_rpg_dice_roller.DiceRoller();
273
- import_rpg_dice_roller.NumberGenerator.generator.engine = engine;
274
- let diceWithoutComment = dice.replace(COMMENT_REGEX, "").trimEnd();
275
- if (sort) diceWithoutComment = `${diceWithoutComment}${sort}`;
276
- let diceRoll;
277
- try {
278
- diceRoll = roller.roll(diceWithoutComment);
279
- } catch (error) {
280
- throw new DiceTypeError(diceWithoutComment, "roll", error);
281
- }
282
- if (compare && diceRoll) {
283
- const currentRoll = Array.isArray(diceRoll) ? diceRoll[0] : diceRoll;
284
- const maxDiceValue = currentRoll.maxTotal;
285
- const minDiceValue = currentRoll.minTotal;
286
- const trivial = isTrivialComparison(maxDiceValue, minDiceValue, compare);
287
- compare.trivial = trivial ? true : void 0;
288
- }
289
- const commentMatch = dice.match(COMMENT_REGEX);
290
- const comment = commentMatch ? commentMatch[2] : void 0;
291
- let rerollCount = 0;
292
- let res;
293
- if (pity && compare) {
294
- const currentRoll = Array.isArray(diceRoll) ? diceRoll[0] : diceRoll;
295
- const maxPossible = currentRoll ? currentRoll.maxTotal : null;
296
- const isComparisonPossible = maxPossible === null || canComparisonSucceed(maxPossible, compare);
297
- if (isComparisonPossible) {
298
- let isFail = (0, import_mathjs2.evaluate)(`${roller.total}${compare.sign}${compare.value}`);
299
- if (!isFail) {
300
- const maxReroll = 100;
301
- while (!isFail && rerollCount < maxReroll) {
302
- try {
303
- res = roll(diceWithoutComment, engine, false);
304
- } catch (error) {
305
- throw new DiceTypeError(diceWithoutComment, "roll", error);
306
- }
307
- rerollCount++;
308
- if (res && res.total !== void 0)
309
- isFail = (0, import_mathjs2.evaluate)(`${res.total}${compare.sign}${compare.value}`);
310
- }
311
- if (res) {
312
- return {
313
- ...res,
314
- dice,
315
- comment,
316
- compare,
317
- modifier: modificator,
318
- pityLogs: rerollCount,
319
- trivial: res.trivial ?? (compare?.trivial ? true : void 0)
320
- };
311
+ if (bestKey && bestScore >= 0.6) {
312
+ const statValue = stats[bestKey];
313
+ result += statValue.toString();
314
+ } else {
315
+ result += token;
321
316
  }
317
+ lastIndex = tokenRegex.lastIndex;
322
318
  }
319
+ result += outsideText.slice(lastIndex);
323
320
  }
321
+ dice = result;
324
322
  }
325
- let resultOutput = replaceUnwantedText(roller.output);
326
- if (explodingSuccess) {
327
- const successes = countExplodingSuccesses(
328
- diceRoll,
329
- explodingSuccess.sign,
330
- explodingSuccess.value
331
- );
332
- resultOutput = resultOutput.replace(/=\s*-?\d+(?:\.\d+)?$/, `= ${successes}`).replace(explodingSuccess.normalizedSegment, explodingSuccess.originalSegment);
333
- return {
334
- dice: diceDisplay,
335
- result: resultOutput,
336
- comment,
337
- compare: compare ? compare : void 0,
338
- modifier: modificator,
339
- total: successes,
340
- pityLogs: rerollCount > 0 ? rerollCount : void 0,
341
- trivial: compare?.trivial ? true : void 0
342
- };
343
- }
344
- return {
345
- dice,
346
- result: resultOutput,
347
- comment,
348
- compare: compare ? compare : void 0,
349
- modifier: modificator,
350
- total: roller.total,
351
- pityLogs: rerollCount > 0 ? rerollCount : void 0,
352
- trivial: compare?.trivial ? true : void 0
353
- };
323
+ if (dollarValue) dice = dice.replaceAll("$", dollarValue);
324
+ return replaceFormulaInDice(dice);
354
325
  }
355
- function sharedRolls(dice, engine = import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto, pity, explodingSuccessMain, diceDisplay) {
356
- if (!explodingSuccessMain) {
357
- explodingSuccessMain = normalizeExplodingSuccess(dice.split(";")[0] ?? dice);
358
- }
359
- if (explodingSuccessMain) {
360
- dice = dice.replace(explodingSuccessMain.originalSegment, "!");
361
- }
362
- if (dice.match(/\d+?#(.*?)/))
363
- throw new DiceTypeError(
364
- dice,
365
- "noBulkRoll",
366
- "bulk roll are not allowed in shared rolls"
367
- );
368
- const results = [];
369
- const mainComment = /\s+#(?<comment>.*)/.exec(dice)?.groups?.comment?.trimEnd() ?? void 0;
370
- const split = dice.split(";");
371
- const displayDice = diceDisplay ?? explodingSuccessMain?.originalDice ?? split[0];
372
- let diceMain = fixParenthesis(split[0]);
373
- const commentsRegex = /\[(?<comments>.*?)\]/gi;
374
- const comments = formatComment(diceMain);
375
- const diceMainWithoutComments = diceMain.replace(commentsRegex, "").trim();
376
- const toHideRegex = /\((?<dice>[^)]+)\)/;
377
- const toHide = toHideRegex.exec(diceMainWithoutComments)?.groups;
378
- let hidden = false;
379
- if (toHide?.dice) {
380
- diceMain = toHide.dice;
381
- hidden = true;
382
- } else if (toHide) {
383
- diceMain = "1d1";
384
- hidden = true;
385
- } else {
386
- diceMain = diceMainWithoutComments;
387
- }
388
- const rollBounds = getRollBounds(diceMain, engine);
389
- let diceResult = roll(diceMain, engine, pity);
390
- if (!diceResult || !diceResult.total) {
391
- if (hidden) {
392
- diceResult = roll(fixParenthesis(split[0]), engine, pity);
393
- hidden = false;
394
- } else return void 0;
395
- }
396
- if (!diceResult || !diceResult.total) return void 0;
397
- if (explodingSuccessMain && diceResult.result) {
398
- const values = extractValuesFromOutput(diceResult.result);
399
- diceResult.total = values.filter(
400
- (v) => matchComparison(explodingSuccessMain.sign, v, explodingSuccessMain.value)
401
- ).length;
402
- }
403
- let aggregatedCompare = diceResult.compare;
404
- let hasTrivialComparison = diceResult.compare?.trivial === true;
405
- results.push(`\u203B ${comments}${diceResult.result}`);
406
- let total = diceResult.total;
407
- diceResult.comment = mainComment;
408
- if (!total) {
409
- return {
410
- dice: displayDice,
411
- result: results.join(";"),
412
- comment: mainComment,
413
- compare: aggregatedCompare,
414
- modifier: diceResult.modifier,
415
- total,
416
- trivial: hasTrivialComparison ? true : void 0
417
- };
418
- }
419
- for (let element of split.slice(1)) {
420
- const comment = formatComment(element);
421
- element = element.replaceAll(commentsRegex, "").replaceAll(OPTIONAL_COMMENT, "").trim();
422
- let toRoll = element.replace(SYMBOL_DICE, `${diceResult.total}`);
423
- const compareRegex = toRoll.match(SIGN_REGEX_SPACE);
424
- if (compareRegex) {
425
- const compareResult = compareSignFormule(
426
- toRoll,
427
- compareRegex,
428
- element,
429
- diceResult,
430
- engine,
431
- pity,
432
- rollBounds
433
- );
434
- toRoll = compareResult.dice;
435
- results.push(compareResult.results);
436
- if (!aggregatedCompare && compareResult.compare)
437
- aggregatedCompare = compareResult.compare;
438
- if (compareResult.trivial) hasTrivialComparison = true;
439
- } else {
440
- const { formule, diceAll } = replaceText(
441
- element,
442
- diceResult.total,
443
- diceResult.dice
444
- );
326
+ function replaceFormulaInDice(dice) {
327
+ const formula = /(?<formula>\{{2}(.+?)}{2})/gim;
328
+ let match;
329
+ let modifiedDice = dice;
330
+ while ((match = formula.exec(dice)) !== null) {
331
+ if (match.groups?.formula) {
332
+ const formulae = match.groups.formula.replaceAll("{{", "").replaceAll("}}", "");
445
333
  try {
446
- const evaluated = (0, import_mathjs2.evaluate)(toRoll);
447
- results.push(`\u25C8 ${comment}${diceAll}: ${formule} = ${evaluated}`);
448
- total += Number.parseInt(evaluated, 10);
334
+ const result = (0, import_mathjs.evaluate)(formulae);
335
+ modifiedDice = modifiedDice.replace(match.groups.formula, result.toString());
449
336
  } catch (error) {
450
- const evaluated = roll(toRoll, engine, pity);
451
- if (evaluated) {
452
- results.push(
453
- `\u25C8 ${comment}${diceAll}: ${evaluated.result.split(":").slice(1).join(":")}`
454
- );
455
- if (!aggregatedCompare && evaluated.compare)
456
- aggregatedCompare = evaluated.compare;
457
- if (evaluated.compare?.trivial) hasTrivialComparison = true;
458
- } else results.push(`\u25C8 ${comment}${diceAll}: ${formule} = ${evaluated}`);
459
- total += evaluated?.total ?? 0;
337
+ throw new FormulaError(match.groups.formula, "replaceFormulasInDice", error);
460
338
  }
461
339
  }
462
340
  }
463
- if (hidden)
464
- results.shift();
465
- return {
466
- dice: displayDice,
467
- result: results.join(";"),
468
- comment: mainComment,
469
- compare: hasTrivialComparison && aggregatedCompare ? { ...aggregatedCompare, trivial: true } : aggregatedCompare,
470
- modifier: diceResult.modifier,
471
- total,
472
- trivial: hasTrivialComparison ? true : void 0
473
- };
341
+ return cleanedDice(modifiedDice);
474
342
  }
475
- function replaceInFormula(element, diceResult, compareResult, res, engine = import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto, pity) {
476
- const { formule, diceAll } = replaceText(
477
- element,
478
- diceResult.total ?? 0,
479
- diceResult.dice
480
- );
481
- const validSign = res ? "\u2713" : "\u2715";
482
- const invertedSign = res ? compareResult.compare.sign : inverseSign(compareResult.compare.sign);
483
- let evaluateRoll;
484
- try {
485
- evaluateRoll = (0, import_mathjs2.evaluate)(compareResult.dice);
486
- return `${validSign} ${diceAll}: ${formule} = ${evaluateRoll}${invertedSign}${compareResult.compare?.value}`;
487
- } catch (error) {
488
- const evaluateRoll2 = roll(compareResult.dice, engine, pity);
489
- if (evaluateRoll2)
490
- return `${validSign} ${diceAll}: ${evaluateRoll2.result.split(":").splice(1).join(":")}`;
491
- return `${validSign} ${diceAll}: ${formule} = ${evaluateRoll2}${invertedSign}${compareResult.compare?.value}`;
492
- }
343
+ function cleanedDice(dice) {
344
+ return dice.replaceAll("+-", "-").replaceAll("--", "+").replaceAll("++", "+").replaceAll("=>", ">=").replaceAll("=<", "<=").trimEnd();
493
345
  }
494
- function rollCompare(value, engine = import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto, pity) {
495
- if (isNumber(value)) return { value: Number.parseInt(value, 10) };
496
- if (!value || typeof value === "string" && value.trim() === "") {
497
- return { value: 0, diceResult: value };
498
- }
499
- const rollComp = roll(value, engine, pity);
500
- if (!rollComp?.total) {
501
- try {
502
- return { value: (0, import_mathjs2.evaluate)(value), diceResult: value };
503
- } catch (error) {
504
- return { value: 0, diceResult: value };
346
+ function isNumber(value) {
347
+ return value !== void 0 && (typeof value === "number" || !Number.isNaN(Number(value)) && typeof value === "string" && value.trim().length > 0);
348
+ }
349
+ function replaceExpByRandom(dice, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto) {
350
+ const diceRegex = /\{exp( ?\|\| ?(?<default>\d+))?}/gi;
351
+ return dice.replace(diceRegex, (_match, _p1, _p2, _offset, _string, groups) => {
352
+ const defaultValue = groups?.default;
353
+ return defaultValue ?? randomInt(1, 999, engine).toString();
354
+ });
355
+ }
356
+ function randomInt(min, max, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto, rng) {
357
+ if (!rng) rng = new import_random_js.Random(engine || void 0);
358
+ return rng.integer(min, max);
359
+ }
360
+ function levenshteinDistance(a, b) {
361
+ if (a === b) return 0;
362
+ const al = a.length;
363
+ const bl = b.length;
364
+ if (al === 0) return bl;
365
+ if (bl === 0) return al;
366
+ const v0 = new Array(bl + 1);
367
+ const v1 = new Array(bl + 1);
368
+ for (let i = 0; i <= bl; i++) v0[i] = i;
369
+ for (let i = 0; i < al; i++) {
370
+ v1[0] = i + 1;
371
+ for (let j = 0; j < bl; j++) {
372
+ const cost = a[i] === b[j] ? 0 : 1;
373
+ v1[j + 1] = Math.min(v1[j] + 1, v0[j + 1] + 1, v0[j] + cost);
505
374
  }
375
+ for (let j = 0; j <= bl; j++) v0[j] = v1[j];
506
376
  }
507
- return {
508
- dice: value,
509
- value: rollComp.total,
510
- diceResult: rollComp?.result
511
- };
377
+ return v1[bl];
378
+ }
379
+ function similarityScore(a, b) {
380
+ const la = a.length;
381
+ const lb = b.length;
382
+ if (la === 0 && lb === 0) return 1;
383
+ const dist = levenshteinDistance(a, b);
384
+ const max = Math.max(la, lb);
385
+ return 1 - dist / max;
386
+ }
387
+ function createCriticalCustom(dice, customCritical, template, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto) {
388
+ const compareRegex = dice.match(SIGN_REGEX_SPACE);
389
+ let customDice = dice;
390
+ const compareValue = diceTypeRandomParse(customCritical.value, template, engine);
391
+ if (compareValue.includes("$"))
392
+ throw new DiceTypeError(compareValue, "createCriticalCustom");
393
+ const comparaison = `${customCritical.sign}${compareValue}`;
394
+ if (compareRegex) customDice = customDice.replace(SIGN_REGEX_SPACE, comparaison);
395
+ else customDice += comparaison;
396
+ return diceTypeRandomParse(customDice, template, engine);
512
397
  }
513
398
 
514
399
  // src/dice/compare.ts
@@ -542,18 +427,37 @@ function canComparisonFail(maxRollValue, compare, minRollValue = 1) {
542
427
  return true;
543
428
  }
544
429
  }
545
- function getCompare(dice, compareRegex, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto, pity) {
546
- if (dice.match(/((\{.*,(.*)+\}|([><=!]+\d+f))([><=]|!=)+\d+\}?)|\{(.*)(([><=]|!=)+).*\}/))
547
- return { dice, compare: void 0 };
548
- dice = dice.replace(SIGN_REGEX_SPACE, "");
549
- let compare;
550
- const calc = compareRegex[2];
430
+ function rollCompare(value, engine = import_rpg_dice_roller3.NumberGenerator.engines.nodeCrypto, pity) {
431
+ if (isNumber(value)) return { value: Number.parseInt(value, 10) };
432
+ if (!value || typeof value === "string" && value.trim() === "") {
433
+ return { value: 0, diceResult: value };
434
+ }
435
+ const rollComp = roll(value, engine, pity);
436
+ if (!rollComp?.total) {
437
+ try {
438
+ return { value: (0, import_mathjs2.evaluate)(value), diceResult: value };
439
+ } catch (error) {
440
+ return { value: 0, diceResult: value };
441
+ }
442
+ }
443
+ return {
444
+ dice: value,
445
+ value: rollComp.total,
446
+ diceResult: rollComp?.result
447
+ };
448
+ }
449
+ function getCompare(dice, compareRegex, engine = import_rpg_dice_roller3.NumberGenerator.engines.nodeCrypto, pity) {
450
+ if (dice.match(/((\{.*,(.*)+\}|([><=!]+\d+f))([><=]|!=)+\d+\}?)|\{(.*)(([><=]|!=)+).*\}/))
451
+ return { dice, compare: void 0 };
452
+ dice = dice.replace(SIGN_REGEX_SPACE, "");
453
+ let compare;
454
+ const calc = compareRegex[2];
551
455
  const sign = calc.match(/[+-/*^]/)?.[0];
552
456
  const compareSign = compareRegex[0].match(SIGN_REGEX)?.[0];
553
457
  if (sign) {
554
458
  const toCalc = calc.replace(SIGN_REGEX, "").replace(/\s/g, "").replace(/;(.*)/, "");
555
459
  const rCompare = rollCompare(toCalc, engine, pity);
556
- const total = (0, import_mathjs3.evaluate)(rCompare.value.toString());
460
+ const total = (0, import_mathjs2.evaluate)(rCompare.value.toString());
557
461
  dice = dice.replace(SIGN_REGEX_SPACE, `${compareSign}${total}`);
558
462
  compare = {
559
463
  sign: compareSign,
@@ -595,11 +499,11 @@ function canComparisonSucceed(maxRollValue, compare, minRollValue) {
595
499
  }
596
500
 
597
501
  // src/dice/exploding.ts
598
- var import_mathjs5 = require("mathjs");
502
+ var import_mathjs4 = require("mathjs");
599
503
 
600
504
  // src/dice/signs.ts
601
- var import_rpg_dice_roller3 = require("@dice-roller/rpg-dice-roller");
602
- var import_mathjs4 = require("mathjs");
505
+ var import_rpg_dice_roller4 = require("@dice-roller/rpg-dice-roller");
506
+ var import_mathjs3 = require("mathjs");
603
507
 
604
508
  // src/dice/replace.ts
605
509
  function replaceUnwantedText(dice) {
@@ -668,14 +572,14 @@ function inverseSign(sign) {
668
572
  return "==";
669
573
  }
670
574
  }
671
- function compareSignFormule(toRoll, compareRegex, element, diceResult, engine = import_rpg_dice_roller3.NumberGenerator.engines.nodeCrypto, pity, rollBounds) {
575
+ function compareSignFormule(toRoll, compareRegex, element, diceResult, engine = import_rpg_dice_roller4.NumberGenerator.engines.nodeCrypto, pity, rollBounds) {
672
576
  let results = "";
673
577
  let trivial = false;
674
578
  const compareResult = getCompare(toRoll, compareRegex, engine);
675
579
  const toCompare = `${compareResult.dice}${compareResult.compare?.sign}${compareResult.compare?.value}`;
676
580
  let res;
677
581
  try {
678
- res = (0, import_mathjs4.evaluate)(toCompare);
582
+ res = (0, import_mathjs3.evaluate)(toCompare);
679
583
  } catch (error) {
680
584
  res = roll(toCompare, engine, pity);
681
585
  }
@@ -687,7 +591,7 @@ function compareSignFormule(toRoll, compareRegex, element, diceResult, engine =
687
591
  } else if (res instanceof Object) {
688
592
  const diceResult2 = res;
689
593
  if (diceResult2.compare) {
690
- const toEvaluate = (0, import_mathjs4.evaluate)(
594
+ const toEvaluate = (0, import_mathjs3.evaluate)(
691
595
  `${diceResult2.total}${diceResult2.compare.sign}${diceResult2.compare.value}`
692
596
  );
693
597
  const sign = toEvaluate ? "\u2713" : "\u2715";
@@ -724,7 +628,7 @@ function normalizeExplodingSuccess(dice) {
724
628
  let parsedValue = Number.parseFloat(valueStr);
725
629
  if (Number.isNaN(parsedValue)) {
726
630
  try {
727
- parsedValue = Number.parseFloat((0, import_mathjs5.evaluate)(valueStr));
631
+ parsedValue = Number.parseFloat((0, import_mathjs4.evaluate)(valueStr));
728
632
  } catch (_error) {
729
633
  parsedValue = 0;
730
634
  }
@@ -759,7 +663,16 @@ function countExplodingSuccesses(diceRoll, sign, value) {
759
663
  }
760
664
 
761
665
  // src/dice/extract.ts
762
- var import_rpg_dice_roller4 = require("@dice-roller/rpg-dice-roller");
666
+ var import_rpg_dice_roller5 = require("@dice-roller/rpg-dice-roller");
667
+
668
+ // src/dice/calculator.ts
669
+ var import_mathjs5 = require("mathjs");
670
+ function calculator(sign, value, total) {
671
+ if (sign === "^") sign = "**";
672
+ return (0, import_mathjs5.evaluate)(`${total} ${sign} ${value}`);
673
+ }
674
+
675
+ // src/dice/extract.ts
763
676
  function getModifier(dice) {
764
677
  const modifier = dice.matchAll(/(\+|-|%|\/|\^|\*|\*{2})(\d+)/gi);
765
678
  let modificator;
@@ -791,10 +704,10 @@ function extractValuesFromOutput(output) {
791
704
  }
792
705
  return values;
793
706
  }
794
- function getRollBounds(dice, engine = import_rpg_dice_roller4.NumberGenerator.engines.nodeCrypto) {
707
+ function getRollBounds(dice, engine = import_rpg_dice_roller5.NumberGenerator.engines.nodeCrypto) {
795
708
  try {
796
- const roller = new import_rpg_dice_roller4.DiceRoller();
797
- import_rpg_dice_roller4.NumberGenerator.generator.engine = engine;
709
+ const roller = new import_rpg_dice_roller5.DiceRoller();
710
+ import_rpg_dice_roller5.NumberGenerator.generator.engine = engine;
798
711
  const rollResult = roller.roll(dice);
799
712
  const instance = Array.isArray(rollResult) ? rollResult[0] : rollResult;
800
713
  const { minTotal, maxTotal } = instance;
@@ -803,330 +716,543 @@ function getRollBounds(dice, engine = import_rpg_dice_roller4.NumberGenerator.en
803
716
  }
804
717
  return void 0;
805
718
  }
806
-
807
- // src/engine.ts
808
- var import_rpg_dice_roller5 = require("@dice-roller/rpg-dice-roller");
809
- function getEngineId(engine) {
810
- if (engine === import_rpg_dice_roller5.NumberGenerator.engines.nodeCrypto) return "nodeCrypto";
811
- if (engine === import_rpg_dice_roller5.NumberGenerator.engines.nativeMath) return "nativeMath";
812
- if (engine === import_rpg_dice_roller5.NumberGenerator.engines.browserCrypto) return "browserCrypto";
813
- try {
814
- const e = engine;
815
- if (e && typeof e === "object") {
816
- if (typeof e.name === "string" && e.name) return e.name;
817
- if (e.constructor?.name) return e.constructor.name;
719
+ function setSortOrder(toRoll, sort) {
720
+ const sortRegex = /(sa|sd|s)/i;
721
+ if (sort && !toRoll.match(sortRegex)) {
722
+ const modifierComparisonRegex = /([+\-*/%^]\d+|([><=!]+\d+f)|([><=]|!=)+\d+)$/;
723
+ const match = toRoll.match(modifierComparisonRegex);
724
+ if (match) {
725
+ const index = match.index;
726
+ toRoll = `${toRoll.slice(0, index)}${sort}${toRoll.slice(index)}`;
727
+ } else {
728
+ toRoll += sort;
818
729
  }
819
- } catch {
820
730
  }
821
- return "unknown";
731
+ return toRoll;
822
732
  }
823
- function getEngine(engine) {
824
- switch (engine) {
825
- case "nativeMath":
826
- return import_rpg_dice_roller5.NumberGenerator.engines.nativeMath;
827
- case "browserCrypto":
828
- return import_rpg_dice_roller5.NumberGenerator.engines.browserCrypto;
829
- case "nodeCrypto":
830
- return import_rpg_dice_roller5.NumberGenerator.engines.nodeCrypto;
831
- default:
832
- return import_rpg_dice_roller5.NumberGenerator.engines.nativeMath;
733
+ function prepareDice(diceInput) {
734
+ let dice = standardizeDice(replaceFormulaInDice(diceInput)).replace(/^\+/, "").replaceAll("=>", ">=").replaceAll("=<", "<=").trimStart();
735
+ dice = dice.replaceAll(DETECT_CRITICAL, "").trimEnd();
736
+ const explodingSuccess = normalizeExplodingSuccess(dice);
737
+ if (explodingSuccess) dice = explodingSuccess.dice;
738
+ let diceDisplay;
739
+ if (dice.includes(";")) {
740
+ const mainDice = dice.split(";")[0];
741
+ diceDisplay = explodingSuccess?.originalDice ?? mainDice;
742
+ } else {
743
+ diceDisplay = explodingSuccess?.originalDice ?? dice;
744
+ }
745
+ const curlyBulkMatch = dice.match(/^\{(\d+#.*)\}$/);
746
+ const isCurlyBulk = !!curlyBulkMatch;
747
+ const bulkContent = isCurlyBulk ? curlyBulkMatch[1] : "";
748
+ const isSharedRoll = dice.includes(";");
749
+ let isSharedCurly = false;
750
+ if (isSharedRoll && dice.match(/^\{.*;\s*.*\}$/)) {
751
+ dice = dice.slice(1, -1);
752
+ isSharedCurly = true;
753
+ diceDisplay = diceDisplay.slice(1);
833
754
  }
755
+ let isSimpleCurly = false;
756
+ if (!isCurlyBulk && !isSharedRoll && dice.match(/^\{.*\}$/)) {
757
+ const innerContent = dice.slice(1, -1);
758
+ const hasModifiers = innerContent.match(/[+\-*/%^]/);
759
+ const hasComparison = innerContent.match(/(([><=!]+\d+f)|([><=]|!=)+\d+)/);
760
+ if (!(hasComparison && !hasModifiers)) {
761
+ dice = innerContent;
762
+ isSimpleCurly = true;
763
+ }
764
+ }
765
+ return {
766
+ dice,
767
+ diceDisplay,
768
+ explodingSuccess,
769
+ isSharedRoll,
770
+ isSharedCurly,
771
+ isCurlyBulk,
772
+ bulkContent,
773
+ isSimpleCurly
774
+ };
834
775
  }
835
776
 
836
- // src/errors.ts
837
- var DiceTypeError = class extends Error {
838
- dice;
839
- cause;
840
- method;
841
- constructor(dice, cause, method) {
842
- super(dice);
843
- this.name = "Invalid_Dice_Type";
844
- this.dice = dice;
845
- this.cause = cause;
846
- this.method = method;
777
+ // src/dice/bulk.ts
778
+ function handleBulkRolls(dice, isCurlyBulk, bulkContent, compare, explodingSuccess, diceDisplay, engine, sort) {
779
+ const bulkProcessContent = isCurlyBulk ? bulkContent : dice;
780
+ const diceArray = bulkProcessContent.split("#");
781
+ const numberOfDice = Number.parseInt(diceArray[0], 10);
782
+ let diceToRoll = diceArray[1].replace(COMMENT_REGEX, "");
783
+ const commentsMatch = diceArray[1].match(COMMENT_REGEX);
784
+ const comments = commentsMatch ? commentsMatch[2] : void 0;
785
+ let curlyCompare;
786
+ if (isCurlyBulk) {
787
+ const curlyCompareRegex = diceToRoll.match(SIGN_REGEX_SPACE);
788
+ if (curlyCompareRegex) {
789
+ const compareSign = curlyCompareRegex[0].match(SIGN_REGEX)?.[0];
790
+ const compareValue = curlyCompareRegex[2];
791
+ if (compareSign && compareValue) {
792
+ curlyCompare = {
793
+ sign: compareSign,
794
+ value: Number.parseInt(compareValue, 10)
795
+ };
796
+ diceToRoll = diceToRoll.replace(SIGN_REGEX_SPACE, "");
797
+ }
798
+ }
847
799
  }
848
- };
849
- var FormulaError = class extends Error {
850
- formula;
851
- cause;
852
- method;
853
- constructor(formula, cause, method) {
854
- super(formula);
855
- this.name = "Invalid_Formula";
856
- this.formula = formula;
857
- this.cause = cause;
858
- this.method = method;
800
+ diceToRoll = setSortOrder(diceToRoll, sort);
801
+ const activeCompare = compare || curlyCompare || (explodingSuccess ? { sign: explodingSuccess.sign, value: explodingSuccess.value } : void 0);
802
+ if (activeCompare) {
803
+ return handleBulkRollsWithComparison(
804
+ numberOfDice,
805
+ diceToRoll,
806
+ comments,
807
+ activeCompare,
808
+ explodingSuccess,
809
+ diceDisplay,
810
+ isCurlyBulk,
811
+ curlyCompare,
812
+ compare,
813
+ engine
814
+ );
859
815
  }
860
- };
861
- var MaxGreater = class extends Error {
862
- name;
863
- value;
864
- max;
865
- constructor(value, max) {
866
- super(value.toString());
867
- this.name = "Max_Greater";
868
- this.value = value;
869
- this.max = max;
816
+ const roller = new import_rpg_dice_roller6.DiceRoller();
817
+ import_rpg_dice_roller6.NumberGenerator.generator.engine = engine;
818
+ for (let i = 0; i < numberOfDice; i++) {
819
+ try {
820
+ roller.roll(diceToRoll);
821
+ } catch (error) {
822
+ throw new DiceTypeError(diceToRoll, "roll", error);
823
+ }
870
824
  }
871
- };
872
- var EmptyObjectError = class extends Error {
873
- name;
874
- constructor() {
875
- super();
876
- this.name = "Empty_Object";
825
+ const finalDice = isCurlyBulk ? `{${diceToRoll}}` : diceToRoll;
826
+ const modificator = getModifier(dice);
827
+ return {
828
+ dice: finalDice,
829
+ result: replaceUnwantedText(roller.output),
830
+ comment: comments,
831
+ compare: compare ? compare : void 0,
832
+ modifier: modificator,
833
+ total: roller.total
834
+ };
835
+ }
836
+ function handleBulkRollsWithComparison(numberOfDice, diceToRoll, comments, activeCompare, explodingSuccess, diceDisplay, isCurlyBulk, curlyCompare, compare, engine) {
837
+ const results = [];
838
+ let successCount = 0;
839
+ const roller = new import_rpg_dice_roller6.DiceRoller();
840
+ import_rpg_dice_roller6.NumberGenerator.generator.engine = engine;
841
+ let trivialComparisonDetected = false;
842
+ const formatOutput = (output, addStar) => {
843
+ const formatted = addStar && isCurlyBulk ? output.replace(
844
+ /\[([^\]]+)\]/,
845
+ (_m, content) => `[${content.split(",").map((d) => `${d.trim()}*`).join(", ")}]`
846
+ ) : output;
847
+ return curlyCompare ? formatted.replace(/^([^:]+):/, `$1${curlyCompare.sign}${curlyCompare.value}:`) : formatted;
848
+ };
849
+ for (let i = 0; i < numberOfDice; i++) {
850
+ try {
851
+ const individualRoll = roller.roll(diceToRoll);
852
+ const rollInstance = Array.isArray(individualRoll) ? individualRoll[0] : individualRoll;
853
+ if (!trivialComparisonDetected && activeCompare) {
854
+ const { maxTotal, minTotal } = rollInstance;
855
+ trivialComparisonDetected = isTrivialComparison(
856
+ maxTotal,
857
+ minTotal,
858
+ activeCompare
859
+ );
860
+ }
861
+ const rollOutput = rollInstance.output;
862
+ if (explodingSuccess) {
863
+ const successesForRoll = countExplodingSuccesses(
864
+ rollInstance,
865
+ explodingSuccess.sign,
866
+ explodingSuccess.value
867
+ );
868
+ successCount += successesForRoll;
869
+ let formattedRollOutput = rollOutput.replace(explodingSuccess.normalizedSegment, explodingSuccess.originalSegment).replace(/=\s*-?\d+(?:\.\d+)?$/, `= ${successesForRoll}`);
870
+ formattedRollOutput = formatOutput(formattedRollOutput, false);
871
+ results.push(formattedRollOutput);
872
+ } else {
873
+ const rollTotal = rollInstance.total;
874
+ const isSuccess = (0, import_mathjs6.evaluate)(
875
+ `${rollTotal}${activeCompare.sign}${activeCompare.value}`
876
+ );
877
+ if (isSuccess) successCount++;
878
+ results.push(formatOutput(rollOutput, isSuccess));
879
+ }
880
+ } catch (error) {
881
+ throw new DiceTypeError(diceToRoll, "roll", error);
882
+ }
877
883
  }
878
- };
879
- var TooManyDice = class extends Error {
880
- name;
881
- constructor() {
882
- super();
883
- this.name = "Too_Many_Dice";
884
+ if (explodingSuccess) {
885
+ const signSource = explodingSuccess?.originalDice ?? diceDisplay;
886
+ const explodingMatch = signSource.match(EXPLODING_SUCCESS_REGEX);
887
+ if (explodingMatch) {
888
+ const [, doubledSignRaw, valueStr] = explodingMatch;
889
+ let doubledSign;
890
+ if (doubledSignRaw === "!!==") doubledSign = "==";
891
+ else if (doubledSignRaw === "!==") doubledSign = "!==";
892
+ else doubledSign = doubledSignRaw.replace(/^!/, "");
893
+ const signMap = {
894
+ ">>": ">",
895
+ "<<": "<",
896
+ ">=": ">=",
897
+ "<=": "<=",
898
+ "==": "=",
899
+ "!==": "!=",
900
+ "!!==": "="
901
+ };
902
+ const mappedSign = signMap[doubledSign];
903
+ const mappedValue = Number.parseFloat(valueStr);
904
+ if (mappedSign && !Number.isNaN(mappedValue)) {
905
+ const resultsString = replaceUnwantedText(results.join("; "));
906
+ const flatValues = resultsString.split(";").flatMap((segment) => extractValuesFromOutput(segment));
907
+ if (mappedSign === "!=") {
908
+ const equalsCount = flatValues.filter((val) => val === mappedValue).length;
909
+ successCount = flatValues.length - equalsCount;
910
+ } else {
911
+ successCount = flatValues.filter(
912
+ (val) => matchComparison(mappedSign, val, mappedValue)
913
+ ).length;
914
+ }
915
+ }
916
+ }
884
917
  }
885
- };
886
- var TooManyStats = class extends Error {
887
- name;
888
- constructor() {
889
- super();
890
- this.name = "Too_Many_Stats";
918
+ if (compare && trivialComparisonDetected) compare.trivial = true;
919
+ const finalDice = isCurlyBulk ? `{${diceToRoll}${curlyCompare?.sign}${curlyCompare?.value}}` : diceToRoll;
920
+ const resultOutput = replaceUnwantedText(results.join("; "));
921
+ const finalTotal = explodingSuccess ? resultOutput.split(";").flatMap((segment) => extractValuesFromOutput(segment)).filter(
922
+ (val) => matchComparison(explodingSuccess.sign, val, explodingSuccess.value)
923
+ ).length : successCount;
924
+ const modificator = getModifier(diceDisplay);
925
+ return {
926
+ dice: explodingSuccess ? diceDisplay : finalDice,
927
+ result: resultOutput,
928
+ comment: comments,
929
+ compare: isCurlyBulk ? void 0 : compare,
930
+ modifier: modificator,
931
+ total: finalTotal,
932
+ trivial: trivialComparisonDetected ? true : void 0
933
+ };
934
+ }
935
+
936
+ // src/dice/pity.ts
937
+ var import_mathjs7 = require("mathjs");
938
+ function handlePitySystem(dice, compare, diceRoll, roller, engine) {
939
+ const currentRoll = Array.isArray(diceRoll) ? diceRoll[0] : diceRoll;
940
+ const maxPossible = currentRoll ? currentRoll.maxTotal : null;
941
+ const isComparisonPossible = maxPossible === null || canComparisonSucceed(maxPossible, compare);
942
+ if (!isComparisonPossible) {
943
+ return { rerollCount: 0 };
891
944
  }
892
- };
893
- var NoStatisticsError = class extends Error {
894
- name;
895
- constructor() {
896
- super();
897
- this.name = "No_Statistics";
945
+ let isFail = (0, import_mathjs7.evaluate)(`${roller.total}${compare.sign}${compare.value}`);
946
+ if (isFail) {
947
+ return { rerollCount: 0 };
898
948
  }
899
- };
900
-
901
- // src/interfaces/index.ts
902
- var SortOrder = /* @__PURE__ */ ((SortOrder2) => {
903
- SortOrder2["Ascending"] = "sa";
904
- SortOrder2["Descending"] = "sd";
905
- SortOrder2["None"] = "none";
906
- return SortOrder2;
907
- })(SortOrder || {});
908
-
909
- // src/interfaces/zod.ts
910
- var import_zod = require("zod");
911
- var statisticValueSchema = import_zod.z.object({
912
- max: import_zod.z.number().transform((val) => val === 0 ? void 0 : val).optional(),
913
- min: import_zod.z.number().transform(
914
- (val) => Number.isNaN(Number.parseInt(val, 10)) ? void 0 : val
915
- ).optional(),
916
- combinaison: import_zod.z.string().transform((str) => str.trim() || void 0).optional(),
917
- exclude: import_zod.z.boolean().optional()
918
- }).superRefine((data, ctx) => {
919
- if (data.max !== void 0 && data.min !== void 0 && data.max <= data.min) {
920
- ctx.addIssue({
921
- code: "custom",
922
- message: `Max_Greater; ${data.min}; ${data.max}`,
923
- path: ["max"]
924
- });
949
+ const maxReroll = 100;
950
+ let rerollCount = 0;
951
+ let res;
952
+ while (!isFail && rerollCount < maxReroll) {
953
+ try {
954
+ res = roll(dice, engine, false);
955
+ } catch (error) {
956
+ throw new DiceTypeError(dice, "roll", error);
957
+ }
958
+ rerollCount++;
959
+ if (res && res.total !== void 0) {
960
+ isFail = (0, import_mathjs7.evaluate)(`${res.total}${compare.sign}${compare.value}`);
961
+ }
925
962
  }
926
- });
927
- var statisticSchema = import_zod.z.record(import_zod.z.string(), statisticValueSchema).optional().refine((stats) => !stats || Object.keys(stats).length <= 25, {
928
- message: "TooManyStats"
929
- });
930
- var criticalSchema = import_zod.z.object({
931
- success: import_zod.z.string().or(import_zod.z.number().min(0)).optional(),
932
- failure: import_zod.z.string().or(import_zod.z.number().min(0)).optional()
933
- }).transform((values) => {
934
- if (values.success === "") values.success = void 0;
935
- if (values.failure === "") values.failure = void 0;
936
- if (values.failure === 0) values.failure = void 0;
937
- if (values.success === 0) values.success = void 0;
938
- values.success = Number.parseInt(values.success, 10);
939
- values.failure = Number.parseInt(values.failure, 10);
940
- return values;
941
- });
942
- var criticalValueSchema = import_zod.z.object({
943
- sign: import_zod.z.enum(["<", ">", "<=", ">=", "!=", "=="]),
944
- value: import_zod.z.string(),
945
- onNaturalDice: import_zod.z.boolean().optional(),
946
- affectSkill: import_zod.z.boolean().optional()
947
- });
948
- var damageSchema = import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional().refine((stats) => !stats || Object.keys(stats).length <= 25, {
949
- message: "TooManyDice"
950
- });
951
- var customCriticalSchema = import_zod.z.record(import_zod.z.string(), criticalValueSchema).optional().refine((stats) => !stats || Object.keys(stats).length <= 22, {
952
- message: "TooManyDice"
953
- });
954
- var templateSchema = import_zod.z.object({
955
- charName: import_zod.z.boolean().optional(),
956
- statistics: statisticSchema,
957
- total: import_zod.z.number().min(0).transform((val) => val === 0 ? void 0 : val).optional(),
958
- forceDistrib: import_zod.z.boolean().optional(),
959
- diceType: import_zod.z.string().optional(),
960
- critical: criticalSchema.optional(),
961
- customCritical: customCriticalSchema,
962
- damage: damageSchema
963
- });
964
-
965
- // src/utils.ts
966
- var import_mathjs6 = require("mathjs");
967
- var import_uniformize = require("uniformize");
968
- var import_rpg_dice_roller6 = require("@dice-roller/rpg-dice-roller");
969
- var import_random_js = require("random-js");
970
- function escapeRegex(string) {
971
- return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
972
- }
973
- function standardizeDice(dice) {
974
- return dice.replace(
975
- /(\[[^\]]+])|([^[]+)/g,
976
- (_match, insideBrackets, outsideText) => insideBrackets ? insideBrackets : outsideText.standardize().replaceAll("df", "dF")
977
- );
963
+ return { rerollCount, result: res };
978
964
  }
979
- function generateStatsDice(originalDice, stats, dollarValue) {
980
- let dice = originalDice.standardize();
981
- if (stats && Object.keys(stats).length > 0) {
982
- const statKeys = Object.keys(stats);
983
- const partsRegex = /(\[[^\]]+])|([^[]+)/g;
984
- let result = "";
985
- let match;
986
- while ((match = partsRegex.exec(dice)) !== null) {
987
- const insideBrackets = match[1];
988
- const outsideText = match[2];
989
- if (insideBrackets) {
990
- result += insideBrackets;
991
- continue;
992
- }
993
- if (!outsideText) {
994
- continue;
995
- }
996
- const tokenRegex = /([\p{L}\p{N}_]+)/gu;
997
- let lastIndex = 0;
998
- let tokenMatch;
999
- while ((tokenMatch = tokenRegex.exec(outsideText)) !== null) {
1000
- result += outsideText.slice(lastIndex, tokenMatch.index);
1001
- const token = tokenMatch[0];
1002
- const tokenStd = token.standardize();
1003
- const diceMatch = /^(\d*)d(.+)$/i.exec(tokenStd);
1004
- if (diceMatch) {
1005
- const diceCount = diceMatch[1] || "";
1006
- const afterD = diceMatch[2];
1007
- let foundStatAfterD = false;
1008
- for (const key of statKeys) {
1009
- const keyStd = key.standardize();
1010
- if (afterD === keyStd) {
1011
- result += `${diceCount}d${stats[key].toString()}`;
1012
- foundStatAfterD = true;
1013
- break;
1014
- }
1015
- }
1016
- if (foundStatAfterD) {
1017
- lastIndex = tokenRegex.lastIndex;
1018
- continue;
1019
- }
1020
- }
1021
- let bestKey = null;
1022
- let bestScore = 0;
1023
- for (const key of statKeys) {
1024
- const keyStd = key.standardize();
1025
- if (tokenStd === keyStd) {
1026
- bestKey = key;
1027
- bestScore = 1;
1028
- break;
1029
- }
1030
- const score = similarityScore(tokenStd, keyStd);
1031
- if (score > bestScore) {
1032
- bestScore = score;
1033
- bestKey = key;
1034
- }
1035
- }
1036
- if (bestKey && bestScore >= 0.6) {
1037
- const statValue = stats[bestKey];
1038
- result += statValue.toString();
1039
- } else {
1040
- result += token;
1041
- }
1042
- lastIndex = tokenRegex.lastIndex;
1043
- }
1044
- result += outsideText.slice(lastIndex);
965
+
966
+ // src/roll.ts
967
+ function roll(dice, engine = import_rpg_dice_roller7.NumberGenerator.engines.nodeCrypto, pity, sort) {
968
+ if (sort === "none" /* None */) sort = void 0;
969
+ const prepared = prepareDice(dice);
970
+ if (!prepared.dice.includes("d")) return void 0;
971
+ if (prepared.isSharedRoll) {
972
+ return sharedRolls(
973
+ prepared.dice,
974
+ engine,
975
+ pity,
976
+ prepared.explodingSuccess,
977
+ prepared.diceDisplay,
978
+ prepared.isSharedCurly
979
+ );
980
+ }
981
+ let processedDice = fixParenthesis(prepared.dice);
982
+ const modificator = getModifier(processedDice);
983
+ const compareRegex = processedDice.match(SIGN_REGEX_SPACE);
984
+ let compare;
985
+ if (compareRegex && !prepared.isCurlyBulk) {
986
+ const compareResult = getCompare(processedDice, compareRegex, engine, pity);
987
+ processedDice = compareResult.dice;
988
+ compare = compareResult.compare;
989
+ }
990
+ let finalDiceDisplay = prepared.diceDisplay;
991
+ if (prepared.isSimpleCurly && !prepared.diceDisplay.startsWith("{")) {
992
+ finalDiceDisplay = `{${prepared.diceDisplay}}`;
993
+ }
994
+ const bulkProcessContent = prepared.isCurlyBulk ? prepared.bulkContent : processedDice;
995
+ if (bulkProcessContent.match(/\d+?#(.*)/)) {
996
+ return handleBulkRolls(
997
+ processedDice,
998
+ prepared.isCurlyBulk,
999
+ prepared.bulkContent,
1000
+ compare,
1001
+ prepared.explodingSuccess,
1002
+ prepared.diceDisplay,
1003
+ engine,
1004
+ sort
1005
+ );
1006
+ }
1007
+ const roller = new import_rpg_dice_roller7.DiceRoller();
1008
+ import_rpg_dice_roller7.NumberGenerator.generator.engine = engine;
1009
+ let diceWithoutComment = processedDice.replace(COMMENT_REGEX, "").trimEnd();
1010
+ diceWithoutComment = setSortOrder(diceWithoutComment, sort);
1011
+ let diceRoll;
1012
+ try {
1013
+ diceRoll = roller.roll(diceWithoutComment);
1014
+ } catch (error) {
1015
+ throw new DiceTypeError(diceWithoutComment, "roll", error);
1016
+ }
1017
+ if (compare && diceRoll) {
1018
+ const currentRoll = Array.isArray(diceRoll) ? diceRoll[0] : diceRoll;
1019
+ const trivial = isTrivialComparison(
1020
+ currentRoll.maxTotal,
1021
+ currentRoll.minTotal,
1022
+ compare
1023
+ );
1024
+ compare.trivial = trivial ? true : void 0;
1025
+ }
1026
+ const commentMatch = processedDice.match(COMMENT_REGEX);
1027
+ const comment = commentMatch ? commentMatch[2] : void 0;
1028
+ let rerollCount = 0;
1029
+ let pityResult;
1030
+ if (pity && compare) {
1031
+ const pityData = handlePitySystem(
1032
+ diceWithoutComment,
1033
+ compare,
1034
+ diceRoll,
1035
+ roller,
1036
+ engine
1037
+ );
1038
+ rerollCount = pityData.rerollCount;
1039
+ pityResult = pityData.result;
1040
+ if (pityResult) {
1041
+ return {
1042
+ ...pityResult,
1043
+ dice: prepared.isSimpleCurly ? finalDiceDisplay : processedDice,
1044
+ comment,
1045
+ compare,
1046
+ modifier: modificator,
1047
+ pityLogs: rerollCount,
1048
+ trivial: pityResult.trivial ?? (compare?.trivial ? true : void 0)
1049
+ };
1045
1050
  }
1046
- dice = result;
1047
1051
  }
1048
- if (dollarValue) dice = dice.replaceAll("$", dollarValue);
1049
- return replaceFormulaInDice(dice);
1052
+ let resultOutput = replaceUnwantedText(roller.output);
1053
+ if (prepared.explodingSuccess) {
1054
+ const successes = countExplodingSuccesses(
1055
+ diceRoll,
1056
+ prepared.explodingSuccess.sign,
1057
+ prepared.explodingSuccess.value
1058
+ );
1059
+ resultOutput = resultOutput.replace(/=\s*-?\d+(?:\.\d+)?$/, `= ${successes}`).replace(
1060
+ prepared.explodingSuccess.normalizedSegment,
1061
+ prepared.explodingSuccess.originalSegment
1062
+ );
1063
+ return {
1064
+ dice: prepared.isSimpleCurly ? finalDiceDisplay : prepared.diceDisplay,
1065
+ result: resultOutput,
1066
+ comment,
1067
+ compare: compare ? compare : void 0,
1068
+ modifier: modificator,
1069
+ total: successes,
1070
+ pityLogs: rerollCount > 0 ? rerollCount : void 0,
1071
+ trivial: compare?.trivial ? true : void 0
1072
+ };
1073
+ }
1074
+ return {
1075
+ dice: prepared.isSimpleCurly ? finalDiceDisplay : processedDice,
1076
+ result: resultOutput,
1077
+ comment,
1078
+ compare: compare ? compare : void 0,
1079
+ modifier: modificator,
1080
+ total: roller.total,
1081
+ pityLogs: rerollCount > 0 ? rerollCount : void 0,
1082
+ trivial: compare?.trivial ? true : void 0
1083
+ };
1050
1084
  }
1051
- function replaceFormulaInDice(dice) {
1052
- const formula = /(?<formula>\{{2}(.+?)}{2})/gim;
1053
- let match;
1054
- let modifiedDice = dice;
1055
- while ((match = formula.exec(dice)) !== null) {
1056
- if (match.groups?.formula) {
1057
- const formulae = match.groups.formula.replaceAll("{{", "").replaceAll("}}", "");
1085
+ function sharedRolls(dice, engine = import_rpg_dice_roller7.NumberGenerator.engines.nodeCrypto, pity, explodingSuccessMain, diceDisplay, isSharedCurly) {
1086
+ if (!explodingSuccessMain)
1087
+ explodingSuccessMain = normalizeExplodingSuccess(dice.split(";")[0] ?? dice);
1088
+ if (explodingSuccessMain) {
1089
+ dice = dice.replace(explodingSuccessMain.originalSegment, "!");
1090
+ }
1091
+ if (dice.match(/\d+?#(.*?)/))
1092
+ throw new DiceTypeError(
1093
+ dice,
1094
+ "noBulkRoll",
1095
+ "bulk roll are not allowed in shared rolls"
1096
+ );
1097
+ const results = [];
1098
+ const mainComment = /\s+#(?<comment>.*)/.exec(dice)?.groups?.comment?.trimEnd() ?? void 0;
1099
+ const split = dice.split(";");
1100
+ const displayDice = diceDisplay ?? explodingSuccessMain?.originalDice ?? split[0];
1101
+ let diceMain = fixParenthesis(split[0]);
1102
+ const commentsRegex = /\[(?<comments>.*?)\]/gi;
1103
+ const comments = formatComment(diceMain);
1104
+ const diceMainWithoutComments = diceMain.replace(commentsRegex, "").trim();
1105
+ const toHideRegex = /\((?<dice>[^)]+)\)/;
1106
+ const toHide = toHideRegex.exec(diceMainWithoutComments)?.groups;
1107
+ let hidden = false;
1108
+ if (toHide?.dice) {
1109
+ diceMain = toHide.dice;
1110
+ hidden = true;
1111
+ } else if (toHide) {
1112
+ diceMain = "1d1";
1113
+ hidden = true;
1114
+ } else {
1115
+ diceMain = diceMainWithoutComments;
1116
+ }
1117
+ const rollBounds = getRollBounds(diceMain, engine);
1118
+ let diceResult = roll(diceMain, engine, pity);
1119
+ if (!diceResult || !diceResult.total) {
1120
+ if (hidden) {
1121
+ diceResult = roll(fixParenthesis(split[0]), engine, pity);
1122
+ hidden = false;
1123
+ } else return void 0;
1124
+ }
1125
+ if (!diceResult || !diceResult.total) return void 0;
1126
+ if (explodingSuccessMain && diceResult.result) {
1127
+ const values = extractValuesFromOutput(diceResult.result);
1128
+ diceResult.total = values.filter(
1129
+ (v) => matchComparison(explodingSuccessMain.sign, v, explodingSuccessMain.value)
1130
+ ).length;
1131
+ }
1132
+ let aggregatedCompare = diceResult.compare;
1133
+ let hasTrivialComparison = diceResult.compare?.trivial === true;
1134
+ results.push(`\u203B ${comments}${diceResult.result}`);
1135
+ let total = diceResult.total;
1136
+ diceResult.comment = mainComment;
1137
+ if (!total) {
1138
+ return {
1139
+ dice: displayDice,
1140
+ result: results.join(";"),
1141
+ comment: mainComment,
1142
+ compare: aggregatedCompare,
1143
+ modifier: diceResult.modifier,
1144
+ total,
1145
+ trivial: hasTrivialComparison ? true : void 0
1146
+ };
1147
+ }
1148
+ for (let element of split.slice(1)) {
1149
+ const comment = formatComment(element);
1150
+ element = element.replaceAll(commentsRegex, "").replaceAll(OPTIONAL_COMMENT, "").trim();
1151
+ let toRoll = element.replace(SYMBOL_DICE, `${diceResult.total}`);
1152
+ const compareRegex = toRoll.match(SIGN_REGEX_SPACE);
1153
+ if (compareRegex) {
1154
+ if (isSharedCurly) {
1155
+ const compareResult = compareSignFormule(
1156
+ toRoll,
1157
+ compareRegex,
1158
+ element,
1159
+ diceResult,
1160
+ engine,
1161
+ pity,
1162
+ rollBounds
1163
+ );
1164
+ const { diceAll } = replaceText(element, diceResult.total, diceResult.dice);
1165
+ let successCount = 0;
1166
+ try {
1167
+ const evaluated = (0, import_mathjs8.evaluate)(toRoll);
1168
+ successCount = evaluated ? 1 : 0;
1169
+ } catch (error) {
1170
+ const evaluated = roll(toRoll, engine, pity);
1171
+ successCount = evaluated?.total ?? 0 ? 1 : 0;
1172
+ }
1173
+ results.push(`\u203B ${comment}${diceAll}: ${successCount}`);
1174
+ total += successCount;
1175
+ if (!aggregatedCompare && compareResult.compare)
1176
+ aggregatedCompare = compareResult.compare;
1177
+ if (compareResult.trivial) hasTrivialComparison = true;
1178
+ } else {
1179
+ const compareResult = compareSignFormule(
1180
+ toRoll,
1181
+ compareRegex,
1182
+ element,
1183
+ diceResult,
1184
+ engine,
1185
+ pity,
1186
+ rollBounds
1187
+ );
1188
+ toRoll = compareResult.dice;
1189
+ results.push(compareResult.results);
1190
+ if (!aggregatedCompare && compareResult.compare)
1191
+ aggregatedCompare = compareResult.compare;
1192
+ if (compareResult.trivial) hasTrivialComparison = true;
1193
+ }
1194
+ } else {
1195
+ const { formule, diceAll } = replaceText(
1196
+ element,
1197
+ diceResult.total,
1198
+ diceResult.dice
1199
+ );
1058
1200
  try {
1059
- const result = (0, import_mathjs6.evaluate)(formulae);
1060
- modifiedDice = modifiedDice.replace(match.groups.formula, result.toString());
1201
+ const evaluated = (0, import_mathjs8.evaluate)(toRoll);
1202
+ results.push(`\u25C8 ${comment}${diceAll}: ${formule} = ${evaluated}`);
1203
+ total += Number.parseInt(evaluated, 10);
1061
1204
  } catch (error) {
1062
- throw new FormulaError(match.groups.formula, "replaceFormulasInDice", error);
1205
+ const evaluated = roll(toRoll, engine, pity);
1206
+ if (evaluated) {
1207
+ results.push(
1208
+ `\u25C8 ${comment}${diceAll}: ${evaluated.result.split(":").slice(1).join(":")}`
1209
+ );
1210
+ if (!aggregatedCompare && evaluated.compare)
1211
+ aggregatedCompare = evaluated.compare;
1212
+ if (evaluated.compare?.trivial) hasTrivialComparison = true;
1213
+ } else results.push(`\u25C8 ${comment}${diceAll}: ${formule} = ${evaluated}`);
1214
+ total += evaluated?.total ?? 0;
1063
1215
  }
1064
1216
  }
1065
1217
  }
1066
- return cleanedDice(modifiedDice);
1067
- }
1068
- function cleanedDice(dice) {
1069
- return dice.replaceAll("+-", "-").replaceAll("--", "+").replaceAll("++", "+").replaceAll("=>", ">=").replaceAll("=<", "<=").trimEnd();
1070
- }
1071
- function isNumber(value) {
1072
- return value !== void 0 && (typeof value === "number" || !Number.isNaN(Number(value)) && typeof value === "string" && value.trim().length > 0);
1073
- }
1074
- function replaceExpByRandom(dice, engine = import_rpg_dice_roller6.NumberGenerator.engines.nodeCrypto) {
1075
- const diceRegex = /\{exp( ?\|\| ?(?<default>\d+))?}/gi;
1076
- return dice.replace(diceRegex, (_match, _p1, _p2, _offset, _string, groups) => {
1077
- const defaultValue = groups?.default;
1078
- return defaultValue ?? randomInt(1, 999, engine).toString();
1079
- });
1080
- }
1081
- function randomInt(min, max, engine = import_rpg_dice_roller6.NumberGenerator.engines.nodeCrypto, rng) {
1082
- if (!rng) rng = new import_random_js.Random(engine || void 0);
1083
- return rng.integer(min, max);
1218
+ if (hidden)
1219
+ results.shift();
1220
+ return {
1221
+ dice: displayDice,
1222
+ result: results.join(";"),
1223
+ comment: mainComment,
1224
+ compare: hasTrivialComparison && aggregatedCompare ? { ...aggregatedCompare, trivial: true } : aggregatedCompare,
1225
+ modifier: diceResult.modifier,
1226
+ total,
1227
+ trivial: hasTrivialComparison ? true : void 0
1228
+ };
1084
1229
  }
1085
- function levenshteinDistance(a, b) {
1086
- if (a === b) return 0;
1087
- const al = a.length;
1088
- const bl = b.length;
1089
- if (al === 0) return bl;
1090
- if (bl === 0) return al;
1091
- const v0 = new Array(bl + 1);
1092
- const v1 = new Array(bl + 1);
1093
- for (let i = 0; i <= bl; i++) v0[i] = i;
1094
- for (let i = 0; i < al; i++) {
1095
- v1[0] = i + 1;
1096
- for (let j = 0; j < bl; j++) {
1097
- const cost = a[i] === b[j] ? 0 : 1;
1098
- v1[j + 1] = Math.min(v1[j] + 1, v0[j + 1] + 1, v0[j] + cost);
1099
- }
1100
- for (let j = 0; j <= bl; j++) v0[j] = v1[j];
1230
+ function replaceInFormula(element, diceResult, compareResult, res, engine = import_rpg_dice_roller7.NumberGenerator.engines.nodeCrypto, pity) {
1231
+ const { formule, diceAll } = replaceText(
1232
+ element,
1233
+ diceResult.total ?? 0,
1234
+ diceResult.dice
1235
+ );
1236
+ const validSign = res ? "\u2713" : "\u2715";
1237
+ const invertedSign = res ? compareResult.compare.sign : inverseSign(compareResult.compare.sign);
1238
+ let evaluateRoll;
1239
+ try {
1240
+ evaluateRoll = (0, import_mathjs8.evaluate)(compareResult.dice);
1241
+ return `${validSign} ${diceAll}: ${formule} = ${evaluateRoll}${invertedSign}${compareResult.compare?.value}`;
1242
+ } catch (error) {
1243
+ const evaluateRoll2 = roll(compareResult.dice, engine, pity);
1244
+ if (evaluateRoll2)
1245
+ return `${validSign} ${diceAll}: ${evaluateRoll2.result.split(":").splice(1).join(":")}`;
1246
+ return `${validSign} ${diceAll}: ${formule} = ${evaluateRoll2}${invertedSign}${compareResult.compare?.value}`;
1101
1247
  }
1102
- return v1[bl];
1103
- }
1104
- function similarityScore(a, b) {
1105
- const la = a.length;
1106
- const lb = b.length;
1107
- if (la === 0 && lb === 0) return 1;
1108
- const dist = levenshteinDistance(a, b);
1109
- const max = Math.max(la, lb);
1110
- return 1 - dist / max;
1111
- }
1112
- function createCriticalCustom(dice, customCritical, template, engine = import_rpg_dice_roller6.NumberGenerator.engines.nodeCrypto) {
1113
- const compareRegex = dice.match(SIGN_REGEX_SPACE);
1114
- let customDice = dice;
1115
- const compareValue = diceTypeRandomParse(customCritical.value, template, engine);
1116
- if (compareValue.includes("$"))
1117
- throw new DiceTypeError(compareValue, "createCriticalCustom");
1118
- const comparaison = `${customCritical.sign}${compareValue}`;
1119
- if (compareRegex) customDice = customDice.replace(SIGN_REGEX_SPACE, comparaison);
1120
- else customDice += comparaison;
1121
- return diceTypeRandomParse(customDice, template, engine);
1122
1248
  }
1123
1249
 
1124
1250
  // src/verify_template.ts
1125
- var import_mathjs7 = require("mathjs");
1251
+ var import_mathjs9 = require("mathjs");
1126
1252
  var import_random_js2 = require("random-js");
1127
1253
  var import_uniformize2 = require("uniformize");
1128
- var import_rpg_dice_roller7 = require("@dice-roller/rpg-dice-roller");
1129
- function evalStatsDice(testDice, allStats, engine = import_rpg_dice_roller7.NumberGenerator.engines.nodeCrypto, pity) {
1254
+ var import_rpg_dice_roller8 = require("@dice-roller/rpg-dice-roller");
1255
+ function evalStatsDice(testDice, allStats, engine = import_rpg_dice_roller8.NumberGenerator.engines.nodeCrypto, pity) {
1130
1256
  let dice = testDice.trimEnd();
1131
1257
  if (allStats && Object.keys(allStats).length > 0) {
1132
1258
  const names = Object.keys(allStats);
@@ -1146,7 +1272,7 @@ function evalStatsDice(testDice, allStats, engine = import_rpg_dice_roller7.Numb
1146
1272
  throw new DiceTypeError(dice, "evalStatsDice", error);
1147
1273
  }
1148
1274
  }
1149
- function diceRandomParse(value, template, engine = import_rpg_dice_roller7.NumberGenerator.engines.nodeCrypto) {
1275
+ function diceRandomParse(value, template, engine = import_rpg_dice_roller8.NumberGenerator.engines.nodeCrypto) {
1150
1276
  if (!template.statistics) return replaceFormulaInDice(value.standardize());
1151
1277
  value = value.standardize();
1152
1278
  const statNames = Object.keys(template.statistics);
@@ -1168,7 +1294,7 @@ function diceRandomParse(value, template, engine = import_rpg_dice_roller7.Numbe
1168
1294
  }
1169
1295
  return replaceFormulaInDice(newDice);
1170
1296
  }
1171
- function diceTypeRandomParse(dice, template, engine = import_rpg_dice_roller7.NumberGenerator.engines.nodeCrypto) {
1297
+ function diceTypeRandomParse(dice, template, engine = import_rpg_dice_roller8.NumberGenerator.engines.nodeCrypto) {
1172
1298
  dice = replaceExpByRandom(dice);
1173
1299
  if (!template.statistics) return dice;
1174
1300
  const firstStatNotcombinaison = Object.keys(template.statistics).find(
@@ -1190,7 +1316,7 @@ function evalCombinaison(combinaison, stats) {
1190
1316
  formula = formula.replace(regex, value.toString());
1191
1317
  }
1192
1318
  try {
1193
- newStats[stat] = (0, import_mathjs7.evaluate)(formula);
1319
+ newStats[stat] = (0, import_mathjs9.evaluate)(formula);
1194
1320
  } catch (error) {
1195
1321
  throw new FormulaError(stat, "evalCombinaison", error);
1196
1322
  }
@@ -1204,7 +1330,7 @@ function evalOneCombinaison(combinaison, stats) {
1204
1330
  formula = formula.replace(regex, value.toString());
1205
1331
  }
1206
1332
  try {
1207
- return (0, import_mathjs7.evaluate)(formula);
1333
+ return (0, import_mathjs9.evaluate)(formula);
1208
1334
  } catch (error) {
1209
1335
  throw new FormulaError(combinaison, "evalOneCombinaison", error);
1210
1336
  }
@@ -1216,7 +1342,7 @@ function convertNumber(number) {
1216
1342
  if (isNumber(number)) return Number.parseInt(number.toString(), 10);
1217
1343
  return void 0;
1218
1344
  }
1219
- function verifyTemplateValue(template, verify = true, engine = import_rpg_dice_roller7.NumberGenerator.engines.nodeCrypto) {
1345
+ function verifyTemplateValue(template, verify = true, engine = import_rpg_dice_roller8.NumberGenerator.engines.nodeCrypto) {
1220
1346
  const parsedTemplate = templateSchema.parse(template);
1221
1347
  const { success, failure } = parsedTemplate.critical ?? {};
1222
1348
  const criticicalVal = {
@@ -1271,7 +1397,7 @@ function verifyTemplateValue(template, verify = true, engine = import_rpg_dice_r
1271
1397
  testStatCombinaison(statistiqueTemplate, engine);
1272
1398
  return statistiqueTemplate;
1273
1399
  }
1274
- function testDiceRegistered(template, engine = import_rpg_dice_roller7.NumberGenerator.engines.nodeCrypto) {
1400
+ function testDiceRegistered(template, engine = import_rpg_dice_roller8.NumberGenerator.engines.nodeCrypto) {
1275
1401
  if (!template.damage) return;
1276
1402
  if (Object.keys(template.damage).length === 0) throw new EmptyObjectError();
1277
1403
  if (Object.keys(template.damage).length > 25) throw new TooManyDice();
@@ -1287,7 +1413,7 @@ function testDiceRegistered(template, engine = import_rpg_dice_roller7.NumberGen
1287
1413
  }
1288
1414
  }
1289
1415
  }
1290
- function testStatCombinaison(template, engine = import_rpg_dice_roller7.NumberGenerator.engines.nodeCrypto) {
1416
+ function testStatCombinaison(template, engine = import_rpg_dice_roller8.NumberGenerator.engines.nodeCrypto) {
1291
1417
  if (!template.statistics) return;
1292
1418
  const onlycombinaisonStats = Object.fromEntries(
1293
1419
  Object.entries(template.statistics).filter(
@@ -1313,7 +1439,7 @@ function testStatCombinaison(template, engine = import_rpg_dice_roller7.NumberGe
1313
1439
  formula = formula.replace(regex, randomStatValue.toString());
1314
1440
  }
1315
1441
  try {
1316
- (0, import_mathjs7.evaluate)(formula);
1442
+ (0, import_mathjs9.evaluate)(formula);
1317
1443
  } catch (e) {
1318
1444
  error.push(stat);
1319
1445
  }
@@ -1321,9 +1447,9 @@ function testStatCombinaison(template, engine = import_rpg_dice_roller7.NumberGe
1321
1447
  if (error.length > 0) throw new FormulaError(error.join(", "), "testStatCombinaison");
1322
1448
  return;
1323
1449
  }
1324
- function generateRandomStat(total = 100, max, min, engine = import_rpg_dice_roller7.NumberGenerator.engines.nodeCrypto) {
1450
+ function generateRandomStat(total = 100, max, min, engine = import_rpg_dice_roller8.NumberGenerator.engines.nodeCrypto) {
1325
1451
  let randomStatValue = total + 1;
1326
- const random = new import_random_js2.Random(engine || import_rpg_dice_roller7.NumberGenerator.engines.nodeCrypto);
1452
+ const random = new import_random_js2.Random(engine || import_rpg_dice_roller8.NumberGenerator.engines.nodeCrypto);
1327
1453
  while (randomStatValue >= total || randomStatValue === 0) {
1328
1454
  if (max && min) randomStatValue = randomInt(min, max, engine, random);
1329
1455
  else if (max) randomStatValue = randomInt(1, max, engine, random);
@@ -1337,7 +1463,6 @@ function generateRandomStat(total = 100, max, min, engine = import_rpg_dice_roll
1337
1463
  COMMENT_REGEX,
1338
1464
  DETECT_CRITICAL,
1339
1465
  DiceTypeError,
1340
- EXPLODING_SUCCESS_REGEX,
1341
1466
  EmptyObjectError,
1342
1467
  FormulaError,
1343
1468
  MaxGreater,
@@ -1349,11 +1474,6 @@ function generateRandomStat(total = 100, max, min, engine = import_rpg_dice_roll
1349
1474
  SortOrder,
1350
1475
  TooManyDice,
1351
1476
  TooManyStats,
1352
- calculator,
1353
- canComparisonFail,
1354
- canComparisonSucceed,
1355
- compareSignFormule,
1356
- countExplodingSuccesses,
1357
1477
  createCriticalCustom,
1358
1478
  diceRandomParse,
1359
1479
  diceTypeRandomParse,
@@ -1361,29 +1481,16 @@ function generateRandomStat(total = 100, max, min, engine = import_rpg_dice_roll
1361
1481
  evalCombinaison,
1362
1482
  evalOneCombinaison,
1363
1483
  evalStatsDice,
1364
- extractValuesFromOutput,
1365
- fixParenthesis,
1366
- formatComment,
1367
1484
  generateRandomStat,
1368
1485
  generateStatsDice,
1369
- getCompare,
1370
1486
  getEngine,
1371
1487
  getEngineId,
1372
- getModifier,
1373
- getRollBounds,
1374
- inverseSign,
1375
1488
  isNumber,
1376
- isTrivialComparison,
1377
- matchComparison,
1378
- normalizeExplodingSuccess,
1379
1489
  randomInt,
1380
1490
  replaceExpByRandom,
1381
1491
  replaceFormulaInDice,
1382
1492
  replaceInFormula,
1383
- replaceText,
1384
- replaceUnwantedText,
1385
1493
  roll,
1386
- rollCompare,
1387
1494
  standardizeDice,
1388
1495
  templateSchema,
1389
1496
  testDiceRegistered,