@dicelette/core 1.28.3 → 1.28.4
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/README.md +0 -3
- package/dist/index.d.mts +3 -7
- package/dist/index.d.ts +3 -7
- package/dist/index.js +77 -80
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +77 -79
- package/dist/index.mjs.map +1 -1
- package/package.json +61 -57
package/README.md
CHANGED
|
@@ -123,9 +123,6 @@ Note: when a parameter `engine` is shown it usually defaults to the `NumberGener
|
|
|
123
123
|
|
|
124
124
|
### Utility functions (`src/utils.ts`)
|
|
125
125
|
|
|
126
|
-
#### Function: escapeRegex(string: string): string
|
|
127
|
-
- Escape input string to be used in a RegExp.
|
|
128
|
-
|
|
129
126
|
#### Function: standardizeDice(dice: string): string
|
|
130
127
|
- Standardizes dice notation while preserving bracketed text.
|
|
131
128
|
|
package/dist/index.d.mts
CHANGED
|
@@ -355,6 +355,7 @@ declare function resolveFormulaHint(formula: string, allAttributes: Record<strin
|
|
|
355
355
|
declare function calculateSimilarity(str1: string, str2: string): number;
|
|
356
356
|
/**
|
|
357
357
|
* Calculates the Levenshtein distance between two strings.
|
|
358
|
+
* Uses two rolling rows instead of a full matrix: O(min(m,n)) space instead of O(m×n).
|
|
358
359
|
*/
|
|
359
360
|
declare function levenshteinDistance(str1: string, str2: string): number;
|
|
360
361
|
declare function findBestStatMatch<T>(searchTerm: string, normalizedStats: Map<string, T>, similarityThreshold?: number): T | undefined;
|
|
@@ -380,11 +381,6 @@ declare function splitDiceComment(dice: string): {
|
|
|
380
381
|
dice: string;
|
|
381
382
|
comment: string | undefined;
|
|
382
383
|
};
|
|
383
|
-
/**
|
|
384
|
-
* Escape regex string
|
|
385
|
-
* @param string {string}
|
|
386
|
-
*/
|
|
387
|
-
declare function escapeRegex(string: string): string;
|
|
388
384
|
/**
|
|
389
385
|
* Allow to keep the text as if in brackets
|
|
390
386
|
* @param dice {string}
|
|
@@ -463,7 +459,7 @@ declare function evalOneCombinaison(combinaison: string, stats: Record<string, n
|
|
|
463
459
|
/**
|
|
464
460
|
* Parse the provided JSON and verify each field to check if everything could work when rolling
|
|
465
461
|
* @param {unknown} template
|
|
466
|
-
* @param
|
|
462
|
+
* @param verify - If true, will roll the dices to check if everything is valid
|
|
467
463
|
* @param engine
|
|
468
464
|
* @returns {StatisticalTemplate}
|
|
469
465
|
*/
|
|
@@ -490,4 +486,4 @@ declare function testStatCombinaison(template: StatisticalTemplate, engine?: Eng
|
|
|
490
486
|
*/
|
|
491
487
|
declare function generateRandomStat(total?: number | undefined, max?: number, min?: number, engine?: Engine | null): number;
|
|
492
488
|
|
|
493
|
-
export { COMMENT_REGEX, type Compare, type ComparedValue, type Critical, type CustomCritical, type CustomCriticalMap, DETECT_CRITICAL, DiceTypeError, EmptyObjectError, FormulaError, type FormulaHintResult, MIN_THRESHOLD_MATCH, MaxGreater, type Modifier, NORMALIZE_SINGLE_DICE, NoStatisticsError, OPTIONAL_COMMENT, REMOVER_PATTERN, type Resultat, SIGN_REGEX, SIGN_REGEX_SPACE, SYMBOL_DICE, type Sign, SortOrder, type Statistic, type StatisticalSchema, type StatisticalTemplate, TooManyDice, TooManyStats, calculateSimilarity, createCriticalCustom, diceRandomParse, diceTypeRandomParse,
|
|
489
|
+
export { COMMENT_REGEX, type Compare, type ComparedValue, type Critical, type CustomCritical, type CustomCriticalMap, DETECT_CRITICAL, DiceTypeError, EmptyObjectError, FormulaError, type FormulaHintResult, MIN_THRESHOLD_MATCH, MaxGreater, type Modifier, NORMALIZE_SINGLE_DICE, NoStatisticsError, OPTIONAL_COMMENT, REMOVER_PATTERN, type Resultat, SIGN_REGEX, SIGN_REGEX_SPACE, SYMBOL_DICE, type Sign, SortOrder, type Statistic, type StatisticalSchema, type StatisticalTemplate, TooManyDice, TooManyStats, calculateSimilarity, createCriticalCustom, diceRandomParse, diceTypeRandomParse, evalCombinaison, evalOneCombinaison, evalStatsDice, findBestRecord, findBestStatMatch, generateRandomStat, generateStatsDice, getCachedRegex, getEngine, getEngineId, includeDiceType, isNumber, levenshteinDistance, randomInt, replaceExpByRandom, replaceFormulaInDice, replaceInFormula, replaceUnknown, resolveFormulaHint, roll, splitDiceComment, standardizeDice, templateSchema, testDiceRegistered, testStatCombinaison, verifyStatMatcherPattern, verifyTemplateValue };
|
package/dist/index.d.ts
CHANGED
|
@@ -355,6 +355,7 @@ declare function resolveFormulaHint(formula: string, allAttributes: Record<strin
|
|
|
355
355
|
declare function calculateSimilarity(str1: string, str2: string): number;
|
|
356
356
|
/**
|
|
357
357
|
* Calculates the Levenshtein distance between two strings.
|
|
358
|
+
* Uses two rolling rows instead of a full matrix: O(min(m,n)) space instead of O(m×n).
|
|
358
359
|
*/
|
|
359
360
|
declare function levenshteinDistance(str1: string, str2: string): number;
|
|
360
361
|
declare function findBestStatMatch<T>(searchTerm: string, normalizedStats: Map<string, T>, similarityThreshold?: number): T | undefined;
|
|
@@ -380,11 +381,6 @@ declare function splitDiceComment(dice: string): {
|
|
|
380
381
|
dice: string;
|
|
381
382
|
comment: string | undefined;
|
|
382
383
|
};
|
|
383
|
-
/**
|
|
384
|
-
* Escape regex string
|
|
385
|
-
* @param string {string}
|
|
386
|
-
*/
|
|
387
|
-
declare function escapeRegex(string: string): string;
|
|
388
384
|
/**
|
|
389
385
|
* Allow to keep the text as if in brackets
|
|
390
386
|
* @param dice {string}
|
|
@@ -463,7 +459,7 @@ declare function evalOneCombinaison(combinaison: string, stats: Record<string, n
|
|
|
463
459
|
/**
|
|
464
460
|
* Parse the provided JSON and verify each field to check if everything could work when rolling
|
|
465
461
|
* @param {unknown} template
|
|
466
|
-
* @param
|
|
462
|
+
* @param verify - If true, will roll the dices to check if everything is valid
|
|
467
463
|
* @param engine
|
|
468
464
|
* @returns {StatisticalTemplate}
|
|
469
465
|
*/
|
|
@@ -490,4 +486,4 @@ declare function testStatCombinaison(template: StatisticalTemplate, engine?: Eng
|
|
|
490
486
|
*/
|
|
491
487
|
declare function generateRandomStat(total?: number | undefined, max?: number, min?: number, engine?: Engine | null): number;
|
|
492
488
|
|
|
493
|
-
export { COMMENT_REGEX, type Compare, type ComparedValue, type Critical, type CustomCritical, type CustomCriticalMap, DETECT_CRITICAL, DiceTypeError, EmptyObjectError, FormulaError, type FormulaHintResult, MIN_THRESHOLD_MATCH, MaxGreater, type Modifier, NORMALIZE_SINGLE_DICE, NoStatisticsError, OPTIONAL_COMMENT, REMOVER_PATTERN, type Resultat, SIGN_REGEX, SIGN_REGEX_SPACE, SYMBOL_DICE, type Sign, SortOrder, type Statistic, type StatisticalSchema, type StatisticalTemplate, TooManyDice, TooManyStats, calculateSimilarity, createCriticalCustom, diceRandomParse, diceTypeRandomParse,
|
|
489
|
+
export { COMMENT_REGEX, type Compare, type ComparedValue, type Critical, type CustomCritical, type CustomCriticalMap, DETECT_CRITICAL, DiceTypeError, EmptyObjectError, FormulaError, type FormulaHintResult, MIN_THRESHOLD_MATCH, MaxGreater, type Modifier, NORMALIZE_SINGLE_DICE, NoStatisticsError, OPTIONAL_COMMENT, REMOVER_PATTERN, type Resultat, SIGN_REGEX, SIGN_REGEX_SPACE, SYMBOL_DICE, type Sign, SortOrder, type Statistic, type StatisticalSchema, type StatisticalTemplate, TooManyDice, TooManyStats, calculateSimilarity, createCriticalCustom, diceRandomParse, diceTypeRandomParse, evalCombinaison, evalOneCombinaison, evalStatsDice, findBestRecord, findBestStatMatch, generateRandomStat, generateStatsDice, getCachedRegex, getEngine, getEngineId, includeDiceType, isNumber, levenshteinDistance, randomInt, replaceExpByRandom, replaceFormulaInDice, replaceInFormula, replaceUnknown, resolveFormulaHint, roll, splitDiceComment, standardizeDice, templateSchema, testDiceRegistered, testStatCombinaison, verifyStatMatcherPattern, verifyTemplateValue };
|
package/dist/index.js
CHANGED
|
@@ -41,7 +41,6 @@ __export(index_exports, {
|
|
|
41
41
|
createCriticalCustom: () => createCriticalCustom,
|
|
42
42
|
diceRandomParse: () => diceRandomParse,
|
|
43
43
|
diceTypeRandomParse: () => diceTypeRandomParse,
|
|
44
|
-
escapeRegex: () => escapeRegex,
|
|
45
44
|
evalCombinaison: () => evalCombinaison,
|
|
46
45
|
evalOneCombinaison: () => evalOneCombinaison,
|
|
47
46
|
evalStatsDice: () => evalStatsDice,
|
|
@@ -240,6 +239,7 @@ var templateSchema = import_zod.z.object({
|
|
|
240
239
|
});
|
|
241
240
|
|
|
242
241
|
// src/regex.ts
|
|
242
|
+
var REGEX_CACHE_MAX = 500;
|
|
243
243
|
var regexCache = /* @__PURE__ */ new Map();
|
|
244
244
|
var NORMALIZE_SINGLE_DICE = (str) => str.replace(/\b1d(\d+)/gi, "d$1");
|
|
245
245
|
var REMOVER_PATTERN = {
|
|
@@ -254,6 +254,10 @@ function getCachedRegex(pattern, flags = "") {
|
|
|
254
254
|
const key = `${pattern}|${flags}`;
|
|
255
255
|
let regex = regexCache.get(key);
|
|
256
256
|
if (!regex) {
|
|
257
|
+
if (regexCache.size >= REGEX_CACHE_MAX) {
|
|
258
|
+
const oldest = regexCache.keys().next().value;
|
|
259
|
+
if (oldest !== void 0) regexCache.delete(oldest);
|
|
260
|
+
}
|
|
257
261
|
regex = new RegExp(pattern, flags);
|
|
258
262
|
regexCache.set(key, regex);
|
|
259
263
|
}
|
|
@@ -299,12 +303,12 @@ var import_rpg_dice_roller2 = require("@dice-roller/rpg-dice-roller");
|
|
|
299
303
|
function evalStatsDice(testDice, allStats, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto, pity) {
|
|
300
304
|
let dice = testDice.trimEnd();
|
|
301
305
|
if (allStats && Object.keys(allStats).length > 0) {
|
|
306
|
+
dice = dice.standardize();
|
|
302
307
|
const names = Object.keys(allStats);
|
|
303
308
|
for (const name of names) {
|
|
304
|
-
const regex =
|
|
305
|
-
if (dice.
|
|
306
|
-
|
|
307
|
-
dice = dice.standardize().replace(regex, statValue.toString()).trimEnd();
|
|
309
|
+
const regex = getCachedRegex(name.standardize().escapeRegex(), "gi");
|
|
310
|
+
if (dice.match(regex)) {
|
|
311
|
+
dice = dice.replace(regex, allStats[name].toString()).trimEnd();
|
|
308
312
|
}
|
|
309
313
|
}
|
|
310
314
|
}
|
|
@@ -322,8 +326,8 @@ function diceRandomParse(value, template, engine = import_rpg_dice_roller2.Numbe
|
|
|
322
326
|
const statNames = Object.keys(template.statistics);
|
|
323
327
|
let newDice = value;
|
|
324
328
|
for (const name of statNames) {
|
|
325
|
-
const regex =
|
|
326
|
-
if (
|
|
329
|
+
const regex = getCachedRegex(name.standardize().escapeRegex(), "gi");
|
|
330
|
+
if (newDice.match(regex)) {
|
|
327
331
|
let max;
|
|
328
332
|
let min;
|
|
329
333
|
const foundStat = template.statistics?.[name];
|
|
@@ -333,7 +337,7 @@ function diceRandomParse(value, template, engine = import_rpg_dice_roller2.Numbe
|
|
|
333
337
|
}
|
|
334
338
|
const total = template.total || 100;
|
|
335
339
|
const randomStatValue = generateRandomStat(total, max, min, engine);
|
|
336
|
-
newDice =
|
|
340
|
+
newDice = newDice.replace(regex, randomStatValue.toString());
|
|
337
341
|
}
|
|
338
342
|
}
|
|
339
343
|
return replaceFormulaInDice(newDice);
|
|
@@ -356,7 +360,7 @@ function evalCombinaison(combinaison, stats) {
|
|
|
356
360
|
for (const [stat, combin] of Object.entries(combinaison)) {
|
|
357
361
|
let formula = combin.standardize();
|
|
358
362
|
for (const [statName, value] of Object.entries(stats)) {
|
|
359
|
-
const regex =
|
|
363
|
+
const regex = getCachedRegex(statName.standardize().escapeRegex(), "gi");
|
|
360
364
|
formula = formula.replace(regex, value.toString());
|
|
361
365
|
}
|
|
362
366
|
try {
|
|
@@ -370,7 +374,7 @@ function evalCombinaison(combinaison, stats) {
|
|
|
370
374
|
function evalOneCombinaison(combinaison, stats) {
|
|
371
375
|
let formula = combinaison.standardize();
|
|
372
376
|
for (const [statName, value] of Object.entries(stats)) {
|
|
373
|
-
const regex =
|
|
377
|
+
const regex = getCachedRegex(statName.standardize().escapeRegex(), "gi");
|
|
374
378
|
formula = formula.replace(regex, value.toString());
|
|
375
379
|
}
|
|
376
380
|
try {
|
|
@@ -418,7 +422,7 @@ function verifyTemplateValue(template, verify = true, engine = import_rpg_dice_r
|
|
|
418
422
|
engine
|
|
419
423
|
);
|
|
420
424
|
const rolled = roll(cleanedDice2, engine);
|
|
421
|
-
if (!rolled) throw new DiceTypeError(cleanedDice2, "
|
|
425
|
+
if (!rolled) throw new DiceTypeError(cleanedDice2, "roll");
|
|
422
426
|
}
|
|
423
427
|
if (statistiqueTemplate.customCritical) {
|
|
424
428
|
if (!statistiqueTemplate.diceType) {
|
|
@@ -443,15 +447,16 @@ function verifyTemplateValue(template, verify = true, engine = import_rpg_dice_r
|
|
|
443
447
|
}
|
|
444
448
|
function testDiceRegistered(template, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto) {
|
|
445
449
|
if (!template.damage) return;
|
|
446
|
-
|
|
447
|
-
if (
|
|
448
|
-
|
|
450
|
+
const damageEntries = Object.entries(template.damage);
|
|
451
|
+
if (damageEntries.length === 0) throw new EmptyObjectError();
|
|
452
|
+
if (damageEntries.length > 25) throw new TooManyDice();
|
|
453
|
+
for (const [name, dice] of damageEntries) {
|
|
449
454
|
if (!dice) continue;
|
|
450
455
|
const diceReplaced = replaceExpByRandom(dice);
|
|
451
456
|
const randomDiceParsed = diceRandomParse(diceReplaced, template, engine);
|
|
452
457
|
try {
|
|
453
458
|
const rolled = roll(randomDiceParsed, engine);
|
|
454
|
-
if (!rolled) throw new DiceTypeError(name, "
|
|
459
|
+
if (!rolled) throw new DiceTypeError(name, "testDiceRegistered", dice);
|
|
455
460
|
} catch (error) {
|
|
456
461
|
throw new DiceTypeError(name, "testDiceRegistered", error);
|
|
457
462
|
}
|
|
@@ -459,19 +464,14 @@ function testDiceRegistered(template, engine = import_rpg_dice_roller2.NumberGen
|
|
|
459
464
|
}
|
|
460
465
|
function testStatCombinaison(template, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto) {
|
|
461
466
|
if (!template.statistics) return;
|
|
462
|
-
const onlycombinaisonStats =
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
)
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
Object.entries(template.statistics).filter(([_, value]) => !value.combinaison)
|
|
469
|
-
);
|
|
467
|
+
const onlycombinaisonStats = {};
|
|
468
|
+
const allOtherStats = {};
|
|
469
|
+
for (const [k, v] of Object.entries(template.statistics)) {
|
|
470
|
+
if (v.combinaison !== void 0) onlycombinaisonStats[k] = v;
|
|
471
|
+
else allOtherStats[k] = v;
|
|
472
|
+
}
|
|
470
473
|
if (Object.keys(onlycombinaisonStats).length === 0) return;
|
|
471
|
-
|
|
472
|
-
(stat) => !template.statistics[stat].combinaison
|
|
473
|
-
);
|
|
474
|
-
if (allStats.length === 0) throw new NoStatisticsError();
|
|
474
|
+
if (Object.keys(allOtherStats).length === 0) throw new NoStatisticsError();
|
|
475
475
|
const error = [];
|
|
476
476
|
for (const [stat, value] of Object.entries(onlycombinaisonStats)) {
|
|
477
477
|
let formula = value.combinaison;
|
|
@@ -479,7 +479,7 @@ function testStatCombinaison(template, engine = import_rpg_dice_roller2.NumberGe
|
|
|
479
479
|
const { max, min } = data;
|
|
480
480
|
const total = template.total || 100;
|
|
481
481
|
const randomStatValue = generateRandomStat(total, max, min, engine);
|
|
482
|
-
const regex =
|
|
482
|
+
const regex = getCachedRegex(other.escapeRegex(), "gi");
|
|
483
483
|
formula = formula.replace(regex, randomStatValue.toString());
|
|
484
484
|
}
|
|
485
485
|
try {
|
|
@@ -492,27 +492,21 @@ function testStatCombinaison(template, engine = import_rpg_dice_roller2.NumberGe
|
|
|
492
492
|
return;
|
|
493
493
|
}
|
|
494
494
|
function generateRandomStat(total = 100, max, min, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto) {
|
|
495
|
-
|
|
495
|
+
if (total <= 1) throw new RangeError(`total must be greater than 1, got ${total}`);
|
|
496
496
|
const random = new import_random_js.Random(engine || import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto);
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
else randomStatValue = randomInt(1, total, engine, random);
|
|
502
|
-
}
|
|
503
|
-
return randomStatValue;
|
|
497
|
+
const effectiveMin = Math.max(min ?? 1, 1);
|
|
498
|
+
const effectiveMax = Math.min(max ?? total - 1, total - 1);
|
|
499
|
+
if (effectiveMin > effectiveMax) throw new MaxGreater(effectiveMin, effectiveMax);
|
|
500
|
+
return randomInt(effectiveMin, effectiveMax, engine, random);
|
|
504
501
|
}
|
|
505
502
|
|
|
506
503
|
// src/utils.ts
|
|
507
504
|
function splitDiceComment(dice) {
|
|
508
|
-
const match = /\s
|
|
505
|
+
const match = /\s(#|\/{2}|\[|\/\*)(?<comment>.*)/i.exec(dice);
|
|
509
506
|
if (!match?.groups) return { dice: dice.trimEnd(), comment: void 0 };
|
|
510
507
|
const comment = match.groups.comment.trim() || void 0;
|
|
511
508
|
return { dice: dice.slice(0, match.index).trimEnd(), comment };
|
|
512
509
|
}
|
|
513
|
-
function escapeRegex(string) {
|
|
514
|
-
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
515
|
-
}
|
|
516
510
|
function standardizeDice(dice) {
|
|
517
511
|
return dice.replace(
|
|
518
512
|
/(\[[^\]]+])|([^[]+)/g,
|
|
@@ -657,32 +651,31 @@ var import_rpg_dice_roller5 = require("@dice-roller/rpg-dice-roller");
|
|
|
657
651
|
var import_mathjs3 = require("mathjs");
|
|
658
652
|
|
|
659
653
|
// src/dice/replace.ts
|
|
654
|
+
var PARENTHESIS_REGEX = /d\((\d+)\)/g;
|
|
655
|
+
var BRACKET_COMMENT_REGEX = /\[(?<comments>.*?)\]/;
|
|
656
|
+
var OPTIONAL_COMMENT_REGEX = /\s+(#|\/\/)(?<comment>.*)/;
|
|
660
657
|
function replaceUnwantedText(dice, sortOrder) {
|
|
661
|
-
|
|
662
|
-
if (sortOrder)
|
|
658
|
+
let d = dice.replaceAll(/[{}]/g, "").replaceAll(/s[ad]/gi, "");
|
|
659
|
+
if (sortOrder) d = sortDice(d, sortOrder);
|
|
660
|
+
if (!d.length) throw new DiceTypeError(dice, "empty_dice");
|
|
663
661
|
return d;
|
|
664
662
|
}
|
|
665
663
|
function sortDice(dice, sortOrder) {
|
|
666
664
|
if (sortOrder === "none" /* None */) return dice;
|
|
667
665
|
const dices = dice.split(/; ?/);
|
|
666
|
+
const decorated = dices.map((d) => ({
|
|
667
|
+
d,
|
|
668
|
+
v: Number.parseInt(d.split("= ")[1], 10) || 0
|
|
669
|
+
}));
|
|
668
670
|
if (sortOrder === "sa" /* Ascending */) {
|
|
669
|
-
|
|
670
|
-
const totalA = Number.parseInt(a.split("= ")[1], 10) || 0;
|
|
671
|
-
const totalB = Number.parseInt(b.split("= ")[1], 10) || 0;
|
|
672
|
-
return totalB - totalA;
|
|
673
|
-
});
|
|
671
|
+
decorated.sort((a, b) => b.v - a.v);
|
|
674
672
|
} else if (sortOrder === "sd" /* Descending */) {
|
|
675
|
-
|
|
676
|
-
const totalA = Number.parseInt(a.split("= ")[1], 10) || 0;
|
|
677
|
-
const totalB = Number.parseInt(b.split("= ")[1], 10) || 0;
|
|
678
|
-
return totalA - totalB;
|
|
679
|
-
});
|
|
673
|
+
decorated.sort((a, b) => a.v - b.v);
|
|
680
674
|
}
|
|
681
|
-
return
|
|
675
|
+
return decorated.map((x) => x.d).join("; ");
|
|
682
676
|
}
|
|
683
677
|
function fixParenthesis(dice) {
|
|
684
|
-
|
|
685
|
-
return dice.replaceAll(parenthesisRegex, (_match, p1) => `d${p1}`);
|
|
678
|
+
return dice.replaceAll(PARENTHESIS_REGEX, (_match, p1) => `d${p1}`);
|
|
686
679
|
}
|
|
687
680
|
function replaceText(element, total, dice) {
|
|
688
681
|
return {
|
|
@@ -691,12 +684,10 @@ function replaceText(element, total, dice) {
|
|
|
691
684
|
};
|
|
692
685
|
}
|
|
693
686
|
function formatComment(dice) {
|
|
694
|
-
const
|
|
695
|
-
const commentsMatch = commentsRegex.exec(dice);
|
|
687
|
+
const commentsMatch = BRACKET_COMMENT_REGEX.exec(dice);
|
|
696
688
|
const comments = commentsMatch?.groups?.comments ? `${commentsMatch.groups.comments}` : "";
|
|
697
|
-
const diceWithoutBrackets = dice.replace(
|
|
698
|
-
const
|
|
699
|
-
const optionalComments = optionalCommentsRegex.exec(diceWithoutBrackets);
|
|
689
|
+
const diceWithoutBrackets = dice.replace(BRACKET_COMMENT_REGEX, "");
|
|
690
|
+
const optionalComments = OPTIONAL_COMMENT_REGEX.exec(diceWithoutBrackets);
|
|
700
691
|
const optional = optionalComments?.groups?.comment ? `${optionalComments.groups.comment.trim()}` : "";
|
|
701
692
|
let finalComment = "";
|
|
702
693
|
if (comments && optional) finalComment = `__${comments} ${optional}__ \u2014 `;
|
|
@@ -848,23 +839,27 @@ function calculateSimilarity(str1, str2) {
|
|
|
848
839
|
return (longer.length - distance) / longer.length;
|
|
849
840
|
}
|
|
850
841
|
function levenshteinDistance(str1, str2) {
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
842
|
+
if (str1.length > str2.length) {
|
|
843
|
+
[str1, str2] = [str2, str1];
|
|
844
|
+
}
|
|
845
|
+
let prev = Array.from({ length: str1.length + 1 }, (_, i) => i);
|
|
846
|
+
let curr = new Array(str1.length + 1);
|
|
854
847
|
for (let j = 1; j <= str2.length; j++) {
|
|
848
|
+
curr[0] = j;
|
|
855
849
|
for (let i = 1; i <= str1.length; i++) {
|
|
856
850
|
const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;
|
|
857
|
-
|
|
858
|
-
|
|
851
|
+
curr[i] = Math.min(
|
|
852
|
+
curr[i - 1] + 1,
|
|
859
853
|
// insertion
|
|
860
|
-
|
|
854
|
+
prev[i] + 1,
|
|
861
855
|
// deletion
|
|
862
|
-
|
|
856
|
+
prev[i - 1] + cost
|
|
863
857
|
// substitution
|
|
864
858
|
);
|
|
865
859
|
}
|
|
860
|
+
[prev, curr] = [curr, prev];
|
|
866
861
|
}
|
|
867
|
-
return
|
|
862
|
+
return prev[str1.length];
|
|
868
863
|
}
|
|
869
864
|
function findBestStatMatch(searchTerm, normalizedStats, similarityThreshold = MIN_THRESHOLD_MATCH) {
|
|
870
865
|
const exact = normalizedStats.get(searchTerm);
|
|
@@ -1141,25 +1136,20 @@ function prepareDice(diceInput) {
|
|
|
1141
1136
|
dice = dice.replaceAll(REMOVER_PATTERN.CRITICAL_BLOCK, "").trimEnd();
|
|
1142
1137
|
const explodingSuccess = normalizeExplodingSuccess(dice);
|
|
1143
1138
|
if (explodingSuccess) dice = explodingSuccess.dice;
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
diceDisplay = explodingSuccess?.originalDice ?? mainDice;
|
|
1148
|
-
} else {
|
|
1149
|
-
diceDisplay = explodingSuccess?.originalDice ?? dice;
|
|
1150
|
-
}
|
|
1139
|
+
const sharedSeparatorIndex = dice.indexOf(";");
|
|
1140
|
+
const hasSharedSeparator = sharedSeparatorIndex !== -1;
|
|
1141
|
+
let diceDisplay = explodingSuccess?.originalDice ?? (hasSharedSeparator ? dice.slice(0, sharedSeparatorIndex) : dice);
|
|
1151
1142
|
const curlyBulkMatch = dice.match(/^\{(\d+#.*)\}$/);
|
|
1152
1143
|
const isCurlyBulk = !!curlyBulkMatch;
|
|
1153
1144
|
const bulkContent = isCurlyBulk ? curlyBulkMatch[1] : "";
|
|
1154
|
-
const isSharedRoll = dice.includes(";");
|
|
1155
1145
|
let isSharedCurly = false;
|
|
1156
|
-
if (
|
|
1146
|
+
if (hasSharedSeparator && dice.match(/^\{.*;\s*.*\}$/)) {
|
|
1157
1147
|
dice = dice.slice(1, -1);
|
|
1158
1148
|
isSharedCurly = true;
|
|
1159
1149
|
diceDisplay = diceDisplay.slice(1);
|
|
1160
1150
|
}
|
|
1161
1151
|
let isSimpleCurly = false;
|
|
1162
|
-
if (!isCurlyBulk && !
|
|
1152
|
+
if (!isCurlyBulk && !hasSharedSeparator && dice.match(/^\{.*\}$/)) {
|
|
1163
1153
|
const innerContent = dice.slice(1, -1);
|
|
1164
1154
|
const hasModifiers = innerContent.match(/[+\-*/%^]/);
|
|
1165
1155
|
const hasComparison = innerContent.match(/(([><=!]+\d+f)|([><=]|!=)+\d+)/);
|
|
@@ -1172,7 +1162,7 @@ function prepareDice(diceInput) {
|
|
|
1172
1162
|
dice,
|
|
1173
1163
|
diceDisplay,
|
|
1174
1164
|
explodingSuccess,
|
|
1175
|
-
isSharedRoll,
|
|
1165
|
+
isSharedRoll: hasSharedSeparator,
|
|
1176
1166
|
isSharedCurly,
|
|
1177
1167
|
isCurlyBulk,
|
|
1178
1168
|
bulkContent,
|
|
@@ -1189,7 +1179,13 @@ function getSortOrder(dice) {
|
|
|
1189
1179
|
function handleBulkRolls(dice, isCurlyBulk, bulkContent, compare, explodingSuccess, diceDisplay, engine, sort) {
|
|
1190
1180
|
const bulkProcessContent = isCurlyBulk ? bulkContent : dice;
|
|
1191
1181
|
const diceArray = bulkProcessContent.split("#");
|
|
1182
|
+
if (!isNumber(diceArray[0])) {
|
|
1183
|
+
throw new DiceTypeError(dice, "bulk_number");
|
|
1184
|
+
}
|
|
1192
1185
|
const numberOfDice = Number.parseInt(diceArray[0], 10);
|
|
1186
|
+
if (numberOfDice <= 0) {
|
|
1187
|
+
throw new DiceTypeError(dice, "bulk_zero");
|
|
1188
|
+
}
|
|
1193
1189
|
const { dice: diceToRollBase, comment: comments } = splitDiceComment(diceArray[1]);
|
|
1194
1190
|
let diceToRoll = diceToRollBase;
|
|
1195
1191
|
let curlyCompare;
|
|
@@ -1372,6 +1368,7 @@ function handlePitySystem(dice, compare, diceRoll, roller, engine) {
|
|
|
1372
1368
|
isFail = (0, import_mathjs9.evaluate)(`${res.total}${compare.sign}${compare.value}`);
|
|
1373
1369
|
}
|
|
1374
1370
|
}
|
|
1371
|
+
if (!res?.result.length) throw new DiceTypeError(dice, "empty_dice");
|
|
1375
1372
|
return { rerollCount, result: res };
|
|
1376
1373
|
}
|
|
1377
1374
|
|
|
@@ -1473,6 +1470,7 @@ function roll(dice, engine = import_rpg_dice_roller8.NumberGenerator.engines.nod
|
|
|
1473
1470
|
prepared.explodingSuccess.normalizedSegment,
|
|
1474
1471
|
prepared.explodingSuccess.originalSegment
|
|
1475
1472
|
);
|
|
1473
|
+
if (!resultOutput.length) throw new DiceTypeError(dice, "empty_dice");
|
|
1476
1474
|
return {
|
|
1477
1475
|
dice: prepared.isSimpleCurly ? finalDiceDisplay : prepared.diceDisplay,
|
|
1478
1476
|
result: resultOutput,
|
|
@@ -1683,7 +1681,6 @@ function replaceInFormula(element, diceResult, compareResult, res, engine = impo
|
|
|
1683
1681
|
createCriticalCustom,
|
|
1684
1682
|
diceRandomParse,
|
|
1685
1683
|
diceTypeRandomParse,
|
|
1686
|
-
escapeRegex,
|
|
1687
1684
|
evalCombinaison,
|
|
1688
1685
|
evalOneCombinaison,
|
|
1689
1686
|
evalStatsDice,
|