@dicelette/core 1.28.0 → 1.28.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -60,6 +60,7 @@ __export(index_exports, {
60
60
  replaceFormulaInDice: () => replaceFormulaInDice,
61
61
  replaceInFormula: () => replaceInFormula,
62
62
  replaceUnknown: () => replaceUnknown,
63
+ resolveFormulaHint: () => resolveFormulaHint,
63
64
  roll: () => roll,
64
65
  standardizeDice: () => standardizeDice,
65
66
  templateSchema: () => templateSchema,
@@ -277,211 +278,259 @@ function includeDiceType(dice, diceType, userStats) {
277
278
  }
278
279
 
279
280
  // src/roll.ts
280
- var import_rpg_dice_roller7 = require("@dice-roller/rpg-dice-roller");
281
- var import_mathjs8 = require("mathjs");
281
+ var import_rpg_dice_roller8 = require("@dice-roller/rpg-dice-roller");
282
+ var import_mathjs10 = require("mathjs");
282
283
 
283
284
  // src/dice/bulk.ts
284
- var import_rpg_dice_roller6 = require("@dice-roller/rpg-dice-roller");
285
- var import_mathjs6 = require("mathjs");
285
+ var import_rpg_dice_roller7 = require("@dice-roller/rpg-dice-roller");
286
+ var import_mathjs8 = require("mathjs");
286
287
 
287
288
  // src/dice/compare.ts
288
- var import_rpg_dice_roller3 = require("@dice-roller/rpg-dice-roller");
289
+ var import_rpg_dice_roller4 = require("@dice-roller/rpg-dice-roller");
289
290
  var import_mathjs2 = require("mathjs");
290
291
 
291
292
  // src/utils.ts
293
+ var import_uniformize2 = require("uniformize");
294
+ var import_rpg_dice_roller3 = require("@dice-roller/rpg-dice-roller");
295
+ var import_random_js2 = require("random-js");
296
+
297
+ // src/verify_template.ts
292
298
  var import_mathjs = require("mathjs");
299
+ var import_random_js = require("random-js");
293
300
  var import_uniformize = require("uniformize");
294
301
  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
- );
302
+ function evalStatsDice(testDice, allStats, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto, pity) {
303
+ let dice = testDice.trimEnd();
304
+ if (allStats && Object.keys(allStats).length > 0) {
305
+ const names = Object.keys(allStats);
306
+ for (const name of names) {
307
+ const regex = new RegExp(escapeRegex(name.standardize()), "gi");
308
+ if (dice.standardize().match(regex)) {
309
+ const statValue = allStats[name];
310
+ dice = dice.standardize().replace(regex, statValue.toString()).trimEnd();
311
+ }
320
312
  }
321
313
  }
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
- }
314
+ try {
315
+ if (!roll(replaceFormulaInDice(replaceExpByRandom(dice)), engine, pity))
316
+ throw new DiceTypeError(dice, "evalStatsDice", "no roll result");
317
+ return testDice;
318
+ } catch (error) {
319
+ throw new DiceTypeError(dice, "evalStatsDice", error);
346
320
  }
347
- return bestMatch;
348
321
  }
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);
322
+ function diceRandomParse(value, template, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto) {
323
+ if (!template.statistics) return replaceFormulaInDice(value.standardize());
324
+ value = value.standardize();
325
+ const statNames = Object.keys(template.statistics);
326
+ let newDice = value;
327
+ for (const name of statNames) {
328
+ const regex = new RegExp(escapeRegex(name.standardize()), "gi");
329
+ if (value.match(regex)) {
330
+ let max;
331
+ let min;
332
+ const foundStat = template.statistics?.[name];
333
+ if (foundStat) {
334
+ max = foundStat.max;
335
+ min = foundStat.min;
336
+ }
337
+ const total = template.total || 100;
338
+ const randomStatValue = generateRandomStat(total, max, min, engine);
339
+ newDice = value.replace(regex, randomStatValue.toString());
340
+ }
353
341
  }
354
- return findBestStatMatch(
355
- searchTerm.standardize(),
356
- normalizeRecord,
357
- similarityThreshold
358
- ) || null;
342
+ return replaceFormulaInDice(newDice);
359
343
  }
360
- function replaceUnknown(dice, replacer) {
361
- return dice.replaceAll(REMOVER_PATTERN.STAT_MATCHER, replacer).replaceAll("+0", "").replaceAll("-0", "");
344
+ function diceTypeRandomParse(dice, template, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto) {
345
+ dice = replaceExpByRandom(dice);
346
+ if (!template.statistics) return dice;
347
+ const firstStatNotcombinaison = Object.keys(template.statistics).find(
348
+ (stat) => !template.statistics?.[stat].combinaison
349
+ );
350
+ if (!firstStatNotcombinaison) return dice;
351
+ const stats = template.statistics[firstStatNotcombinaison];
352
+ const { min, max } = stats;
353
+ const total = template.total || 100;
354
+ const randomStatValue = generateRandomStat(total, max, min, engine);
355
+ return replaceFormulaInDice(dice.replaceAll("$", randomStatValue.toString()));
362
356
  }
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("error.invalidDice.stats");
357
+ function evalCombinaison(combinaison, stats) {
358
+ const newStats = {};
359
+ for (const [stat, combin] of Object.entries(combinaison)) {
360
+ let formula = combin.standardize();
361
+ for (const [statName, value] of Object.entries(stats)) {
362
+ const regex = new RegExp(statName.standardize(), "gi");
363
+ formula = formula.replace(regex, value.toString());
364
+ }
365
+ try {
366
+ newStats[stat] = (0, import_mathjs.evaluate)(formula);
367
+ } catch (error) {
368
+ throw new FormulaError(stat, "evalCombinaison", error);
369
+ }
370
370
  }
371
- return dice.replaceAll("+0", "").replaceAll("-0", "");
371
+ return newStats;
372
372
  }
373
-
374
- // src/utils.ts
375
- function escapeRegex(string) {
376
- return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
373
+ function evalOneCombinaison(combinaison, stats) {
374
+ let formula = combinaison.standardize();
375
+ for (const [statName, value] of Object.entries(stats)) {
376
+ const regex = new RegExp(statName.standardize(), "gi");
377
+ formula = formula.replace(regex, value.toString());
378
+ }
379
+ try {
380
+ return (0, import_mathjs.evaluate)(formula);
381
+ } catch (error) {
382
+ throw new FormulaError(combinaison, "evalOneCombinaison", error);
383
+ }
377
384
  }
378
- function standardizeDice(dice) {
379
- return dice.replace(
380
- /(\[[^\]]+])|([^[]+)/g,
381
- (_match, insideBrackets, outsideText) => insideBrackets ? insideBrackets : outsideText.standardize().replaceAll("df", "dF")
382
- );
385
+ function convertNumber(number) {
386
+ if (number === void 0 || number === null) return void 0;
387
+ if (number.toString().length === 0 || Number.isNaN(Number.parseInt(number.toString(), 10)))
388
+ return void 0;
389
+ if (isNumber(number)) return Number.parseInt(number.toString(), 10);
390
+ return void 0;
383
391
  }
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()}`;
392
+ function verifyTemplateValue(template, verify = true, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto) {
393
+ const parsedTemplate = templateSchema.parse(template);
394
+ const { success, failure } = parsedTemplate.critical ?? {};
395
+ const criticicalVal = {
396
+ success: convertNumber(success),
397
+ failure: convertNumber(failure)
398
+ };
399
+ const statistiqueTemplate = {
400
+ diceType: parsedTemplate.diceType,
401
+ statistics: parsedTemplate.statistics,
402
+ critical: criticicalVal,
403
+ total: parsedTemplate.total,
404
+ charName: parsedTemplate.charName,
405
+ damage: parsedTemplate.damage,
406
+ customCritical: parsedTemplate.customCritical,
407
+ forceDistrib: parsedTemplate.forceDistrib
408
+ };
409
+ if (!verify) return statistiqueTemplate;
410
+ if (statistiqueTemplate.diceType) {
411
+ if (statistiqueTemplate.diceType.match(DETECT_CRITICAL)) {
412
+ throw new DiceTypeError(
413
+ statistiqueTemplate.diceType,
414
+ "critical_dice_type",
415
+ "contains critical detection: should be in custom critical instead"
416
+ );
417
+ }
418
+ const cleanedDice2 = diceTypeRandomParse(
419
+ statistiqueTemplate.diceType,
420
+ statistiqueTemplate,
421
+ engine
422
+ );
423
+ const rolled = roll(cleanedDice2, engine);
424
+ if (!rolled) throw new DiceTypeError(cleanedDice2, "no_roll_result", "no roll result");
393
425
  }
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();
426
+ if (statistiqueTemplate.customCritical) {
427
+ if (!statistiqueTemplate.diceType) {
428
+ throw new DiceTypeError("no_dice_type", "no_dice_type", "no dice type");
429
+ }
430
+ const customCritical = statistiqueTemplate.customCritical;
431
+ for (const [, custom] of Object.entries(customCritical)) {
432
+ const cleanedDice2 = createCriticalCustom(
433
+ statistiqueTemplate.diceType,
434
+ custom,
435
+ statistiqueTemplate,
436
+ engine
437
+ );
438
+ const rolled = roll(cleanedDice2, engine);
439
+ if (!rolled)
440
+ throw new DiceTypeError(cleanedDice2, "verifyTemplateValue", "no roll result");
441
+ }
401
442
  }
402
- return token;
443
+ testDiceRegistered(statistiqueTemplate, engine);
444
+ testStatCombinaison(statistiqueTemplate, engine);
445
+ return statistiqueTemplate;
403
446
  }
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]);
447
+ function testDiceRegistered(template, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto) {
448
+ if (!template.damage) return;
449
+ if (Object.keys(template.damage).length === 0) throw new EmptyObjectError();
450
+ if (Object.keys(template.damage).length > 25) throw new TooManyDice();
451
+ for (const [name, dice] of Object.entries(template.damage)) {
452
+ if (!dice) continue;
453
+ const diceReplaced = replaceExpByRandom(dice);
454
+ const randomDiceParsed = diceRandomParse(diceReplaced, template, engine);
455
+ try {
456
+ const rolled = roll(randomDiceParsed, engine);
457
+ if (!rolled) throw new DiceTypeError(name, "no_roll_result", dice);
458
+ } catch (error) {
459
+ throw new DiceTypeError(name, "testDiceRegistered", error);
411
460
  }
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);
461
+ }
462
+ }
463
+ function testStatCombinaison(template, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto) {
464
+ if (!template.statistics) return;
465
+ const onlycombinaisonStats = Object.fromEntries(
466
+ Object.entries(template.statistics).filter(
467
+ ([_, value]) => value.combinaison !== void 0
468
+ )
469
+ );
470
+ const allOtherStats = Object.fromEntries(
471
+ Object.entries(template.statistics).filter(([_, value]) => !value.combinaison)
472
+ );
473
+ if (Object.keys(onlycombinaisonStats).length === 0) return;
474
+ const allStats = Object.keys(template.statistics).filter(
475
+ (stat) => !template.statistics[stat].combinaison
476
+ );
477
+ if (allStats.length === 0) throw new NoStatisticsError();
478
+ const error = [];
479
+ for (const [stat, value] of Object.entries(onlycombinaisonStats)) {
480
+ let formula = value.combinaison;
481
+ for (const [other, data] of Object.entries(allOtherStats)) {
482
+ const { max, min } = data;
483
+ const total = template.total || 100;
484
+ const randomStatValue = generateRandomStat(total, max, min, engine);
485
+ const regex = new RegExp(other, "gi");
486
+ formula = formula.replace(regex, randomStatValue.toString());
487
+ }
488
+ try {
489
+ (0, import_mathjs.evaluate)(formula);
490
+ } catch (e) {
491
+ error.push(stat);
444
492
  }
445
- dice = result;
446
493
  }
447
- if (dollarValue) dice = dice.replaceAll("$", dollarValue);
448
- return replaceFormulaInDice(dice);
494
+ if (error.length > 0) throw new FormulaError(error.join(", "), "testStatCombinaison");
495
+ return;
449
496
  }
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
- }
497
+ function generateRandomStat(total = 100, max, min, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto) {
498
+ let randomStatValue = total + 1;
499
+ const random = new import_random_js.Random(engine || import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto);
500
+ while (randomStatValue >= total || randomStatValue === 0) {
501
+ if (max && min) randomStatValue = randomInt(min, max, engine, random);
502
+ else if (max) randomStatValue = randomInt(1, max, engine, random);
503
+ else if (min) randomStatValue = randomInt(min, total, engine, random);
504
+ else randomStatValue = randomInt(1, total, engine, random);
464
505
  }
465
- return cleanedDice(modifiedDice);
506
+ return randomStatValue;
466
507
  }
467
- function cleanedDice(dice) {
468
- return dice.replaceAll("+-", "-").replaceAll("--", "+").replaceAll("++", "+").replaceAll("=>", ">=").replaceAll("=<", "<=").trimEnd();
508
+
509
+ // src/utils.ts
510
+ function escapeRegex(string) {
511
+ return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
512
+ }
513
+ function standardizeDice(dice) {
514
+ return dice.replace(
515
+ /(\[[^\]]+])|([^[]+)/g,
516
+ (_match, insideBrackets, outsideText) => insideBrackets ? insideBrackets : outsideText.standardize().replaceAll("df", "dF")
517
+ );
469
518
  }
470
519
  function isNumber(value) {
471
520
  return value !== void 0 && (typeof value === "number" || !Number.isNaN(Number(value)) && typeof value === "string" && value.trim().length > 0);
472
521
  }
473
- function replaceExpByRandom(dice, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto) {
522
+ function replaceExpByRandom(dice, engine = import_rpg_dice_roller3.NumberGenerator.engines.nodeCrypto) {
474
523
  const diceRegex = /\{exp( ?\|\| ?(?<default>\d+))?}/gi;
475
524
  return dice.replace(diceRegex, (_match, _p1, _p2, _offset, _string, groups) => {
476
525
  const defaultValue = groups?.default;
477
526
  return defaultValue ?? randomInt(1, 999, engine).toString();
478
527
  });
479
528
  }
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);
529
+ function randomInt(min, max, engine = import_rpg_dice_roller3.NumberGenerator.engines.nodeCrypto, rng) {
530
+ if (!rng) rng = new import_random_js2.Random(engine || void 0);
482
531
  return rng.integer(min, max);
483
532
  }
484
- function createCriticalCustom(dice, customCritical, template, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto) {
533
+ function createCriticalCustom(dice, customCritical, template, engine = import_rpg_dice_roller3.NumberGenerator.engines.nodeCrypto) {
485
534
  const compareRegex = dice.match(SIGN_REGEX_SPACE);
486
535
  let customDice = dice;
487
536
  const compareValue = diceTypeRandomParse(customCritical.value, template, engine);
@@ -524,7 +573,7 @@ function canComparisonFail(maxRollValue, compare, minRollValue = 1) {
524
573
  return true;
525
574
  }
526
575
  }
527
- function rollCompare(value, engine = import_rpg_dice_roller3.NumberGenerator.engines.nodeCrypto, pity) {
576
+ function rollCompare(value, engine = import_rpg_dice_roller4.NumberGenerator.engines.nodeCrypto, pity) {
528
577
  if (isNumber(value)) return { value: Number.parseInt(value, 10) };
529
578
  if (!value || typeof value === "string" && value.trim() === "") {
530
579
  return { value: 0, diceResult: value };
@@ -543,7 +592,7 @@ function rollCompare(value, engine = import_rpg_dice_roller3.NumberGenerator.eng
543
592
  diceResult: rollComp?.result
544
593
  };
545
594
  }
546
- function getCompare(dice, compareRegex, engine = import_rpg_dice_roller3.NumberGenerator.engines.nodeCrypto, pity) {
595
+ function getCompare(dice, compareRegex, engine = import_rpg_dice_roller4.NumberGenerator.engines.nodeCrypto, pity) {
547
596
  if (dice.match(/((\{.*,(.*)+\}|([><=!]+\d+f))([><=]|!=)+\d+\}?)|\{(.*)(([><=]|!=)+).*\}/))
548
597
  return { dice, compare: void 0 };
549
598
  dice = dice.replace(SIGN_REGEX_SPACE, "");
@@ -599,7 +648,7 @@ function canComparisonSucceed(maxRollValue, compare, minRollValue) {
599
648
  var import_mathjs4 = require("mathjs");
600
649
 
601
650
  // src/dice/signs.ts
602
- var import_rpg_dice_roller4 = require("@dice-roller/rpg-dice-roller");
651
+ var import_rpg_dice_roller5 = require("@dice-roller/rpg-dice-roller");
603
652
  var import_mathjs3 = require("mathjs");
604
653
 
605
654
  // src/dice/replace.ts
@@ -689,7 +738,7 @@ function inverseSign(sign) {
689
738
  return "==";
690
739
  }
691
740
  }
692
- function compareSignFormule(toRoll, compareRegex, element, diceResult, engine = import_rpg_dice_roller4.NumberGenerator.engines.nodeCrypto, pity, rollBounds) {
741
+ function compareSignFormule(toRoll, compareRegex, element, diceResult, engine = import_rpg_dice_roller5.NumberGenerator.engines.nodeCrypto, pity, rollBounds) {
693
742
  let results = "";
694
743
  let trivial = false;
695
744
  const compareResult = getCompare(toRoll, compareRegex, engine);
@@ -750,43 +799,278 @@ function normalizeExplodingSuccess(dice) {
750
799
  parsedValue = 0;
751
800
  }
752
801
  }
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);
802
+ const normalizedSegment = "!";
803
+ const replacedDice = dice.replace(match[0], normalizedSegment);
804
+ return {
805
+ dice: replacedDice,
806
+ originalDice: dice,
807
+ sign: normalizedSign,
808
+ value: parsedValue,
809
+ normalizedSegment,
810
+ originalSegment: match[0]
811
+ };
812
+ }
813
+ function countExplodingSuccesses(diceRoll, sign, value) {
814
+ const rollsArray = Array.isArray(diceRoll) ? diceRoll : [diceRoll];
815
+ const flatValues = [];
816
+ for (const dr of rollsArray) {
817
+ const groups = dr.rolls ?? [];
818
+ for (const group of groups) {
819
+ const innerRolls = group.rolls ?? [];
820
+ for (const roll2 of innerRolls) {
821
+ if (typeof roll2.value === "number") flatValues.push(roll2.value);
822
+ }
823
+ }
824
+ }
825
+ return flatValues.reduce(
826
+ (acc, current) => acc + (matchComparison(sign, current, value) ? 1 : 0),
827
+ 0
828
+ );
829
+ }
830
+
831
+ // src/dice/extract.ts
832
+ var import_rpg_dice_roller6 = require("@dice-roller/rpg-dice-roller");
833
+
834
+ // src/similarities/generateStatsDice.ts
835
+ var import_mathjs5 = require("mathjs");
836
+
837
+ // src/similarities/similarity.ts
838
+ function calculateSimilarity(str1, str2) {
839
+ const longer = str1.length > str2.length ? str1 : str2;
840
+ const shorter = str1.length > str2.length ? str2 : str1;
841
+ if (longer.length === 0) return 1;
842
+ const distance = levenshteinDistance(longer, shorter);
843
+ return (longer.length - distance) / longer.length;
844
+ }
845
+ function levenshteinDistance(str1, str2) {
846
+ const matrix = Array(str2.length + 1).fill(null).map(() => Array(str1.length + 1).fill(null));
847
+ for (let i = 0; i <= str1.length; i++) matrix[0][i] = i;
848
+ for (let j = 0; j <= str2.length; j++) matrix[j][0] = j;
849
+ for (let j = 1; j <= str2.length; j++) {
850
+ for (let i = 1; i <= str1.length; i++) {
851
+ const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;
852
+ matrix[j][i] = Math.min(
853
+ matrix[j][i - 1] + 1,
854
+ // insertion
855
+ matrix[j - 1][i] + 1,
856
+ // deletion
857
+ matrix[j - 1][i - 1] + cost
858
+ // substitution
859
+ );
860
+ }
861
+ }
862
+ return matrix[str2.length][str1.length];
863
+ }
864
+ function findBestStatMatch(searchTerm, normalizedStats, similarityThreshold = MIN_THRESHOLD_MATCH) {
865
+ const exact = normalizedStats.get(searchTerm);
866
+ if (exact) return exact;
867
+ const candidates = [];
868
+ for (const [normalizedKey, original] of normalizedStats) {
869
+ if (normalizedKey.startsWith(searchTerm))
870
+ candidates.push([original, calculateSimilarity(searchTerm, normalizedKey)]);
871
+ }
872
+ if (candidates.length === 1) return candidates[0][0];
873
+ if (candidates.length > 0) {
874
+ candidates.sort((a, b) => b[1] - a[1]);
875
+ if (candidates[0][1] >= similarityThreshold) return candidates[0][0];
876
+ }
877
+ let bestMatch;
878
+ let bestSimilarity = 0;
879
+ for (const [normalizedKey, original] of normalizedStats) {
880
+ const similarity = calculateSimilarity(searchTerm, normalizedKey);
881
+ if (similarity === 1) return original;
882
+ if (similarity > bestSimilarity && similarity >= similarityThreshold) {
883
+ bestSimilarity = similarity;
884
+ bestMatch = original;
885
+ }
886
+ }
887
+ return bestMatch;
888
+ }
889
+ function findBestRecord(record, searchTerm, similarityThreshold = MIN_THRESHOLD_MATCH) {
890
+ const normalizeRecord = /* @__PURE__ */ new Map();
891
+ for (const key of Object.keys(record)) {
892
+ normalizeRecord.set(key.standardize(), key);
893
+ }
894
+ return findBestStatMatch(
895
+ searchTerm.standardize(),
896
+ normalizeRecord,
897
+ similarityThreshold
898
+ ) || null;
899
+ }
900
+ function replaceUnknown(dice, replacer) {
901
+ return dice.replaceAll(REMOVER_PATTERN.STAT_MATCHER, replacer).replaceAll("+0", "").replaceAll("-0", "");
902
+ }
903
+ function verifyStatMatcherPattern(dice, replaceUnknow) {
904
+ if (REMOVER_PATTERN.STAT_MATCHER.test(dice)) {
905
+ if (replaceUnknow)
906
+ return replaceUnknown(dice, replaceUnknow);
907
+ const matched = dice.matchAll(new RegExp(REMOVER_PATTERN.STAT_MATCHER));
908
+ const stats = matched ? Array.from(matched, (m) => m?.[0]).map((s) => `\`${s}\``).join(", ") : "unknown";
909
+ throw new DiceTypeError(stats, "unknown_stats");
910
+ }
911
+ return dice.replaceAll("+0", "").replaceAll("-0", "");
912
+ }
913
+
914
+ // src/similarities/generateStatsDice.ts
915
+ function handleDiceAfterD(tokenStd, normalizedStats) {
916
+ const diceMatch = /^(\d*)d(.+)$/i.exec(tokenStd);
917
+ if (!diceMatch) return null;
918
+ const diceCount = diceMatch[1] || "";
919
+ const afterD = diceMatch[2];
920
+ const bestMatch = findBestStatMatch(afterD, normalizedStats, 1);
921
+ if (bestMatch) {
922
+ const [, value] = bestMatch;
923
+ return `${diceCount}d${value.toString()}`;
924
+ }
925
+ return null;
926
+ }
927
+ function handleSimpleToken(tokenStd, token, normalizedStats, minThreshold) {
928
+ const bestMatch = findBestStatMatch(tokenStd, normalizedStats, minThreshold);
929
+ if (bestMatch) {
930
+ const [, value] = bestMatch;
931
+ return value.toString();
932
+ }
933
+ return token;
934
+ }
935
+ function generateStatsDice(originalDice, stats, minThreshold = 0.6, dollarValue) {
936
+ let dice = originalDice.standardize();
937
+ if (stats && Object.keys(stats).length > 0) {
938
+ const normalizedStats = /* @__PURE__ */ new Map();
939
+ for (const [key, value] of Object.entries(stats)) {
940
+ const normalized = key.standardize();
941
+ normalizedStats.set(normalized, [key, value]);
942
+ }
943
+ const partsRegex = /(\[[^\]]+])|([^[]+)/g;
944
+ let result = "";
945
+ let match;
946
+ while ((match = partsRegex.exec(dice)) !== null) {
947
+ const insideBrackets = match[1];
948
+ const outsideText = match[2];
949
+ if (insideBrackets) {
950
+ result += insideBrackets;
951
+ continue;
952
+ }
953
+ if (!outsideText) {
954
+ continue;
955
+ }
956
+ const tokenRegex = /(\$?[\p{L}\p{N}_.]+)/gu;
957
+ let lastIndex = 0;
958
+ let tokenMatch;
959
+ while ((tokenMatch = tokenRegex.exec(outsideText)) !== null) {
960
+ result += outsideText.slice(lastIndex, tokenMatch.index);
961
+ const token = tokenMatch[0];
962
+ const tokenHasDollar = token.startsWith("$");
963
+ const tokenForCompare = tokenHasDollar ? token.slice(1) : token;
964
+ const tokenStd = tokenForCompare.standardize();
965
+ const diceReplacement = handleDiceAfterD(tokenStd, normalizedStats);
966
+ if (diceReplacement) {
967
+ result += diceReplacement;
968
+ lastIndex = tokenRegex.lastIndex;
969
+ continue;
970
+ }
971
+ result += handleSimpleToken(tokenStd, token, normalizedStats, minThreshold);
972
+ lastIndex = tokenRegex.lastIndex;
973
+ }
974
+ result += outsideText.slice(lastIndex);
975
+ }
976
+ dice = result;
977
+ }
978
+ if (dollarValue) dice = dice.replaceAll("$", dollarValue);
979
+ return replaceFormulaInDice(dice);
980
+ }
981
+ function replaceFormulaInDice(dice) {
982
+ const formula = /(?<formula>\{{2}(.+?)}{2})/gim;
983
+ let match;
984
+ let modifiedDice = dice;
985
+ while ((match = formula.exec(dice)) !== null) {
986
+ if (match.groups?.formula) {
987
+ const formulae = match.groups.formula.replaceAll("{{", "").replaceAll("}}", "");
988
+ try {
989
+ const result = (0, import_mathjs5.evaluate)(formulae);
990
+ modifiedDice = modifiedDice.replace(match.groups.formula, result.toString());
991
+ } catch (error) {
992
+ throw new FormulaError(match.groups.formula, "replaceFormulasInDice", error);
993
+ }
994
+ }
995
+ }
996
+ return cleanedDice(modifiedDice);
997
+ }
998
+ function cleanedDice(dice) {
999
+ return dice.replaceAll("+-", "-").replaceAll("--", "+").replaceAll("++", "+").replaceAll("=>", ">=").replaceAll("=<", "<=").trimEnd();
1000
+ }
1001
+
1002
+ // src/similarities/resolveFormula.ts
1003
+ var import_mathjs6 = require("mathjs");
1004
+ function toFiniteNumber(value) {
1005
+ if (typeof value === "number" && Number.isFinite(value)) return value;
1006
+ return void 0;
1007
+ }
1008
+ function substituteFormulaTokens(expr, resolvedStats, similarityThreshold = MIN_THRESHOLD_MATCH) {
1009
+ return expr.replace(/([\p{L}\p{M}._-]+)/gu, (token) => {
1010
+ const match = findBestStatMatch(token, resolvedStats, similarityThreshold);
1011
+ return match !== void 0 ? match.toString() : token;
1012
+ });
1013
+ }
1014
+ function resolveFormulaHint(formula, allAttributes, similarityThreshold = MIN_THRESHOLD_MATCH) {
1015
+ const trimmed = formula.trim();
1016
+ if (!trimmed) return { kind: "not-formula" };
1017
+ if (isNumber(trimmed)) return { kind: "not-formula" };
1018
+ const resolved = /* @__PURE__ */ new Map();
1019
+ const pending = /* @__PURE__ */ new Map();
1020
+ for (const [name, val] of Object.entries(allAttributes)) {
1021
+ const norm = name.standardize();
1022
+ if (typeof val === "number") {
1023
+ if (Number.isFinite(val)) resolved.set(norm, val);
1024
+ continue;
1025
+ }
1026
+ const t = val.trim();
1027
+ if (!t) continue;
1028
+ if (isNumber(t)) {
1029
+ const numeric = Number(t);
1030
+ if (Number.isFinite(numeric)) resolved.set(norm, numeric);
1031
+ } else {
1032
+ pending.set(norm, t.standardize());
1033
+ }
1034
+ }
1035
+ for (const [normName, expr2] of pending) {
1036
+ pending.set(normName, substituteFormulaTokens(expr2, resolved, similarityThreshold));
1037
+ }
1038
+ let progress = true;
1039
+ while (pending.size > 0 && progress) {
1040
+ progress = false;
1041
+ for (const [normName, expr2] of pending) {
1042
+ try {
1043
+ const result = toFiniteNumber((0, import_mathjs6.evaluate)(expr2));
1044
+ if (result === void 0) continue;
1045
+ resolved.set(normName, result);
1046
+ pending.delete(normName);
1047
+ progress = true;
1048
+ for (const [otherNorm, otherExpr] of pending) {
1049
+ pending.set(
1050
+ otherNorm,
1051
+ substituteFormulaTokens(otherExpr, resolved, similarityThreshold)
1052
+ );
1053
+ }
1054
+ } catch {
773
1055
  }
774
1056
  }
775
1057
  }
776
- return flatValues.reduce(
777
- (acc, current) => acc + (matchComparison(sign, current, value) ? 1 : 0),
778
- 0
779
- );
1058
+ const normFormula = trimmed.standardize();
1059
+ const expr = substituteFormulaTokens(normFormula, resolved, similarityThreshold);
1060
+ try {
1061
+ const result = toFiniteNumber((0, import_mathjs6.evaluate)(expr));
1062
+ if (result !== void 0) return { kind: "resolved", value: result };
1063
+ return { kind: "error" };
1064
+ } catch {
1065
+ return { kind: "error" };
1066
+ }
780
1067
  }
781
1068
 
782
- // src/dice/extract.ts
783
- var import_rpg_dice_roller5 = require("@dice-roller/rpg-dice-roller");
784
-
785
1069
  // src/dice/calculator.ts
786
- var import_mathjs5 = require("mathjs");
1070
+ var import_mathjs7 = require("mathjs");
787
1071
  function calculator(sign, value, total) {
788
1072
  if (sign === "^") sign = "**";
789
- return (0, import_mathjs5.evaluate)(`${total} ${sign} ${value}`);
1073
+ return (0, import_mathjs7.evaluate)(`${total} ${sign} ${value}`);
790
1074
  }
791
1075
 
792
1076
  // src/dice/extract.ts
@@ -821,10 +1105,10 @@ function extractValuesFromOutput(output) {
821
1105
  }
822
1106
  return values;
823
1107
  }
824
- function getRollBounds(dice, engine = import_rpg_dice_roller5.NumberGenerator.engines.nodeCrypto) {
1108
+ function getRollBounds(dice, engine = import_rpg_dice_roller6.NumberGenerator.engines.nodeCrypto) {
825
1109
  try {
826
- const roller = new import_rpg_dice_roller5.DiceRoller();
827
- import_rpg_dice_roller5.NumberGenerator.generator.engine = engine;
1110
+ const roller = new import_rpg_dice_roller6.DiceRoller();
1111
+ import_rpg_dice_roller6.NumberGenerator.generator.engine = engine;
828
1112
  const rollResult = roller.roll(dice);
829
1113
  const instance = Array.isArray(rollResult) ? rollResult[0] : rollResult;
830
1114
  const { minTotal, maxTotal } = instance;
@@ -937,8 +1221,8 @@ function handleBulkRolls(dice, isCurlyBulk, bulkContent, compare, explodingSucce
937
1221
  sort
938
1222
  );
939
1223
  }
940
- const roller = new import_rpg_dice_roller6.DiceRoller();
941
- import_rpg_dice_roller6.NumberGenerator.generator.engine = engine;
1224
+ const roller = new import_rpg_dice_roller7.DiceRoller();
1225
+ import_rpg_dice_roller7.NumberGenerator.generator.engine = engine;
942
1226
  for (let i = 0; i < numberOfDice; i++) {
943
1227
  try {
944
1228
  roller.roll(diceToRoll);
@@ -960,8 +1244,8 @@ function handleBulkRolls(dice, isCurlyBulk, bulkContent, compare, explodingSucce
960
1244
  function handleBulkRollsWithComparison(numberOfDice, diceToRoll, comments, activeCompare, explodingSuccess, diceDisplay, isCurlyBulk, curlyCompare, compare, engine, sort) {
961
1245
  const results = [];
962
1246
  let successCount = 0;
963
- const roller = new import_rpg_dice_roller6.DiceRoller();
964
- import_rpg_dice_roller6.NumberGenerator.generator.engine = engine;
1247
+ const roller = new import_rpg_dice_roller7.DiceRoller();
1248
+ import_rpg_dice_roller7.NumberGenerator.generator.engine = engine;
965
1249
  let trivialComparisonDetected = false;
966
1250
  const formatOutput = (output, addStar) => {
967
1251
  const formatted = addStar && isCurlyBulk ? output.replace(
@@ -995,7 +1279,7 @@ function handleBulkRollsWithComparison(numberOfDice, diceToRoll, comments, activ
995
1279
  results.push(formattedRollOutput);
996
1280
  } else {
997
1281
  const rollTotal = rollInstance.total;
998
- const isSuccess = (0, import_mathjs6.evaluate)(
1282
+ const isSuccess = (0, import_mathjs8.evaluate)(
999
1283
  `${rollTotal}${activeCompare.sign}${activeCompare.value}`
1000
1284
  );
1001
1285
  if (isSuccess) successCount++;
@@ -1058,7 +1342,7 @@ function handleBulkRollsWithComparison(numberOfDice, diceToRoll, comments, activ
1058
1342
  }
1059
1343
 
1060
1344
  // src/dice/pity.ts
1061
- var import_mathjs7 = require("mathjs");
1345
+ var import_mathjs9 = require("mathjs");
1062
1346
  function handlePitySystem(dice, compare, diceRoll, roller, engine) {
1063
1347
  const currentRoll = Array.isArray(diceRoll) ? diceRoll[0] : diceRoll;
1064
1348
  const maxPossible = currentRoll ? currentRoll.maxTotal : null;
@@ -1066,7 +1350,7 @@ function handlePitySystem(dice, compare, diceRoll, roller, engine) {
1066
1350
  if (!isComparisonPossible) {
1067
1351
  return { rerollCount: 0 };
1068
1352
  }
1069
- let isFail = (0, import_mathjs7.evaluate)(`${roller.total}${compare.sign}${compare.value}`);
1353
+ let isFail = (0, import_mathjs9.evaluate)(`${roller.total}${compare.sign}${compare.value}`);
1070
1354
  if (isFail) {
1071
1355
  return { rerollCount: 0 };
1072
1356
  }
@@ -1081,14 +1365,14 @@ function handlePitySystem(dice, compare, diceRoll, roller, engine) {
1081
1365
  }
1082
1366
  rerollCount++;
1083
1367
  if (res && res.total !== void 0) {
1084
- isFail = (0, import_mathjs7.evaluate)(`${res.total}${compare.sign}${compare.value}`);
1368
+ isFail = (0, import_mathjs9.evaluate)(`${res.total}${compare.sign}${compare.value}`);
1085
1369
  }
1086
1370
  }
1087
1371
  return { rerollCount, result: res };
1088
1372
  }
1089
1373
 
1090
1374
  // src/roll.ts
1091
- function roll(dice, engine = import_rpg_dice_roller7.NumberGenerator.engines.nodeCrypto, pity, sort) {
1375
+ function roll(dice, engine = import_rpg_dice_roller8.NumberGenerator.engines.nodeCrypto, pity, sort) {
1092
1376
  if (sort === "none" /* None */) sort = void 0;
1093
1377
  const prepared = prepareDice(dice);
1094
1378
  if (!prepared.dice.includes("d")) return void 0;
@@ -1129,8 +1413,8 @@ function roll(dice, engine = import_rpg_dice_roller7.NumberGenerator.engines.nod
1129
1413
  sort
1130
1414
  );
1131
1415
  }
1132
- const roller = new import_rpg_dice_roller7.DiceRoller();
1133
- import_rpg_dice_roller7.NumberGenerator.generator.engine = engine;
1416
+ const roller = new import_rpg_dice_roller8.DiceRoller();
1417
+ import_rpg_dice_roller8.NumberGenerator.generator.engine = engine;
1134
1418
  let diceWithoutComment = processedDice.replace(COMMENT_REGEX, "").trimEnd();
1135
1419
  diceWithoutComment = setSortOrder(diceWithoutComment, sort);
1136
1420
  let diceRoll;
@@ -1207,7 +1491,7 @@ function roll(dice, engine = import_rpg_dice_roller7.NumberGenerator.engines.nod
1207
1491
  trivial: compare?.trivial ? true : void 0
1208
1492
  };
1209
1493
  }
1210
- function sharedRolls(dice, engine = import_rpg_dice_roller7.NumberGenerator.engines.nodeCrypto, pity, explodingSuccessMain, diceDisplay, isSharedCurly, sort) {
1494
+ function sharedRolls(dice, engine = import_rpg_dice_roller8.NumberGenerator.engines.nodeCrypto, pity, explodingSuccessMain, diceDisplay, isSharedCurly, sort) {
1211
1495
  if (!explodingSuccessMain)
1212
1496
  explodingSuccessMain = normalizeExplodingSuccess(dice.split(";")[0] ?? dice);
1213
1497
  if (explodingSuccessMain) {
@@ -1242,13 +1526,13 @@ function sharedRolls(dice, engine = import_rpg_dice_roller7.NumberGenerator.engi
1242
1526
  const sortFromMain = getSortOrder(diceMain);
1243
1527
  const rollBounds = getRollBounds(diceMain, engine);
1244
1528
  let diceResult = roll(diceMain, engine, pity, sort);
1245
- if (!diceResult || !diceResult.total) {
1529
+ if (!diceResult?.total) {
1246
1530
  if (hidden) {
1247
1531
  diceResult = roll(fixParenthesis(split[0]), engine, pity, sort);
1248
1532
  hidden = false;
1249
1533
  } else return void 0;
1250
1534
  }
1251
- if (!diceResult || !diceResult.total) return void 0;
1535
+ if (!diceResult?.total) return void 0;
1252
1536
  if (explodingSuccessMain && diceResult.result) {
1253
1537
  const values = extractValuesFromOutput(diceResult.result);
1254
1538
  diceResult.total = values.filter(
@@ -1290,7 +1574,7 @@ function sharedRolls(dice, engine = import_rpg_dice_roller7.NumberGenerator.engi
1290
1574
  const { diceAll } = replaceText(element, diceResult.total, diceResult.dice);
1291
1575
  let successCount = 0;
1292
1576
  try {
1293
- const evaluated = (0, import_mathjs8.evaluate)(toRoll);
1577
+ const evaluated = (0, import_mathjs10.evaluate)(toRoll);
1294
1578
  successCount = evaluated ? 1 : 0;
1295
1579
  } catch (error) {
1296
1580
  const evaluated = roll(toRoll, engine, pity);
@@ -1324,7 +1608,7 @@ function sharedRolls(dice, engine = import_rpg_dice_roller7.NumberGenerator.engi
1324
1608
  diceResult.dice
1325
1609
  );
1326
1610
  try {
1327
- const evaluated = (0, import_mathjs8.evaluate)(toRoll);
1611
+ const evaluated = (0, import_mathjs10.evaluate)(toRoll);
1328
1612
  results.push(`\u25C8 ${comment}${diceAll}: ${formule} = ${evaluated}`);
1329
1613
  total += Number.parseInt(evaluated, 10);
1330
1614
  } catch (error) {
@@ -1353,7 +1637,7 @@ function sharedRolls(dice, engine = import_rpg_dice_roller7.NumberGenerator.engi
1353
1637
  trivial: hasTrivialComparison ? true : void 0
1354
1638
  };
1355
1639
  }
1356
- function replaceInFormula(element, diceResult, compareResult, res, engine = import_rpg_dice_roller7.NumberGenerator.engines.nodeCrypto, pity) {
1640
+ function replaceInFormula(element, diceResult, compareResult, res, engine = import_rpg_dice_roller8.NumberGenerator.engines.nodeCrypto, pity) {
1357
1641
  const { formule, diceAll } = replaceText(
1358
1642
  element,
1359
1643
  diceResult.total ?? 0,
@@ -1363,7 +1647,7 @@ function replaceInFormula(element, diceResult, compareResult, res, engine = impo
1363
1647
  const invertedSign = res ? compareResult.compare.sign : inverseSign(compareResult.compare.sign);
1364
1648
  let evaluateRoll;
1365
1649
  try {
1366
- evaluateRoll = (0, import_mathjs8.evaluate)(compareResult.dice);
1650
+ evaluateRoll = (0, import_mathjs10.evaluate)(compareResult.dice);
1367
1651
  return `${validSign} ${diceAll}: ${formule} = ${evaluateRoll}${invertedSign}${compareResult.compare?.value}`;
1368
1652
  } catch (error) {
1369
1653
  const evaluateRoll2 = roll(compareResult.dice, engine, pity);
@@ -1372,218 +1656,6 @@ function replaceInFormula(element, diceResult, compareResult, res, engine = impo
1372
1656
  return `${validSign} ${diceAll}: ${formule} = ${evaluateRoll2}${invertedSign}${compareResult.compare?.value}`;
1373
1657
  }
1374
1658
  }
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
1659
  // Annotate the CommonJS export names for ESM import in node:
1588
1660
  0 && (module.exports = {
1589
1661
  COMMENT_REGEX,
@@ -1626,6 +1698,7 @@ function generateRandomStat(total = 100, max, min, engine = import_rpg_dice_roll
1626
1698
  replaceFormulaInDice,
1627
1699
  replaceInFormula,
1628
1700
  replaceUnknown,
1701
+ resolveFormulaHint,
1629
1702
  roll,
1630
1703
  standardizeDice,
1631
1704
  templateSchema,