@dicelette/core 1.28.1 → 1.28.3

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
@@ -60,7 +60,9 @@ __export(index_exports, {
60
60
  replaceFormulaInDice: () => replaceFormulaInDice,
61
61
  replaceInFormula: () => replaceInFormula,
62
62
  replaceUnknown: () => replaceUnknown,
63
+ resolveFormulaHint: () => resolveFormulaHint,
63
64
  roll: () => roll,
65
+ splitDiceComment: () => splitDiceComment,
64
66
  standardizeDice: () => standardizeDice,
65
67
  templateSchema: () => templateSchema,
66
68
  testDiceRegistered: () => testDiceRegistered,
@@ -277,211 +279,261 @@ function includeDiceType(dice, diceType, userStats) {
277
279
  }
278
280
 
279
281
  // src/roll.ts
280
- var import_rpg_dice_roller7 = require("@dice-roller/rpg-dice-roller");
281
- var import_mathjs8 = require("mathjs");
282
+ var import_rpg_dice_roller8 = require("@dice-roller/rpg-dice-roller");
283
+ var import_mathjs10 = require("mathjs");
282
284
 
283
285
  // src/dice/bulk.ts
284
- var import_rpg_dice_roller6 = require("@dice-roller/rpg-dice-roller");
285
- var import_mathjs6 = require("mathjs");
286
+ var import_rpg_dice_roller7 = require("@dice-roller/rpg-dice-roller");
287
+ var import_mathjs8 = require("mathjs");
286
288
 
287
- // src/dice/compare.ts
289
+ // src/utils.ts
290
+ var import_uniformize2 = require("uniformize");
288
291
  var import_rpg_dice_roller3 = require("@dice-roller/rpg-dice-roller");
289
- var import_mathjs2 = require("mathjs");
292
+ var import_random_js2 = require("random-js");
290
293
 
291
- // src/utils.ts
294
+ // src/verify_template.ts
292
295
  var import_mathjs = require("mathjs");
296
+ var import_random_js = require("random-js");
293
297
  var import_uniformize = require("uniformize");
294
298
  var import_rpg_dice_roller2 = require("@dice-roller/rpg-dice-roller");
295
- var import_random_js = require("random-js");
296
-
297
- // src/similarity.ts
298
- function calculateSimilarity(str1, str2) {
299
- const longer = str1.length > str2.length ? str1 : str2;
300
- const shorter = str1.length > str2.length ? str2 : str1;
301
- if (longer.length === 0) return 1;
302
- const distance = levenshteinDistance(longer, shorter);
303
- return (longer.length - distance) / longer.length;
304
- }
305
- function levenshteinDistance(str1, str2) {
306
- const matrix = Array(str2.length + 1).fill(null).map(() => Array(str1.length + 1).fill(null));
307
- for (let i = 0; i <= str1.length; i++) matrix[0][i] = i;
308
- for (let j = 0; j <= str2.length; j++) matrix[j][0] = j;
309
- for (let j = 1; j <= str2.length; j++) {
310
- for (let i = 1; i <= str1.length; i++) {
311
- const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;
312
- matrix[j][i] = Math.min(
313
- matrix[j][i - 1] + 1,
314
- // insertion
315
- matrix[j - 1][i] + 1,
316
- // deletion
317
- matrix[j - 1][i - 1] + cost
318
- // substitution
319
- );
299
+ function evalStatsDice(testDice, allStats, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto, pity) {
300
+ let dice = testDice.trimEnd();
301
+ if (allStats && Object.keys(allStats).length > 0) {
302
+ const names = Object.keys(allStats);
303
+ for (const name of names) {
304
+ const regex = new RegExp(escapeRegex(name.standardize()), "gi");
305
+ if (dice.standardize().match(regex)) {
306
+ const statValue = allStats[name];
307
+ dice = dice.standardize().replace(regex, statValue.toString()).trimEnd();
308
+ }
320
309
  }
321
310
  }
322
- return matrix[str2.length][str1.length];
323
- }
324
- function findBestStatMatch(searchTerm, normalizedStats, similarityThreshold = MIN_THRESHOLD_MATCH) {
325
- const exact = normalizedStats.get(searchTerm);
326
- if (exact) return exact;
327
- const candidates = [];
328
- for (const [normalizedKey, original] of normalizedStats) {
329
- if (normalizedKey.startsWith(searchTerm))
330
- candidates.push([original, calculateSimilarity(searchTerm, normalizedKey)]);
331
- }
332
- if (candidates.length === 1) return candidates[0][0];
333
- if (candidates.length > 0) {
334
- candidates.sort((a, b) => b[1] - a[1]);
335
- if (candidates[0][1] >= similarityThreshold) return candidates[0][0];
336
- }
337
- let bestMatch;
338
- let bestSimilarity = 0;
339
- for (const [normalizedKey, original] of normalizedStats) {
340
- const similarity = calculateSimilarity(searchTerm, normalizedKey);
341
- if (similarity === 1) return original;
342
- if (similarity > bestSimilarity && similarity >= similarityThreshold) {
343
- bestSimilarity = similarity;
344
- bestMatch = original;
345
- }
311
+ try {
312
+ if (!roll(replaceFormulaInDice(replaceExpByRandom(dice)), engine, pity))
313
+ throw new DiceTypeError(dice, "evalStatsDice", "no roll result");
314
+ return testDice;
315
+ } catch (error) {
316
+ throw new DiceTypeError(dice, "evalStatsDice", error);
346
317
  }
347
- return bestMatch;
348
318
  }
349
- function findBestRecord(record, searchTerm, similarityThreshold = MIN_THRESHOLD_MATCH) {
350
- const normalizeRecord = /* @__PURE__ */ new Map();
351
- for (const key of Object.keys(record)) {
352
- normalizeRecord.set(key.standardize(), key);
319
+ function diceRandomParse(value, template, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto) {
320
+ if (!template.statistics) return replaceFormulaInDice(value.standardize());
321
+ value = value.standardize();
322
+ const statNames = Object.keys(template.statistics);
323
+ let newDice = value;
324
+ for (const name of statNames) {
325
+ const regex = new RegExp(escapeRegex(name.standardize()), "gi");
326
+ if (value.match(regex)) {
327
+ let max;
328
+ let min;
329
+ const foundStat = template.statistics?.[name];
330
+ if (foundStat) {
331
+ max = foundStat.max;
332
+ min = foundStat.min;
333
+ }
334
+ const total = template.total || 100;
335
+ const randomStatValue = generateRandomStat(total, max, min, engine);
336
+ newDice = value.replace(regex, randomStatValue.toString());
337
+ }
353
338
  }
354
- return findBestStatMatch(
355
- searchTerm.standardize(),
356
- normalizeRecord,
357
- similarityThreshold
358
- ) || null;
339
+ return replaceFormulaInDice(newDice);
359
340
  }
360
- function replaceUnknown(dice, replacer) {
361
- return dice.replaceAll(REMOVER_PATTERN.STAT_MATCHER, replacer).replaceAll("+0", "").replaceAll("-0", "");
341
+ function diceTypeRandomParse(dice, template, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto) {
342
+ dice = replaceExpByRandom(dice);
343
+ if (!template.statistics) return dice;
344
+ const firstStatNotcombinaison = Object.keys(template.statistics).find(
345
+ (stat) => !template.statistics?.[stat].combinaison
346
+ );
347
+ if (!firstStatNotcombinaison) return dice;
348
+ const stats = template.statistics[firstStatNotcombinaison];
349
+ const { min, max } = stats;
350
+ const total = template.total || 100;
351
+ const randomStatValue = generateRandomStat(total, max, min, engine);
352
+ return replaceFormulaInDice(dice.replaceAll("$", randomStatValue.toString()));
362
353
  }
363
- function verifyStatMatcherPattern(dice, replaceUnknow) {
364
- if (REMOVER_PATTERN.STAT_MATCHER.test(dice)) {
365
- if (replaceUnknow)
366
- return replaceUnknown(dice, replaceUnknow);
367
- const matched = dice.matchAll(new RegExp(REMOVER_PATTERN.STAT_MATCHER));
368
- const stats = matched ? Array.from(matched, (m) => m?.[0]).map((s) => `\`${s}\``).join(", ") : "unknown";
369
- throw new DiceTypeError(stats, "unknown_stats");
354
+ function evalCombinaison(combinaison, stats) {
355
+ const newStats = {};
356
+ for (const [stat, combin] of Object.entries(combinaison)) {
357
+ let formula = combin.standardize();
358
+ for (const [statName, value] of Object.entries(stats)) {
359
+ const regex = new RegExp(statName.standardize(), "gi");
360
+ formula = formula.replace(regex, value.toString());
361
+ }
362
+ try {
363
+ newStats[stat] = (0, import_mathjs.evaluate)(formula);
364
+ } catch (error) {
365
+ throw new FormulaError(stat, "evalCombinaison", error);
366
+ }
370
367
  }
371
- return dice.replaceAll("+0", "").replaceAll("-0", "");
368
+ return newStats;
372
369
  }
373
-
374
- // src/utils.ts
375
- function escapeRegex(string) {
376
- return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
370
+ function evalOneCombinaison(combinaison, stats) {
371
+ let formula = combinaison.standardize();
372
+ for (const [statName, value] of Object.entries(stats)) {
373
+ const regex = new RegExp(statName.standardize(), "gi");
374
+ formula = formula.replace(regex, value.toString());
375
+ }
376
+ try {
377
+ return (0, import_mathjs.evaluate)(formula);
378
+ } catch (error) {
379
+ throw new FormulaError(combinaison, "evalOneCombinaison", error);
380
+ }
377
381
  }
378
- function standardizeDice(dice) {
379
- return dice.replace(
380
- /(\[[^\]]+])|([^[]+)/g,
381
- (_match, insideBrackets, outsideText) => insideBrackets ? insideBrackets : outsideText.standardize().replaceAll("df", "dF")
382
- );
382
+ function convertNumber(number) {
383
+ if (number === void 0 || number === null) return void 0;
384
+ if (number.toString().length === 0 || Number.isNaN(Number.parseInt(number.toString(), 10)))
385
+ return void 0;
386
+ if (isNumber(number)) return Number.parseInt(number.toString(), 10);
387
+ return void 0;
383
388
  }
384
- function handleDiceAfterD(tokenStd, normalizedStats) {
385
- const diceMatch = /^(\d*)d(.+)$/i.exec(tokenStd);
386
- if (!diceMatch) return null;
387
- const diceCount = diceMatch[1] || "";
388
- const afterD = diceMatch[2];
389
- const bestMatch = findBestStatMatch(afterD, normalizedStats, 1);
390
- if (bestMatch) {
391
- const [, value] = bestMatch;
392
- return `${diceCount}d${value.toString()}`;
389
+ function verifyTemplateValue(template, verify = true, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto) {
390
+ const parsedTemplate = templateSchema.parse(template);
391
+ const { success, failure } = parsedTemplate.critical ?? {};
392
+ const criticicalVal = {
393
+ success: convertNumber(success),
394
+ failure: convertNumber(failure)
395
+ };
396
+ const statistiqueTemplate = {
397
+ diceType: parsedTemplate.diceType,
398
+ statistics: parsedTemplate.statistics,
399
+ critical: criticicalVal,
400
+ total: parsedTemplate.total,
401
+ charName: parsedTemplate.charName,
402
+ damage: parsedTemplate.damage,
403
+ customCritical: parsedTemplate.customCritical,
404
+ forceDistrib: parsedTemplate.forceDistrib
405
+ };
406
+ if (!verify) return statistiqueTemplate;
407
+ if (statistiqueTemplate.diceType) {
408
+ if (statistiqueTemplate.diceType.match(DETECT_CRITICAL)) {
409
+ throw new DiceTypeError(
410
+ statistiqueTemplate.diceType,
411
+ "critical_dice_type",
412
+ "contains critical detection: should be in custom critical instead"
413
+ );
414
+ }
415
+ const cleanedDice2 = diceTypeRandomParse(
416
+ statistiqueTemplate.diceType,
417
+ statistiqueTemplate,
418
+ engine
419
+ );
420
+ const rolled = roll(cleanedDice2, engine);
421
+ if (!rolled) throw new DiceTypeError(cleanedDice2, "no_roll_result", "no roll result");
393
422
  }
394
- return null;
395
- }
396
- function handleSimpleToken(tokenStd, token, normalizedStats, minThreshold) {
397
- const bestMatch = findBestStatMatch(tokenStd, normalizedStats, minThreshold);
398
- if (bestMatch) {
399
- const [, value] = bestMatch;
400
- return value.toString();
423
+ if (statistiqueTemplate.customCritical) {
424
+ if (!statistiqueTemplate.diceType) {
425
+ throw new DiceTypeError("no_dice_type", "no_dice_type", "no dice type");
426
+ }
427
+ const customCritical = statistiqueTemplate.customCritical;
428
+ for (const [, custom] of Object.entries(customCritical)) {
429
+ const cleanedDice2 = createCriticalCustom(
430
+ statistiqueTemplate.diceType,
431
+ custom,
432
+ statistiqueTemplate,
433
+ engine
434
+ );
435
+ const rolled = roll(cleanedDice2, engine);
436
+ if (!rolled)
437
+ throw new DiceTypeError(cleanedDice2, "verifyTemplateValue", "no roll result");
438
+ }
401
439
  }
402
- return token;
440
+ testDiceRegistered(statistiqueTemplate, engine);
441
+ testStatCombinaison(statistiqueTemplate, engine);
442
+ return statistiqueTemplate;
403
443
  }
404
- function generateStatsDice(originalDice, stats, minThreshold = 0.6, dollarValue) {
405
- let dice = originalDice.standardize();
406
- if (stats && Object.keys(stats).length > 0) {
407
- const normalizedStats = /* @__PURE__ */ new Map();
408
- for (const [key, value] of Object.entries(stats)) {
409
- const normalized = key.standardize();
410
- normalizedStats.set(normalized, [key, value]);
444
+ function testDiceRegistered(template, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto) {
445
+ if (!template.damage) return;
446
+ if (Object.keys(template.damage).length === 0) throw new EmptyObjectError();
447
+ if (Object.keys(template.damage).length > 25) throw new TooManyDice();
448
+ for (const [name, dice] of Object.entries(template.damage)) {
449
+ if (!dice) continue;
450
+ const diceReplaced = replaceExpByRandom(dice);
451
+ const randomDiceParsed = diceRandomParse(diceReplaced, template, engine);
452
+ try {
453
+ const rolled = roll(randomDiceParsed, engine);
454
+ if (!rolled) throw new DiceTypeError(name, "no_roll_result", dice);
455
+ } catch (error) {
456
+ throw new DiceTypeError(name, "testDiceRegistered", error);
411
457
  }
412
- const partsRegex = /(\[[^\]]+])|([^[]+)/g;
413
- let result = "";
414
- let match;
415
- while ((match = partsRegex.exec(dice)) !== null) {
416
- const insideBrackets = match[1];
417
- const outsideText = match[2];
418
- if (insideBrackets) {
419
- result += insideBrackets;
420
- continue;
421
- }
422
- if (!outsideText) {
423
- continue;
424
- }
425
- const tokenRegex = /(\$?[\p{L}\p{N}_.]+)/gu;
426
- let lastIndex = 0;
427
- let tokenMatch;
428
- while ((tokenMatch = tokenRegex.exec(outsideText)) !== null) {
429
- result += outsideText.slice(lastIndex, tokenMatch.index);
430
- const token = tokenMatch[0];
431
- const tokenHasDollar = token.startsWith("$");
432
- const tokenForCompare = tokenHasDollar ? token.slice(1) : token;
433
- const tokenStd = tokenForCompare.standardize();
434
- const diceReplacement = handleDiceAfterD(tokenStd, normalizedStats);
435
- if (diceReplacement) {
436
- result += diceReplacement;
437
- lastIndex = tokenRegex.lastIndex;
438
- continue;
439
- }
440
- result += handleSimpleToken(tokenStd, token, normalizedStats, minThreshold);
441
- lastIndex = tokenRegex.lastIndex;
442
- }
443
- result += outsideText.slice(lastIndex);
458
+ }
459
+ }
460
+ function testStatCombinaison(template, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto) {
461
+ if (!template.statistics) return;
462
+ const onlycombinaisonStats = Object.fromEntries(
463
+ Object.entries(template.statistics).filter(
464
+ ([_, value]) => value.combinaison !== void 0
465
+ )
466
+ );
467
+ const allOtherStats = Object.fromEntries(
468
+ Object.entries(template.statistics).filter(([_, value]) => !value.combinaison)
469
+ );
470
+ if (Object.keys(onlycombinaisonStats).length === 0) return;
471
+ const allStats = Object.keys(template.statistics).filter(
472
+ (stat) => !template.statistics[stat].combinaison
473
+ );
474
+ if (allStats.length === 0) throw new NoStatisticsError();
475
+ const error = [];
476
+ for (const [stat, value] of Object.entries(onlycombinaisonStats)) {
477
+ let formula = value.combinaison;
478
+ for (const [other, data] of Object.entries(allOtherStats)) {
479
+ const { max, min } = data;
480
+ const total = template.total || 100;
481
+ const randomStatValue = generateRandomStat(total, max, min, engine);
482
+ const regex = new RegExp(other, "gi");
483
+ formula = formula.replace(regex, randomStatValue.toString());
484
+ }
485
+ try {
486
+ (0, import_mathjs.evaluate)(formula);
487
+ } catch (e) {
488
+ error.push(stat);
444
489
  }
445
- dice = result;
446
490
  }
447
- if (dollarValue) dice = dice.replaceAll("$", dollarValue);
448
- return replaceFormulaInDice(dice);
491
+ if (error.length > 0) throw new FormulaError(error.join(", "), "testStatCombinaison");
492
+ return;
449
493
  }
450
- function replaceFormulaInDice(dice) {
451
- const formula = /(?<formula>\{{2}(.+?)}{2})/gim;
452
- let match;
453
- let modifiedDice = dice;
454
- while ((match = formula.exec(dice)) !== null) {
455
- if (match.groups?.formula) {
456
- const formulae = match.groups.formula.replaceAll("{{", "").replaceAll("}}", "");
457
- try {
458
- const result = (0, import_mathjs.evaluate)(formulae);
459
- modifiedDice = modifiedDice.replace(match.groups.formula, result.toString());
460
- } catch (error) {
461
- throw new FormulaError(match.groups.formula, "replaceFormulasInDice", error);
462
- }
463
- }
494
+ function generateRandomStat(total = 100, max, min, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto) {
495
+ let randomStatValue = total + 1;
496
+ const random = new import_random_js.Random(engine || import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto);
497
+ while (randomStatValue >= total || randomStatValue === 0) {
498
+ if (max && min) randomStatValue = randomInt(min, max, engine, random);
499
+ else if (max) randomStatValue = randomInt(1, max, engine, random);
500
+ else if (min) randomStatValue = randomInt(min, total, engine, random);
501
+ else randomStatValue = randomInt(1, total, engine, random);
464
502
  }
465
- return cleanedDice(modifiedDice);
503
+ return randomStatValue;
466
504
  }
467
- function cleanedDice(dice) {
468
- return dice.replaceAll("+-", "-").replaceAll("--", "+").replaceAll("++", "+").replaceAll("=>", ">=").replaceAll("=<", "<=").trimEnd();
505
+
506
+ // src/utils.ts
507
+ function splitDiceComment(dice) {
508
+ const match = /\s+(#|\/{2}|\[|\/\*)(?<comment>.*)/i.exec(dice);
509
+ if (!match?.groups) return { dice: dice.trimEnd(), comment: void 0 };
510
+ const comment = match.groups.comment.trim() || void 0;
511
+ return { dice: dice.slice(0, match.index).trimEnd(), comment };
512
+ }
513
+ function escapeRegex(string) {
514
+ return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
515
+ }
516
+ function standardizeDice(dice) {
517
+ return dice.replace(
518
+ /(\[[^\]]+])|([^[]+)/g,
519
+ (_match, insideBrackets, outsideText) => insideBrackets ? insideBrackets : outsideText.standardize().replaceAll("df", "dF")
520
+ );
469
521
  }
470
522
  function isNumber(value) {
471
523
  return value !== void 0 && (typeof value === "number" || !Number.isNaN(Number(value)) && typeof value === "string" && value.trim().length > 0);
472
524
  }
473
- function replaceExpByRandom(dice, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto) {
525
+ function replaceExpByRandom(dice, engine = import_rpg_dice_roller3.NumberGenerator.engines.nodeCrypto) {
474
526
  const diceRegex = /\{exp( ?\|\| ?(?<default>\d+))?}/gi;
475
527
  return dice.replace(diceRegex, (_match, _p1, _p2, _offset, _string, groups) => {
476
528
  const defaultValue = groups?.default;
477
529
  return defaultValue ?? randomInt(1, 999, engine).toString();
478
530
  });
479
531
  }
480
- function randomInt(min, max, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto, rng) {
481
- if (!rng) rng = new import_random_js.Random(engine || void 0);
532
+ function randomInt(min, max, engine = import_rpg_dice_roller3.NumberGenerator.engines.nodeCrypto, rng) {
533
+ if (!rng) rng = new import_random_js2.Random(engine || void 0);
482
534
  return rng.integer(min, max);
483
535
  }
484
- function createCriticalCustom(dice, customCritical, template, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto) {
536
+ function createCriticalCustom(dice, customCritical, template, engine = import_rpg_dice_roller3.NumberGenerator.engines.nodeCrypto) {
485
537
  const compareRegex = dice.match(SIGN_REGEX_SPACE);
486
538
  let customDice = dice;
487
539
  const compareValue = diceTypeRandomParse(customCritical.value, template, engine);
@@ -494,6 +546,8 @@ function createCriticalCustom(dice, customCritical, template, engine = import_rp
494
546
  }
495
547
 
496
548
  // src/dice/compare.ts
549
+ var import_rpg_dice_roller4 = require("@dice-roller/rpg-dice-roller");
550
+ var import_mathjs2 = require("mathjs");
497
551
  function isTrivialComparison(maxValue, minValue, compare) {
498
552
  const canSucceed = canComparisonSucceed(maxValue, compare, minValue);
499
553
  const canFail = canComparisonFail(maxValue, compare, minValue);
@@ -524,7 +578,7 @@ function canComparisonFail(maxRollValue, compare, minRollValue = 1) {
524
578
  return true;
525
579
  }
526
580
  }
527
- function rollCompare(value, engine = import_rpg_dice_roller3.NumberGenerator.engines.nodeCrypto, pity) {
581
+ function rollCompare(value, engine = import_rpg_dice_roller4.NumberGenerator.engines.nodeCrypto, pity) {
528
582
  if (isNumber(value)) return { value: Number.parseInt(value, 10) };
529
583
  if (!value || typeof value === "string" && value.trim() === "") {
530
584
  return { value: 0, diceResult: value };
@@ -543,7 +597,7 @@ function rollCompare(value, engine = import_rpg_dice_roller3.NumberGenerator.eng
543
597
  diceResult: rollComp?.result
544
598
  };
545
599
  }
546
- function getCompare(dice, compareRegex, engine = import_rpg_dice_roller3.NumberGenerator.engines.nodeCrypto, pity) {
600
+ function getCompare(dice, compareRegex, engine = import_rpg_dice_roller4.NumberGenerator.engines.nodeCrypto, pity) {
547
601
  if (dice.match(/((\{.*,(.*)+\}|([><=!]+\d+f))([><=]|!=)+\d+\}?)|\{(.*)(([><=]|!=)+).*\}/))
548
602
  return { dice, compare: void 0 };
549
603
  dice = dice.replace(SIGN_REGEX_SPACE, "");
@@ -599,7 +653,7 @@ function canComparisonSucceed(maxRollValue, compare, minRollValue) {
599
653
  var import_mathjs4 = require("mathjs");
600
654
 
601
655
  // src/dice/signs.ts
602
- var import_rpg_dice_roller4 = require("@dice-roller/rpg-dice-roller");
656
+ var import_rpg_dice_roller5 = require("@dice-roller/rpg-dice-roller");
603
657
  var import_mathjs3 = require("mathjs");
604
658
 
605
659
  // src/dice/replace.ts
@@ -689,7 +743,7 @@ function inverseSign(sign) {
689
743
  return "==";
690
744
  }
691
745
  }
692
- function compareSignFormule(toRoll, compareRegex, element, diceResult, engine = import_rpg_dice_roller4.NumberGenerator.engines.nodeCrypto, pity, rollBounds) {
746
+ function compareSignFormule(toRoll, compareRegex, element, diceResult, engine = import_rpg_dice_roller5.NumberGenerator.engines.nodeCrypto, pity, rollBounds) {
693
747
  let results = "";
694
748
  let trivial = false;
695
749
  const compareResult = getCompare(toRoll, compareRegex, engine);
@@ -750,43 +804,278 @@ function normalizeExplodingSuccess(dice) {
750
804
  parsedValue = 0;
751
805
  }
752
806
  }
753
- const normalizedSegment = "!";
754
- const replacedDice = dice.replace(match[0], normalizedSegment);
755
- return {
756
- dice: replacedDice,
757
- originalDice: dice,
758
- sign: normalizedSign,
759
- value: parsedValue,
760
- normalizedSegment,
761
- originalSegment: match[0]
762
- };
763
- }
764
- function countExplodingSuccesses(diceRoll, sign, value) {
765
- const rollsArray = Array.isArray(diceRoll) ? diceRoll : [diceRoll];
766
- const flatValues = [];
767
- for (const dr of rollsArray) {
768
- const groups = dr.rolls ?? [];
769
- for (const group of groups) {
770
- const innerRolls = group.rolls ?? [];
771
- for (const roll2 of innerRolls) {
772
- if (typeof roll2.value === "number") flatValues.push(roll2.value);
807
+ const normalizedSegment = "!";
808
+ const replacedDice = dice.replace(match[0], normalizedSegment);
809
+ return {
810
+ dice: replacedDice,
811
+ originalDice: dice,
812
+ sign: normalizedSign,
813
+ value: parsedValue,
814
+ normalizedSegment,
815
+ originalSegment: match[0]
816
+ };
817
+ }
818
+ function countExplodingSuccesses(diceRoll, sign, value) {
819
+ const rollsArray = Array.isArray(diceRoll) ? diceRoll : [diceRoll];
820
+ const flatValues = [];
821
+ for (const dr of rollsArray) {
822
+ const groups = dr.rolls ?? [];
823
+ for (const group of groups) {
824
+ const innerRolls = group.rolls ?? [];
825
+ for (const roll2 of innerRolls) {
826
+ if (typeof roll2.value === "number") flatValues.push(roll2.value);
827
+ }
828
+ }
829
+ }
830
+ return flatValues.reduce(
831
+ (acc, current) => acc + (matchComparison(sign, current, value) ? 1 : 0),
832
+ 0
833
+ );
834
+ }
835
+
836
+ // src/dice/extract.ts
837
+ var import_rpg_dice_roller6 = require("@dice-roller/rpg-dice-roller");
838
+
839
+ // src/similarities/generateStatsDice.ts
840
+ var import_mathjs5 = require("mathjs");
841
+
842
+ // src/similarities/similarity.ts
843
+ function calculateSimilarity(str1, str2) {
844
+ const longer = str1.length > str2.length ? str1 : str2;
845
+ const shorter = str1.length > str2.length ? str2 : str1;
846
+ if (longer.length === 0) return 1;
847
+ const distance = levenshteinDistance(longer, shorter);
848
+ return (longer.length - distance) / longer.length;
849
+ }
850
+ function levenshteinDistance(str1, str2) {
851
+ const matrix = Array(str2.length + 1).fill(null).map(() => Array(str1.length + 1).fill(null));
852
+ for (let i = 0; i <= str1.length; i++) matrix[0][i] = i;
853
+ for (let j = 0; j <= str2.length; j++) matrix[j][0] = j;
854
+ for (let j = 1; j <= str2.length; j++) {
855
+ for (let i = 1; i <= str1.length; i++) {
856
+ const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;
857
+ matrix[j][i] = Math.min(
858
+ matrix[j][i - 1] + 1,
859
+ // insertion
860
+ matrix[j - 1][i] + 1,
861
+ // deletion
862
+ matrix[j - 1][i - 1] + cost
863
+ // substitution
864
+ );
865
+ }
866
+ }
867
+ return matrix[str2.length][str1.length];
868
+ }
869
+ function findBestStatMatch(searchTerm, normalizedStats, similarityThreshold = MIN_THRESHOLD_MATCH) {
870
+ const exact = normalizedStats.get(searchTerm);
871
+ if (exact) return exact;
872
+ const candidates = [];
873
+ for (const [normalizedKey, original] of normalizedStats) {
874
+ if (normalizedKey.startsWith(searchTerm))
875
+ candidates.push([original, calculateSimilarity(searchTerm, normalizedKey)]);
876
+ }
877
+ if (candidates.length === 1) return candidates[0][0];
878
+ if (candidates.length > 0) {
879
+ candidates.sort((a, b) => b[1] - a[1]);
880
+ if (candidates[0][1] >= similarityThreshold) return candidates[0][0];
881
+ }
882
+ let bestMatch;
883
+ let bestSimilarity = 0;
884
+ for (const [normalizedKey, original] of normalizedStats) {
885
+ const similarity = calculateSimilarity(searchTerm, normalizedKey);
886
+ if (similarity === 1) return original;
887
+ if (similarity > bestSimilarity && similarity >= similarityThreshold) {
888
+ bestSimilarity = similarity;
889
+ bestMatch = original;
890
+ }
891
+ }
892
+ return bestMatch;
893
+ }
894
+ function findBestRecord(record, searchTerm, similarityThreshold = MIN_THRESHOLD_MATCH) {
895
+ const normalizeRecord = /* @__PURE__ */ new Map();
896
+ for (const key of Object.keys(record)) {
897
+ normalizeRecord.set(key.standardize(), key);
898
+ }
899
+ return findBestStatMatch(
900
+ searchTerm.standardize(),
901
+ normalizeRecord,
902
+ similarityThreshold
903
+ ) || null;
904
+ }
905
+ function replaceUnknown(dice, replacer) {
906
+ return dice.replaceAll(REMOVER_PATTERN.STAT_MATCHER, replacer).replaceAll("+0", "").replaceAll("-0", "");
907
+ }
908
+ function verifyStatMatcherPattern(dice, replaceUnknow) {
909
+ if (REMOVER_PATTERN.STAT_MATCHER.test(dice)) {
910
+ if (replaceUnknow)
911
+ return replaceUnknown(dice, replaceUnknow);
912
+ const matched = dice.matchAll(new RegExp(REMOVER_PATTERN.STAT_MATCHER));
913
+ const stats = matched ? Array.from(matched, (m) => m?.[0]).map((s) => `\`${s}\``).join(", ") : "unknown";
914
+ throw new DiceTypeError(stats, "unknown_stats");
915
+ }
916
+ return dice.replaceAll("+0", "").replaceAll("-0", "");
917
+ }
918
+
919
+ // src/similarities/generateStatsDice.ts
920
+ function handleDiceAfterD(tokenStd, normalizedStats) {
921
+ const diceMatch = /^(\d*)d(.+)$/i.exec(tokenStd);
922
+ if (!diceMatch) return null;
923
+ const diceCount = diceMatch[1] || "";
924
+ const afterD = diceMatch[2];
925
+ const bestMatch = findBestStatMatch(afterD, normalizedStats, 1);
926
+ if (bestMatch) {
927
+ const [, value] = bestMatch;
928
+ return `${diceCount}d${value.toString()}`;
929
+ }
930
+ return null;
931
+ }
932
+ function handleSimpleToken(tokenStd, token, normalizedStats, minThreshold) {
933
+ const bestMatch = findBestStatMatch(tokenStd, normalizedStats, minThreshold);
934
+ if (bestMatch) {
935
+ const [, value] = bestMatch;
936
+ return value.toString();
937
+ }
938
+ return token;
939
+ }
940
+ function generateStatsDice(originalDice, stats, minThreshold = 0.6, dollarValue) {
941
+ let dice = originalDice.standardize();
942
+ if (stats && Object.keys(stats).length > 0) {
943
+ const normalizedStats = /* @__PURE__ */ new Map();
944
+ for (const [key, value] of Object.entries(stats)) {
945
+ const normalized = key.standardize();
946
+ normalizedStats.set(normalized, [key, value]);
947
+ }
948
+ const partsRegex = /(\[[^\]]+])|([^[]+)/g;
949
+ let result = "";
950
+ let match;
951
+ while ((match = partsRegex.exec(dice)) !== null) {
952
+ const insideBrackets = match[1];
953
+ const outsideText = match[2];
954
+ if (insideBrackets) {
955
+ result += insideBrackets;
956
+ continue;
957
+ }
958
+ if (!outsideText) {
959
+ continue;
960
+ }
961
+ const tokenRegex = /(\$?[\p{L}\p{N}_.]+)/gu;
962
+ let lastIndex = 0;
963
+ let tokenMatch;
964
+ while ((tokenMatch = tokenRegex.exec(outsideText)) !== null) {
965
+ result += outsideText.slice(lastIndex, tokenMatch.index);
966
+ const token = tokenMatch[0];
967
+ const tokenHasDollar = token.startsWith("$");
968
+ const tokenForCompare = tokenHasDollar ? token.slice(1) : token;
969
+ const tokenStd = tokenForCompare.standardize();
970
+ const diceReplacement = handleDiceAfterD(tokenStd, normalizedStats);
971
+ if (diceReplacement) {
972
+ result += diceReplacement;
973
+ lastIndex = tokenRegex.lastIndex;
974
+ continue;
975
+ }
976
+ result += handleSimpleToken(tokenStd, token, normalizedStats, minThreshold);
977
+ lastIndex = tokenRegex.lastIndex;
978
+ }
979
+ result += outsideText.slice(lastIndex);
980
+ }
981
+ dice = result;
982
+ }
983
+ if (dollarValue) dice = dice.replaceAll("$", dollarValue);
984
+ return replaceFormulaInDice(dice);
985
+ }
986
+ function replaceFormulaInDice(dice) {
987
+ const formula = /(?<formula>\{{2}(.+?)}{2})/gim;
988
+ let match;
989
+ let modifiedDice = dice;
990
+ while ((match = formula.exec(dice)) !== null) {
991
+ if (match.groups?.formula) {
992
+ const formulae = match.groups.formula.replaceAll("{{", "").replaceAll("}}", "");
993
+ try {
994
+ const result = (0, import_mathjs5.evaluate)(formulae);
995
+ modifiedDice = modifiedDice.replace(match.groups.formula, result.toString());
996
+ } catch (error) {
997
+ throw new FormulaError(match.groups.formula, "replaceFormulasInDice", error);
998
+ }
999
+ }
1000
+ }
1001
+ return cleanedDice(modifiedDice);
1002
+ }
1003
+ function cleanedDice(dice) {
1004
+ return dice.replaceAll("+-", "-").replaceAll("--", "+").replaceAll("++", "+").replaceAll("=>", ">=").replaceAll("=<", "<=").trimEnd();
1005
+ }
1006
+
1007
+ // src/similarities/resolveFormula.ts
1008
+ var import_mathjs6 = require("mathjs");
1009
+ function toFiniteNumber(value) {
1010
+ if (typeof value === "number" && Number.isFinite(value)) return value;
1011
+ return void 0;
1012
+ }
1013
+ function substituteFormulaTokens(expr, resolvedStats, similarityThreshold = MIN_THRESHOLD_MATCH) {
1014
+ return expr.replace(/([\p{L}\p{M}._-]+)/gu, (token) => {
1015
+ const match = findBestStatMatch(token, resolvedStats, similarityThreshold);
1016
+ return match !== void 0 ? match.toString() : token;
1017
+ });
1018
+ }
1019
+ function resolveFormulaHint(formula, allAttributes, similarityThreshold = MIN_THRESHOLD_MATCH) {
1020
+ const trimmed = formula.trim();
1021
+ if (!trimmed) return { kind: "not-formula" };
1022
+ if (isNumber(trimmed)) return { kind: "not-formula" };
1023
+ const resolved = /* @__PURE__ */ new Map();
1024
+ const pending = /* @__PURE__ */ new Map();
1025
+ for (const [name, val] of Object.entries(allAttributes)) {
1026
+ const norm = name.standardize();
1027
+ if (typeof val === "number") {
1028
+ if (Number.isFinite(val)) resolved.set(norm, val);
1029
+ continue;
1030
+ }
1031
+ const t = val.trim();
1032
+ if (!t) continue;
1033
+ if (isNumber(t)) {
1034
+ const numeric = Number(t);
1035
+ if (Number.isFinite(numeric)) resolved.set(norm, numeric);
1036
+ } else {
1037
+ pending.set(norm, t.standardize());
1038
+ }
1039
+ }
1040
+ for (const [normName, expr2] of pending) {
1041
+ pending.set(normName, substituteFormulaTokens(expr2, resolved, similarityThreshold));
1042
+ }
1043
+ let progress = true;
1044
+ while (pending.size > 0 && progress) {
1045
+ progress = false;
1046
+ for (const [normName, expr2] of pending) {
1047
+ try {
1048
+ const result = toFiniteNumber((0, import_mathjs6.evaluate)(expr2));
1049
+ if (result === void 0) continue;
1050
+ resolved.set(normName, result);
1051
+ pending.delete(normName);
1052
+ progress = true;
1053
+ for (const [otherNorm, otherExpr] of pending) {
1054
+ pending.set(
1055
+ otherNorm,
1056
+ substituteFormulaTokens(otherExpr, resolved, similarityThreshold)
1057
+ );
1058
+ }
1059
+ } catch {
773
1060
  }
774
1061
  }
775
1062
  }
776
- return flatValues.reduce(
777
- (acc, current) => acc + (matchComparison(sign, current, value) ? 1 : 0),
778
- 0
779
- );
1063
+ const normFormula = trimmed.standardize();
1064
+ const expr = substituteFormulaTokens(normFormula, resolved, similarityThreshold);
1065
+ try {
1066
+ const result = toFiniteNumber((0, import_mathjs6.evaluate)(expr));
1067
+ if (result !== void 0) return { kind: "resolved", value: result };
1068
+ return { kind: "error" };
1069
+ } catch {
1070
+ return { kind: "error" };
1071
+ }
780
1072
  }
781
1073
 
782
- // src/dice/extract.ts
783
- var import_rpg_dice_roller5 = require("@dice-roller/rpg-dice-roller");
784
-
785
1074
  // src/dice/calculator.ts
786
- var import_mathjs5 = require("mathjs");
1075
+ var import_mathjs7 = require("mathjs");
787
1076
  function calculator(sign, value, total) {
788
1077
  if (sign === "^") sign = "**";
789
- return (0, import_mathjs5.evaluate)(`${total} ${sign} ${value}`);
1078
+ return (0, import_mathjs7.evaluate)(`${total} ${sign} ${value}`);
790
1079
  }
791
1080
 
792
1081
  // src/dice/extract.ts
@@ -821,10 +1110,10 @@ function extractValuesFromOutput(output) {
821
1110
  }
822
1111
  return values;
823
1112
  }
824
- function getRollBounds(dice, engine = import_rpg_dice_roller5.NumberGenerator.engines.nodeCrypto) {
1113
+ function getRollBounds(dice, engine = import_rpg_dice_roller6.NumberGenerator.engines.nodeCrypto) {
825
1114
  try {
826
- const roller = new import_rpg_dice_roller5.DiceRoller();
827
- import_rpg_dice_roller5.NumberGenerator.generator.engine = engine;
1115
+ const roller = new import_rpg_dice_roller6.DiceRoller();
1116
+ import_rpg_dice_roller6.NumberGenerator.generator.engine = engine;
828
1117
  const rollResult = roller.roll(dice);
829
1118
  const instance = Array.isArray(rollResult) ? rollResult[0] : rollResult;
830
1119
  const { minTotal, maxTotal } = instance;
@@ -849,7 +1138,7 @@ function setSortOrder(toRoll, sort) {
849
1138
  }
850
1139
  function prepareDice(diceInput) {
851
1140
  let dice = standardizeDice(replaceFormulaInDice(diceInput)).replace(/^\+/, "").replaceAll("=>", ">=").replaceAll("=<", "<=").trimStart();
852
- dice = dice.replaceAll(DETECT_CRITICAL, "").trimEnd();
1141
+ dice = dice.replaceAll(REMOVER_PATTERN.CRITICAL_BLOCK, "").trimEnd();
853
1142
  const explodingSuccess = normalizeExplodingSuccess(dice);
854
1143
  if (explodingSuccess) dice = explodingSuccess.dice;
855
1144
  let diceDisplay;
@@ -901,9 +1190,8 @@ function handleBulkRolls(dice, isCurlyBulk, bulkContent, compare, explodingSucce
901
1190
  const bulkProcessContent = isCurlyBulk ? bulkContent : dice;
902
1191
  const diceArray = bulkProcessContent.split("#");
903
1192
  const numberOfDice = Number.parseInt(diceArray[0], 10);
904
- let diceToRoll = diceArray[1].replace(COMMENT_REGEX, "");
905
- const commentsMatch = diceArray[1].match(COMMENT_REGEX);
906
- const comments = commentsMatch ? commentsMatch[2] : void 0;
1193
+ const { dice: diceToRollBase, comment: comments } = splitDiceComment(diceArray[1]);
1194
+ let diceToRoll = diceToRollBase;
907
1195
  let curlyCompare;
908
1196
  if (isCurlyBulk) {
909
1197
  const curlyCompareRegex = diceToRoll.match(SIGN_REGEX_SPACE);
@@ -937,8 +1225,8 @@ function handleBulkRolls(dice, isCurlyBulk, bulkContent, compare, explodingSucce
937
1225
  sort
938
1226
  );
939
1227
  }
940
- const roller = new import_rpg_dice_roller6.DiceRoller();
941
- import_rpg_dice_roller6.NumberGenerator.generator.engine = engine;
1228
+ const roller = new import_rpg_dice_roller7.DiceRoller();
1229
+ import_rpg_dice_roller7.NumberGenerator.generator.engine = engine;
942
1230
  for (let i = 0; i < numberOfDice; i++) {
943
1231
  try {
944
1232
  roller.roll(diceToRoll);
@@ -960,8 +1248,8 @@ function handleBulkRolls(dice, isCurlyBulk, bulkContent, compare, explodingSucce
960
1248
  function handleBulkRollsWithComparison(numberOfDice, diceToRoll, comments, activeCompare, explodingSuccess, diceDisplay, isCurlyBulk, curlyCompare, compare, engine, sort) {
961
1249
  const results = [];
962
1250
  let successCount = 0;
963
- const roller = new import_rpg_dice_roller6.DiceRoller();
964
- import_rpg_dice_roller6.NumberGenerator.generator.engine = engine;
1251
+ const roller = new import_rpg_dice_roller7.DiceRoller();
1252
+ import_rpg_dice_roller7.NumberGenerator.generator.engine = engine;
965
1253
  let trivialComparisonDetected = false;
966
1254
  const formatOutput = (output, addStar) => {
967
1255
  const formatted = addStar && isCurlyBulk ? output.replace(
@@ -995,7 +1283,7 @@ function handleBulkRollsWithComparison(numberOfDice, diceToRoll, comments, activ
995
1283
  results.push(formattedRollOutput);
996
1284
  } else {
997
1285
  const rollTotal = rollInstance.total;
998
- const isSuccess = (0, import_mathjs6.evaluate)(
1286
+ const isSuccess = (0, import_mathjs8.evaluate)(
999
1287
  `${rollTotal}${activeCompare.sign}${activeCompare.value}`
1000
1288
  );
1001
1289
  if (isSuccess) successCount++;
@@ -1058,7 +1346,7 @@ function handleBulkRollsWithComparison(numberOfDice, diceToRoll, comments, activ
1058
1346
  }
1059
1347
 
1060
1348
  // src/dice/pity.ts
1061
- var import_mathjs7 = require("mathjs");
1349
+ var import_mathjs9 = require("mathjs");
1062
1350
  function handlePitySystem(dice, compare, diceRoll, roller, engine) {
1063
1351
  const currentRoll = Array.isArray(diceRoll) ? diceRoll[0] : diceRoll;
1064
1352
  const maxPossible = currentRoll ? currentRoll.maxTotal : null;
@@ -1066,7 +1354,7 @@ function handlePitySystem(dice, compare, diceRoll, roller, engine) {
1066
1354
  if (!isComparisonPossible) {
1067
1355
  return { rerollCount: 0 };
1068
1356
  }
1069
- let isFail = (0, import_mathjs7.evaluate)(`${roller.total}${compare.sign}${compare.value}`);
1357
+ let isFail = (0, import_mathjs9.evaluate)(`${roller.total}${compare.sign}${compare.value}`);
1070
1358
  if (isFail) {
1071
1359
  return { rerollCount: 0 };
1072
1360
  }
@@ -1081,14 +1369,14 @@ function handlePitySystem(dice, compare, diceRoll, roller, engine) {
1081
1369
  }
1082
1370
  rerollCount++;
1083
1371
  if (res && res.total !== void 0) {
1084
- isFail = (0, import_mathjs7.evaluate)(`${res.total}${compare.sign}${compare.value}`);
1372
+ isFail = (0, import_mathjs9.evaluate)(`${res.total}${compare.sign}${compare.value}`);
1085
1373
  }
1086
1374
  }
1087
1375
  return { rerollCount, result: res };
1088
1376
  }
1089
1377
 
1090
1378
  // src/roll.ts
1091
- function roll(dice, engine = import_rpg_dice_roller7.NumberGenerator.engines.nodeCrypto, pity, sort) {
1379
+ function roll(dice, engine = import_rpg_dice_roller8.NumberGenerator.engines.nodeCrypto, pity, sort, comment) {
1092
1380
  if (sort === "none" /* None */) sort = void 0;
1093
1381
  const prepared = prepareDice(dice);
1094
1382
  if (!prepared.dice.includes("d")) return void 0;
@@ -1129,10 +1417,12 @@ function roll(dice, engine = import_rpg_dice_roller7.NumberGenerator.engines.nod
1129
1417
  sort
1130
1418
  );
1131
1419
  }
1132
- const roller = new import_rpg_dice_roller7.DiceRoller();
1133
- import_rpg_dice_roller7.NumberGenerator.generator.engine = engine;
1134
- let diceWithoutComment = processedDice.replace(COMMENT_REGEX, "").trimEnd();
1135
- diceWithoutComment = setSortOrder(diceWithoutComment, sort);
1420
+ const roller = new import_rpg_dice_roller8.DiceRoller();
1421
+ import_rpg_dice_roller8.NumberGenerator.generator.engine = engine;
1422
+ const splitResult = splitDiceComment(processedDice);
1423
+ const diceBase = comment !== void 0 ? processedDice.trimEnd() : splitResult.dice;
1424
+ const resolvedComment = comment ?? splitResult.comment;
1425
+ const diceWithoutComment = setSortOrder(diceBase, sort);
1136
1426
  let diceRoll;
1137
1427
  try {
1138
1428
  diceRoll = roller.roll(diceWithoutComment);
@@ -1148,8 +1438,6 @@ function roll(dice, engine = import_rpg_dice_roller7.NumberGenerator.engines.nod
1148
1438
  );
1149
1439
  compare.trivial = trivial ? true : void 0;
1150
1440
  }
1151
- const commentMatch = processedDice.match(COMMENT_REGEX);
1152
- const comment = commentMatch ? commentMatch[2] : void 0;
1153
1441
  let rerollCount = 0;
1154
1442
  let pityResult;
1155
1443
  if (pity && compare) {
@@ -1166,7 +1454,7 @@ function roll(dice, engine = import_rpg_dice_roller7.NumberGenerator.engines.nod
1166
1454
  return {
1167
1455
  ...pityResult,
1168
1456
  dice: prepared.isSimpleCurly ? finalDiceDisplay : processedDice,
1169
- comment,
1457
+ comment: resolvedComment,
1170
1458
  compare,
1171
1459
  modifier: modificator,
1172
1460
  pityLogs: rerollCount,
@@ -1188,7 +1476,7 @@ function roll(dice, engine = import_rpg_dice_roller7.NumberGenerator.engines.nod
1188
1476
  return {
1189
1477
  dice: prepared.isSimpleCurly ? finalDiceDisplay : prepared.diceDisplay,
1190
1478
  result: resultOutput,
1191
- comment,
1479
+ comment: resolvedComment,
1192
1480
  compare: compare ? compare : void 0,
1193
1481
  modifier: modificator,
1194
1482
  total: successes,
@@ -1199,7 +1487,7 @@ function roll(dice, engine = import_rpg_dice_roller7.NumberGenerator.engines.nod
1199
1487
  return {
1200
1488
  dice: prepared.isSimpleCurly ? finalDiceDisplay : processedDice,
1201
1489
  result: resultOutput,
1202
- comment,
1490
+ comment: resolvedComment,
1203
1491
  compare: compare ? compare : void 0,
1204
1492
  modifier: modificator,
1205
1493
  total: roller.total,
@@ -1207,7 +1495,7 @@ function roll(dice, engine = import_rpg_dice_roller7.NumberGenerator.engines.nod
1207
1495
  trivial: compare?.trivial ? true : void 0
1208
1496
  };
1209
1497
  }
1210
- function sharedRolls(dice, engine = import_rpg_dice_roller7.NumberGenerator.engines.nodeCrypto, pity, explodingSuccessMain, diceDisplay, isSharedCurly, sort) {
1498
+ function sharedRolls(dice, engine = import_rpg_dice_roller8.NumberGenerator.engines.nodeCrypto, pity, explodingSuccessMain, diceDisplay, isSharedCurly, sort) {
1211
1499
  if (!explodingSuccessMain)
1212
1500
  explodingSuccessMain = normalizeExplodingSuccess(dice.split(";")[0] ?? dice);
1213
1501
  if (explodingSuccessMain) {
@@ -1242,13 +1530,13 @@ function sharedRolls(dice, engine = import_rpg_dice_roller7.NumberGenerator.engi
1242
1530
  const sortFromMain = getSortOrder(diceMain);
1243
1531
  const rollBounds = getRollBounds(diceMain, engine);
1244
1532
  let diceResult = roll(diceMain, engine, pity, sort);
1245
- if (!diceResult || !diceResult.total) {
1533
+ if (!diceResult?.total) {
1246
1534
  if (hidden) {
1247
1535
  diceResult = roll(fixParenthesis(split[0]), engine, pity, sort);
1248
1536
  hidden = false;
1249
1537
  } else return void 0;
1250
1538
  }
1251
- if (!diceResult || !diceResult.total) return void 0;
1539
+ if (!diceResult?.total) return void 0;
1252
1540
  if (explodingSuccessMain && diceResult.result) {
1253
1541
  const values = extractValuesFromOutput(diceResult.result);
1254
1542
  diceResult.total = values.filter(
@@ -1290,7 +1578,7 @@ function sharedRolls(dice, engine = import_rpg_dice_roller7.NumberGenerator.engi
1290
1578
  const { diceAll } = replaceText(element, diceResult.total, diceResult.dice);
1291
1579
  let successCount = 0;
1292
1580
  try {
1293
- const evaluated = (0, import_mathjs8.evaluate)(toRoll);
1581
+ const evaluated = (0, import_mathjs10.evaluate)(toRoll);
1294
1582
  successCount = evaluated ? 1 : 0;
1295
1583
  } catch (error) {
1296
1584
  const evaluated = roll(toRoll, engine, pity);
@@ -1324,7 +1612,7 @@ function sharedRolls(dice, engine = import_rpg_dice_roller7.NumberGenerator.engi
1324
1612
  diceResult.dice
1325
1613
  );
1326
1614
  try {
1327
- const evaluated = (0, import_mathjs8.evaluate)(toRoll);
1615
+ const evaluated = (0, import_mathjs10.evaluate)(toRoll);
1328
1616
  results.push(`\u25C8 ${comment}${diceAll}: ${formule} = ${evaluated}`);
1329
1617
  total += Number.parseInt(evaluated, 10);
1330
1618
  } catch (error) {
@@ -1353,7 +1641,7 @@ function sharedRolls(dice, engine = import_rpg_dice_roller7.NumberGenerator.engi
1353
1641
  trivial: hasTrivialComparison ? true : void 0
1354
1642
  };
1355
1643
  }
1356
- function replaceInFormula(element, diceResult, compareResult, res, engine = import_rpg_dice_roller7.NumberGenerator.engines.nodeCrypto, pity) {
1644
+ function replaceInFormula(element, diceResult, compareResult, res, engine = import_rpg_dice_roller8.NumberGenerator.engines.nodeCrypto, pity) {
1357
1645
  const { formule, diceAll } = replaceText(
1358
1646
  element,
1359
1647
  diceResult.total ?? 0,
@@ -1363,7 +1651,7 @@ function replaceInFormula(element, diceResult, compareResult, res, engine = impo
1363
1651
  const invertedSign = res ? compareResult.compare.sign : inverseSign(compareResult.compare.sign);
1364
1652
  let evaluateRoll;
1365
1653
  try {
1366
- evaluateRoll = (0, import_mathjs8.evaluate)(compareResult.dice);
1654
+ evaluateRoll = (0, import_mathjs10.evaluate)(compareResult.dice);
1367
1655
  return `${validSign} ${diceAll}: ${formule} = ${evaluateRoll}${invertedSign}${compareResult.compare?.value}`;
1368
1656
  } catch (error) {
1369
1657
  const evaluateRoll2 = roll(compareResult.dice, engine, pity);
@@ -1372,218 +1660,6 @@ function replaceInFormula(element, diceResult, compareResult, res, engine = impo
1372
1660
  return `${validSign} ${diceAll}: ${formule} = ${evaluateRoll2}${invertedSign}${compareResult.compare?.value}`;
1373
1661
  }
1374
1662
  }
1375
-
1376
- // src/verify_template.ts
1377
- var import_mathjs9 = require("mathjs");
1378
- var import_random_js2 = require("random-js");
1379
- var import_uniformize2 = require("uniformize");
1380
- var import_rpg_dice_roller8 = require("@dice-roller/rpg-dice-roller");
1381
- function evalStatsDice(testDice, allStats, engine = import_rpg_dice_roller8.NumberGenerator.engines.nodeCrypto, pity) {
1382
- let dice = testDice.trimEnd();
1383
- if (allStats && Object.keys(allStats).length > 0) {
1384
- const names = Object.keys(allStats);
1385
- for (const name of names) {
1386
- const regex = new RegExp(escapeRegex(name.standardize()), "gi");
1387
- if (dice.standardize().match(regex)) {
1388
- const statValue = allStats[name];
1389
- dice = dice.standardize().replace(regex, statValue.toString()).trimEnd();
1390
- }
1391
- }
1392
- }
1393
- try {
1394
- if (!roll(replaceFormulaInDice(replaceExpByRandom(dice)), engine, pity))
1395
- throw new DiceTypeError(dice, "evalStatsDice", "no roll result");
1396
- return testDice;
1397
- } catch (error) {
1398
- throw new DiceTypeError(dice, "evalStatsDice", error);
1399
- }
1400
- }
1401
- function diceRandomParse(value, template, engine = import_rpg_dice_roller8.NumberGenerator.engines.nodeCrypto) {
1402
- if (!template.statistics) return replaceFormulaInDice(value.standardize());
1403
- value = value.standardize();
1404
- const statNames = Object.keys(template.statistics);
1405
- let newDice = value;
1406
- for (const name of statNames) {
1407
- const regex = new RegExp(escapeRegex(name.standardize()), "gi");
1408
- if (value.match(regex)) {
1409
- let max;
1410
- let min;
1411
- const foundStat = template.statistics?.[name];
1412
- if (foundStat) {
1413
- max = foundStat.max;
1414
- min = foundStat.min;
1415
- }
1416
- const total = template.total || 100;
1417
- const randomStatValue = generateRandomStat(total, max, min, engine);
1418
- newDice = value.replace(regex, randomStatValue.toString());
1419
- }
1420
- }
1421
- return replaceFormulaInDice(newDice);
1422
- }
1423
- function diceTypeRandomParse(dice, template, engine = import_rpg_dice_roller8.NumberGenerator.engines.nodeCrypto) {
1424
- dice = replaceExpByRandom(dice);
1425
- if (!template.statistics) return dice;
1426
- const firstStatNotcombinaison = Object.keys(template.statistics).find(
1427
- (stat) => !template.statistics?.[stat].combinaison
1428
- );
1429
- if (!firstStatNotcombinaison) return dice;
1430
- const stats = template.statistics[firstStatNotcombinaison];
1431
- const { min, max } = stats;
1432
- const total = template.total || 100;
1433
- const randomStatValue = generateRandomStat(total, max, min, engine);
1434
- return replaceFormulaInDice(dice.replaceAll("$", randomStatValue.toString()));
1435
- }
1436
- function evalCombinaison(combinaison, stats) {
1437
- const newStats = {};
1438
- for (const [stat, combin] of Object.entries(combinaison)) {
1439
- let formula = combin.standardize();
1440
- for (const [statName, value] of Object.entries(stats)) {
1441
- const regex = new RegExp(statName.standardize(), "gi");
1442
- formula = formula.replace(regex, value.toString());
1443
- }
1444
- try {
1445
- newStats[stat] = (0, import_mathjs9.evaluate)(formula);
1446
- } catch (error) {
1447
- throw new FormulaError(stat, "evalCombinaison", error);
1448
- }
1449
- }
1450
- return newStats;
1451
- }
1452
- function evalOneCombinaison(combinaison, stats) {
1453
- let formula = combinaison.standardize();
1454
- for (const [statName, value] of Object.entries(stats)) {
1455
- const regex = new RegExp(statName.standardize(), "gi");
1456
- formula = formula.replace(regex, value.toString());
1457
- }
1458
- try {
1459
- return (0, import_mathjs9.evaluate)(formula);
1460
- } catch (error) {
1461
- throw new FormulaError(combinaison, "evalOneCombinaison", error);
1462
- }
1463
- }
1464
- function convertNumber(number) {
1465
- if (number === void 0 || number === null) return void 0;
1466
- if (number.toString().length === 0 || Number.isNaN(Number.parseInt(number.toString(), 10)))
1467
- return void 0;
1468
- if (isNumber(number)) return Number.parseInt(number.toString(), 10);
1469
- return void 0;
1470
- }
1471
- function verifyTemplateValue(template, verify = true, engine = import_rpg_dice_roller8.NumberGenerator.engines.nodeCrypto) {
1472
- const parsedTemplate = templateSchema.parse(template);
1473
- const { success, failure } = parsedTemplate.critical ?? {};
1474
- const criticicalVal = {
1475
- success: convertNumber(success),
1476
- failure: convertNumber(failure)
1477
- };
1478
- const statistiqueTemplate = {
1479
- diceType: parsedTemplate.diceType,
1480
- statistics: parsedTemplate.statistics,
1481
- critical: criticicalVal,
1482
- total: parsedTemplate.total,
1483
- charName: parsedTemplate.charName,
1484
- damage: parsedTemplate.damage,
1485
- customCritical: parsedTemplate.customCritical,
1486
- forceDistrib: parsedTemplate.forceDistrib
1487
- };
1488
- if (!verify) return statistiqueTemplate;
1489
- if (statistiqueTemplate.diceType) {
1490
- if (statistiqueTemplate.diceType.match(DETECT_CRITICAL)) {
1491
- throw new DiceTypeError(
1492
- statistiqueTemplate.diceType,
1493
- "critical_dice_type",
1494
- "contains critical detection: should be in custom critical instead"
1495
- );
1496
- }
1497
- const cleanedDice2 = diceTypeRandomParse(
1498
- statistiqueTemplate.diceType,
1499
- statistiqueTemplate,
1500
- engine
1501
- );
1502
- const rolled = roll(cleanedDice2, engine);
1503
- if (!rolled) throw new DiceTypeError(cleanedDice2, "no_roll_result", "no roll result");
1504
- }
1505
- if (statistiqueTemplate.customCritical) {
1506
- if (!statistiqueTemplate.diceType) {
1507
- throw new DiceTypeError("no_dice_type", "no_dice_type", "no dice type");
1508
- }
1509
- const customCritical = statistiqueTemplate.customCritical;
1510
- for (const [, custom] of Object.entries(customCritical)) {
1511
- const cleanedDice2 = createCriticalCustom(
1512
- statistiqueTemplate.diceType,
1513
- custom,
1514
- statistiqueTemplate,
1515
- engine
1516
- );
1517
- const rolled = roll(cleanedDice2, engine);
1518
- if (!rolled)
1519
- throw new DiceTypeError(cleanedDice2, "verifyTemplateValue", "no roll result");
1520
- }
1521
- }
1522
- testDiceRegistered(statistiqueTemplate, engine);
1523
- testStatCombinaison(statistiqueTemplate, engine);
1524
- return statistiqueTemplate;
1525
- }
1526
- function testDiceRegistered(template, engine = import_rpg_dice_roller8.NumberGenerator.engines.nodeCrypto) {
1527
- if (!template.damage) return;
1528
- if (Object.keys(template.damage).length === 0) throw new EmptyObjectError();
1529
- if (Object.keys(template.damage).length > 25) throw new TooManyDice();
1530
- for (const [name, dice] of Object.entries(template.damage)) {
1531
- if (!dice) continue;
1532
- const diceReplaced = replaceExpByRandom(dice);
1533
- const randomDiceParsed = diceRandomParse(diceReplaced, template, engine);
1534
- try {
1535
- const rolled = roll(randomDiceParsed, engine);
1536
- if (!rolled) throw new DiceTypeError(name, "no_roll_result", dice);
1537
- } catch (error) {
1538
- throw new DiceTypeError(name, "testDiceRegistered", error);
1539
- }
1540
- }
1541
- }
1542
- function testStatCombinaison(template, engine = import_rpg_dice_roller8.NumberGenerator.engines.nodeCrypto) {
1543
- if (!template.statistics) return;
1544
- const onlycombinaisonStats = Object.fromEntries(
1545
- Object.entries(template.statistics).filter(
1546
- ([_, value]) => value.combinaison !== void 0
1547
- )
1548
- );
1549
- const allOtherStats = Object.fromEntries(
1550
- Object.entries(template.statistics).filter(([_, value]) => !value.combinaison)
1551
- );
1552
- if (Object.keys(onlycombinaisonStats).length === 0) return;
1553
- const allStats = Object.keys(template.statistics).filter(
1554
- (stat) => !template.statistics[stat].combinaison
1555
- );
1556
- if (allStats.length === 0) throw new NoStatisticsError();
1557
- const error = [];
1558
- for (const [stat, value] of Object.entries(onlycombinaisonStats)) {
1559
- let formula = value.combinaison;
1560
- for (const [other, data] of Object.entries(allOtherStats)) {
1561
- const { max, min } = data;
1562
- const total = template.total || 100;
1563
- const randomStatValue = generateRandomStat(total, max, min, engine);
1564
- const regex = new RegExp(other, "gi");
1565
- formula = formula.replace(regex, randomStatValue.toString());
1566
- }
1567
- try {
1568
- (0, import_mathjs9.evaluate)(formula);
1569
- } catch (e) {
1570
- error.push(stat);
1571
- }
1572
- }
1573
- if (error.length > 0) throw new FormulaError(error.join(", "), "testStatCombinaison");
1574
- return;
1575
- }
1576
- function generateRandomStat(total = 100, max, min, engine = import_rpg_dice_roller8.NumberGenerator.engines.nodeCrypto) {
1577
- let randomStatValue = total + 1;
1578
- const random = new import_random_js2.Random(engine || import_rpg_dice_roller8.NumberGenerator.engines.nodeCrypto);
1579
- while (randomStatValue >= total || randomStatValue === 0) {
1580
- if (max && min) randomStatValue = randomInt(min, max, engine, random);
1581
- else if (max) randomStatValue = randomInt(1, max, engine, random);
1582
- else if (min) randomStatValue = randomInt(min, total, engine, random);
1583
- else randomStatValue = randomInt(1, total, engine, random);
1584
- }
1585
- return randomStatValue;
1586
- }
1587
1663
  // Annotate the CommonJS export names for ESM import in node:
1588
1664
  0 && (module.exports = {
1589
1665
  COMMENT_REGEX,
@@ -1626,7 +1702,9 @@ function generateRandomStat(total = 100, max, min, engine = import_rpg_dice_roll
1626
1702
  replaceFormulaInDice,
1627
1703
  replaceInFormula,
1628
1704
  replaceUnknown,
1705
+ resolveFormulaHint,
1629
1706
  roll,
1707
+ splitDiceComment,
1630
1708
  standardizeDice,
1631
1709
  templateSchema,
1632
1710
  testDiceRegistered,