@dicelette/core 1.28.2 → 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 +15 -6
- package/dist/index.d.ts +15 -6
- package/dist/index.js +96 -94
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +95 -93
- 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
|
@@ -312,9 +312,10 @@ declare function includeDiceType(dice: string, diceType?: string, userStats?: bo
|
|
|
312
312
|
* @param {Engine|null} engine The random engine to use, default to nodeCrypto
|
|
313
313
|
* @param {boolean} pity Whether to enable pity system (reroll on failure) or not
|
|
314
314
|
* @param {boolean} sort Whether to sort the dice results or not
|
|
315
|
+
* @param {string} comment Optional comment to attach to the result. If provided, skips extracting the comment from the dice string (assumes dice is already clean).
|
|
315
316
|
* @returns {Resultat|undefined} The result of the roll
|
|
316
317
|
*/
|
|
317
|
-
declare function roll(dice: string, engine?: Engine | null, pity?: boolean, sort?: SortOrder): Resultat | undefined;
|
|
318
|
+
declare function roll(dice: string, engine?: Engine | null, pity?: boolean, sort?: SortOrder, comment?: string): Resultat | undefined;
|
|
318
319
|
declare function replaceInFormula(element: string, diceResult: Resultat, compareResult: {
|
|
319
320
|
dice: string;
|
|
320
321
|
compare: Compare | undefined;
|
|
@@ -354,6 +355,7 @@ declare function resolveFormulaHint(formula: string, allAttributes: Record<strin
|
|
|
354
355
|
declare function calculateSimilarity(str1: string, str2: string): number;
|
|
355
356
|
/**
|
|
356
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).
|
|
357
359
|
*/
|
|
358
360
|
declare function levenshteinDistance(str1: string, str2: string): number;
|
|
359
361
|
declare function findBestStatMatch<T>(searchTerm: string, normalizedStats: Map<string, T>, similarityThreshold?: number): T | undefined;
|
|
@@ -368,10 +370,17 @@ declare function replaceUnknown(dice: string, replacer: string): string;
|
|
|
368
370
|
declare function verifyStatMatcherPattern(dice: string, replaceUnknow?: string): string;
|
|
369
371
|
|
|
370
372
|
/**
|
|
371
|
-
*
|
|
372
|
-
*
|
|
373
|
+
* Splits a dice string into the dice expression and its trailing comment.
|
|
374
|
+
* Comments are preceded by whitespace and start with #, //, [, or /*.
|
|
375
|
+
* The returned comment does NOT include the marker prefix.
|
|
376
|
+
* @example
|
|
377
|
+
* splitDiceComment("1d6 # attack") // => { dice: "1d6", comment: "attack" }
|
|
378
|
+
* splitDiceComment("2d8+3") // => { dice: "2d8+3", comment: undefined }
|
|
373
379
|
*/
|
|
374
|
-
declare function
|
|
380
|
+
declare function splitDiceComment(dice: string): {
|
|
381
|
+
dice: string;
|
|
382
|
+
comment: string | undefined;
|
|
383
|
+
};
|
|
375
384
|
/**
|
|
376
385
|
* Allow to keep the text as if in brackets
|
|
377
386
|
* @param dice {string}
|
|
@@ -450,7 +459,7 @@ declare function evalOneCombinaison(combinaison: string, stats: Record<string, n
|
|
|
450
459
|
/**
|
|
451
460
|
* Parse the provided JSON and verify each field to check if everything could work when rolling
|
|
452
461
|
* @param {unknown} template
|
|
453
|
-
* @param
|
|
462
|
+
* @param verify - If true, will roll the dices to check if everything is valid
|
|
454
463
|
* @param engine
|
|
455
464
|
* @returns {StatisticalTemplate}
|
|
456
465
|
*/
|
|
@@ -477,4 +486,4 @@ declare function testStatCombinaison(template: StatisticalTemplate, engine?: Eng
|
|
|
477
486
|
*/
|
|
478
487
|
declare function generateRandomStat(total?: number | undefined, max?: number, min?: number, engine?: Engine | null): number;
|
|
479
488
|
|
|
480
|
-
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
|
@@ -312,9 +312,10 @@ declare function includeDiceType(dice: string, diceType?: string, userStats?: bo
|
|
|
312
312
|
* @param {Engine|null} engine The random engine to use, default to nodeCrypto
|
|
313
313
|
* @param {boolean} pity Whether to enable pity system (reroll on failure) or not
|
|
314
314
|
* @param {boolean} sort Whether to sort the dice results or not
|
|
315
|
+
* @param {string} comment Optional comment to attach to the result. If provided, skips extracting the comment from the dice string (assumes dice is already clean).
|
|
315
316
|
* @returns {Resultat|undefined} The result of the roll
|
|
316
317
|
*/
|
|
317
|
-
declare function roll(dice: string, engine?: Engine | null, pity?: boolean, sort?: SortOrder): Resultat | undefined;
|
|
318
|
+
declare function roll(dice: string, engine?: Engine | null, pity?: boolean, sort?: SortOrder, comment?: string): Resultat | undefined;
|
|
318
319
|
declare function replaceInFormula(element: string, diceResult: Resultat, compareResult: {
|
|
319
320
|
dice: string;
|
|
320
321
|
compare: Compare | undefined;
|
|
@@ -354,6 +355,7 @@ declare function resolveFormulaHint(formula: string, allAttributes: Record<strin
|
|
|
354
355
|
declare function calculateSimilarity(str1: string, str2: string): number;
|
|
355
356
|
/**
|
|
356
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).
|
|
357
359
|
*/
|
|
358
360
|
declare function levenshteinDistance(str1: string, str2: string): number;
|
|
359
361
|
declare function findBestStatMatch<T>(searchTerm: string, normalizedStats: Map<string, T>, similarityThreshold?: number): T | undefined;
|
|
@@ -368,10 +370,17 @@ declare function replaceUnknown(dice: string, replacer: string): string;
|
|
|
368
370
|
declare function verifyStatMatcherPattern(dice: string, replaceUnknow?: string): string;
|
|
369
371
|
|
|
370
372
|
/**
|
|
371
|
-
*
|
|
372
|
-
*
|
|
373
|
+
* Splits a dice string into the dice expression and its trailing comment.
|
|
374
|
+
* Comments are preceded by whitespace and start with #, //, [, or /*.
|
|
375
|
+
* The returned comment does NOT include the marker prefix.
|
|
376
|
+
* @example
|
|
377
|
+
* splitDiceComment("1d6 # attack") // => { dice: "1d6", comment: "attack" }
|
|
378
|
+
* splitDiceComment("2d8+3") // => { dice: "2d8+3", comment: undefined }
|
|
373
379
|
*/
|
|
374
|
-
declare function
|
|
380
|
+
declare function splitDiceComment(dice: string): {
|
|
381
|
+
dice: string;
|
|
382
|
+
comment: string | undefined;
|
|
383
|
+
};
|
|
375
384
|
/**
|
|
376
385
|
* Allow to keep the text as if in brackets
|
|
377
386
|
* @param dice {string}
|
|
@@ -450,7 +459,7 @@ declare function evalOneCombinaison(combinaison: string, stats: Record<string, n
|
|
|
450
459
|
/**
|
|
451
460
|
* Parse the provided JSON and verify each field to check if everything could work when rolling
|
|
452
461
|
* @param {unknown} template
|
|
453
|
-
* @param
|
|
462
|
+
* @param verify - If true, will roll the dices to check if everything is valid
|
|
454
463
|
* @param engine
|
|
455
464
|
* @returns {StatisticalTemplate}
|
|
456
465
|
*/
|
|
@@ -477,4 +486,4 @@ declare function testStatCombinaison(template: StatisticalTemplate, engine?: Eng
|
|
|
477
486
|
*/
|
|
478
487
|
declare function generateRandomStat(total?: number | undefined, max?: number, min?: number, engine?: Engine | null): number;
|
|
479
488
|
|
|
480
|
-
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,
|
|
@@ -62,6 +61,7 @@ __export(index_exports, {
|
|
|
62
61
|
replaceUnknown: () => replaceUnknown,
|
|
63
62
|
resolveFormulaHint: () => resolveFormulaHint,
|
|
64
63
|
roll: () => roll,
|
|
64
|
+
splitDiceComment: () => splitDiceComment,
|
|
65
65
|
standardizeDice: () => standardizeDice,
|
|
66
66
|
templateSchema: () => templateSchema,
|
|
67
67
|
testDiceRegistered: () => testDiceRegistered,
|
|
@@ -239,6 +239,7 @@ var templateSchema = import_zod.z.object({
|
|
|
239
239
|
});
|
|
240
240
|
|
|
241
241
|
// src/regex.ts
|
|
242
|
+
var REGEX_CACHE_MAX = 500;
|
|
242
243
|
var regexCache = /* @__PURE__ */ new Map();
|
|
243
244
|
var NORMALIZE_SINGLE_DICE = (str) => str.replace(/\b1d(\d+)/gi, "d$1");
|
|
244
245
|
var REMOVER_PATTERN = {
|
|
@@ -253,6 +254,10 @@ function getCachedRegex(pattern, flags = "") {
|
|
|
253
254
|
const key = `${pattern}|${flags}`;
|
|
254
255
|
let regex = regexCache.get(key);
|
|
255
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
|
+
}
|
|
256
261
|
regex = new RegExp(pattern, flags);
|
|
257
262
|
regexCache.set(key, regex);
|
|
258
263
|
}
|
|
@@ -285,10 +290,6 @@ var import_mathjs10 = require("mathjs");
|
|
|
285
290
|
var import_rpg_dice_roller7 = require("@dice-roller/rpg-dice-roller");
|
|
286
291
|
var import_mathjs8 = require("mathjs");
|
|
287
292
|
|
|
288
|
-
// src/dice/compare.ts
|
|
289
|
-
var import_rpg_dice_roller4 = require("@dice-roller/rpg-dice-roller");
|
|
290
|
-
var import_mathjs2 = require("mathjs");
|
|
291
|
-
|
|
292
293
|
// src/utils.ts
|
|
293
294
|
var import_uniformize2 = require("uniformize");
|
|
294
295
|
var import_rpg_dice_roller3 = require("@dice-roller/rpg-dice-roller");
|
|
@@ -302,12 +303,12 @@ var import_rpg_dice_roller2 = require("@dice-roller/rpg-dice-roller");
|
|
|
302
303
|
function evalStatsDice(testDice, allStats, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto, pity) {
|
|
303
304
|
let dice = testDice.trimEnd();
|
|
304
305
|
if (allStats && Object.keys(allStats).length > 0) {
|
|
306
|
+
dice = dice.standardize();
|
|
305
307
|
const names = Object.keys(allStats);
|
|
306
308
|
for (const name of names) {
|
|
307
|
-
const regex =
|
|
308
|
-
if (dice.
|
|
309
|
-
|
|
310
|
-
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();
|
|
311
312
|
}
|
|
312
313
|
}
|
|
313
314
|
}
|
|
@@ -325,8 +326,8 @@ function diceRandomParse(value, template, engine = import_rpg_dice_roller2.Numbe
|
|
|
325
326
|
const statNames = Object.keys(template.statistics);
|
|
326
327
|
let newDice = value;
|
|
327
328
|
for (const name of statNames) {
|
|
328
|
-
const regex =
|
|
329
|
-
if (
|
|
329
|
+
const regex = getCachedRegex(name.standardize().escapeRegex(), "gi");
|
|
330
|
+
if (newDice.match(regex)) {
|
|
330
331
|
let max;
|
|
331
332
|
let min;
|
|
332
333
|
const foundStat = template.statistics?.[name];
|
|
@@ -336,7 +337,7 @@ function diceRandomParse(value, template, engine = import_rpg_dice_roller2.Numbe
|
|
|
336
337
|
}
|
|
337
338
|
const total = template.total || 100;
|
|
338
339
|
const randomStatValue = generateRandomStat(total, max, min, engine);
|
|
339
|
-
newDice =
|
|
340
|
+
newDice = newDice.replace(regex, randomStatValue.toString());
|
|
340
341
|
}
|
|
341
342
|
}
|
|
342
343
|
return replaceFormulaInDice(newDice);
|
|
@@ -359,7 +360,7 @@ function evalCombinaison(combinaison, stats) {
|
|
|
359
360
|
for (const [stat, combin] of Object.entries(combinaison)) {
|
|
360
361
|
let formula = combin.standardize();
|
|
361
362
|
for (const [statName, value] of Object.entries(stats)) {
|
|
362
|
-
const regex =
|
|
363
|
+
const regex = getCachedRegex(statName.standardize().escapeRegex(), "gi");
|
|
363
364
|
formula = formula.replace(regex, value.toString());
|
|
364
365
|
}
|
|
365
366
|
try {
|
|
@@ -373,7 +374,7 @@ function evalCombinaison(combinaison, stats) {
|
|
|
373
374
|
function evalOneCombinaison(combinaison, stats) {
|
|
374
375
|
let formula = combinaison.standardize();
|
|
375
376
|
for (const [statName, value] of Object.entries(stats)) {
|
|
376
|
-
const regex =
|
|
377
|
+
const regex = getCachedRegex(statName.standardize().escapeRegex(), "gi");
|
|
377
378
|
formula = formula.replace(regex, value.toString());
|
|
378
379
|
}
|
|
379
380
|
try {
|
|
@@ -421,7 +422,7 @@ function verifyTemplateValue(template, verify = true, engine = import_rpg_dice_r
|
|
|
421
422
|
engine
|
|
422
423
|
);
|
|
423
424
|
const rolled = roll(cleanedDice2, engine);
|
|
424
|
-
if (!rolled) throw new DiceTypeError(cleanedDice2, "
|
|
425
|
+
if (!rolled) throw new DiceTypeError(cleanedDice2, "roll");
|
|
425
426
|
}
|
|
426
427
|
if (statistiqueTemplate.customCritical) {
|
|
427
428
|
if (!statistiqueTemplate.diceType) {
|
|
@@ -446,15 +447,16 @@ function verifyTemplateValue(template, verify = true, engine = import_rpg_dice_r
|
|
|
446
447
|
}
|
|
447
448
|
function testDiceRegistered(template, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto) {
|
|
448
449
|
if (!template.damage) return;
|
|
449
|
-
|
|
450
|
-
if (
|
|
451
|
-
|
|
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) {
|
|
452
454
|
if (!dice) continue;
|
|
453
455
|
const diceReplaced = replaceExpByRandom(dice);
|
|
454
456
|
const randomDiceParsed = diceRandomParse(diceReplaced, template, engine);
|
|
455
457
|
try {
|
|
456
458
|
const rolled = roll(randomDiceParsed, engine);
|
|
457
|
-
if (!rolled) throw new DiceTypeError(name, "
|
|
459
|
+
if (!rolled) throw new DiceTypeError(name, "testDiceRegistered", dice);
|
|
458
460
|
} catch (error) {
|
|
459
461
|
throw new DiceTypeError(name, "testDiceRegistered", error);
|
|
460
462
|
}
|
|
@@ -462,19 +464,14 @@ function testDiceRegistered(template, engine = import_rpg_dice_roller2.NumberGen
|
|
|
462
464
|
}
|
|
463
465
|
function testStatCombinaison(template, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto) {
|
|
464
466
|
if (!template.statistics) return;
|
|
465
|
-
const onlycombinaisonStats =
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
)
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
Object.entries(template.statistics).filter(([_, value]) => !value.combinaison)
|
|
472
|
-
);
|
|
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
|
+
}
|
|
473
473
|
if (Object.keys(onlycombinaisonStats).length === 0) return;
|
|
474
|
-
|
|
475
|
-
(stat) => !template.statistics[stat].combinaison
|
|
476
|
-
);
|
|
477
|
-
if (allStats.length === 0) throw new NoStatisticsError();
|
|
474
|
+
if (Object.keys(allOtherStats).length === 0) throw new NoStatisticsError();
|
|
478
475
|
const error = [];
|
|
479
476
|
for (const [stat, value] of Object.entries(onlycombinaisonStats)) {
|
|
480
477
|
let formula = value.combinaison;
|
|
@@ -482,7 +479,7 @@ function testStatCombinaison(template, engine = import_rpg_dice_roller2.NumberGe
|
|
|
482
479
|
const { max, min } = data;
|
|
483
480
|
const total = template.total || 100;
|
|
484
481
|
const randomStatValue = generateRandomStat(total, max, min, engine);
|
|
485
|
-
const regex =
|
|
482
|
+
const regex = getCachedRegex(other.escapeRegex(), "gi");
|
|
486
483
|
formula = formula.replace(regex, randomStatValue.toString());
|
|
487
484
|
}
|
|
488
485
|
try {
|
|
@@ -495,20 +492,20 @@ function testStatCombinaison(template, engine = import_rpg_dice_roller2.NumberGe
|
|
|
495
492
|
return;
|
|
496
493
|
}
|
|
497
494
|
function generateRandomStat(total = 100, max, min, engine = import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto) {
|
|
498
|
-
|
|
495
|
+
if (total <= 1) throw new RangeError(`total must be greater than 1, got ${total}`);
|
|
499
496
|
const random = new import_random_js.Random(engine || import_rpg_dice_roller2.NumberGenerator.engines.nodeCrypto);
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
else randomStatValue = randomInt(1, total, engine, random);
|
|
505
|
-
}
|
|
506
|
-
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);
|
|
507
501
|
}
|
|
508
502
|
|
|
509
503
|
// src/utils.ts
|
|
510
|
-
function
|
|
511
|
-
|
|
504
|
+
function splitDiceComment(dice) {
|
|
505
|
+
const match = /\s(#|\/{2}|\[|\/\*)(?<comment>.*)/i.exec(dice);
|
|
506
|
+
if (!match?.groups) return { dice: dice.trimEnd(), comment: void 0 };
|
|
507
|
+
const comment = match.groups.comment.trim() || void 0;
|
|
508
|
+
return { dice: dice.slice(0, match.index).trimEnd(), comment };
|
|
512
509
|
}
|
|
513
510
|
function standardizeDice(dice) {
|
|
514
511
|
return dice.replace(
|
|
@@ -543,6 +540,8 @@ function createCriticalCustom(dice, customCritical, template, engine = import_rp
|
|
|
543
540
|
}
|
|
544
541
|
|
|
545
542
|
// src/dice/compare.ts
|
|
543
|
+
var import_rpg_dice_roller4 = require("@dice-roller/rpg-dice-roller");
|
|
544
|
+
var import_mathjs2 = require("mathjs");
|
|
546
545
|
function isTrivialComparison(maxValue, minValue, compare) {
|
|
547
546
|
const canSucceed = canComparisonSucceed(maxValue, compare, minValue);
|
|
548
547
|
const canFail = canComparisonFail(maxValue, compare, minValue);
|
|
@@ -652,32 +651,31 @@ var import_rpg_dice_roller5 = require("@dice-roller/rpg-dice-roller");
|
|
|
652
651
|
var import_mathjs3 = require("mathjs");
|
|
653
652
|
|
|
654
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>.*)/;
|
|
655
657
|
function replaceUnwantedText(dice, sortOrder) {
|
|
656
|
-
|
|
657
|
-
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");
|
|
658
661
|
return d;
|
|
659
662
|
}
|
|
660
663
|
function sortDice(dice, sortOrder) {
|
|
661
664
|
if (sortOrder === "none" /* None */) return dice;
|
|
662
665
|
const dices = dice.split(/; ?/);
|
|
666
|
+
const decorated = dices.map((d) => ({
|
|
667
|
+
d,
|
|
668
|
+
v: Number.parseInt(d.split("= ")[1], 10) || 0
|
|
669
|
+
}));
|
|
663
670
|
if (sortOrder === "sa" /* Ascending */) {
|
|
664
|
-
|
|
665
|
-
const totalA = Number.parseInt(a.split("= ")[1], 10) || 0;
|
|
666
|
-
const totalB = Number.parseInt(b.split("= ")[1], 10) || 0;
|
|
667
|
-
return totalB - totalA;
|
|
668
|
-
});
|
|
671
|
+
decorated.sort((a, b) => b.v - a.v);
|
|
669
672
|
} else if (sortOrder === "sd" /* Descending */) {
|
|
670
|
-
|
|
671
|
-
const totalA = Number.parseInt(a.split("= ")[1], 10) || 0;
|
|
672
|
-
const totalB = Number.parseInt(b.split("= ")[1], 10) || 0;
|
|
673
|
-
return totalA - totalB;
|
|
674
|
-
});
|
|
673
|
+
decorated.sort((a, b) => a.v - b.v);
|
|
675
674
|
}
|
|
676
|
-
return
|
|
675
|
+
return decorated.map((x) => x.d).join("; ");
|
|
677
676
|
}
|
|
678
677
|
function fixParenthesis(dice) {
|
|
679
|
-
|
|
680
|
-
return dice.replaceAll(parenthesisRegex, (_match, p1) => `d${p1}`);
|
|
678
|
+
return dice.replaceAll(PARENTHESIS_REGEX, (_match, p1) => `d${p1}`);
|
|
681
679
|
}
|
|
682
680
|
function replaceText(element, total, dice) {
|
|
683
681
|
return {
|
|
@@ -686,12 +684,10 @@ function replaceText(element, total, dice) {
|
|
|
686
684
|
};
|
|
687
685
|
}
|
|
688
686
|
function formatComment(dice) {
|
|
689
|
-
const
|
|
690
|
-
const commentsMatch = commentsRegex.exec(dice);
|
|
687
|
+
const commentsMatch = BRACKET_COMMENT_REGEX.exec(dice);
|
|
691
688
|
const comments = commentsMatch?.groups?.comments ? `${commentsMatch.groups.comments}` : "";
|
|
692
|
-
const diceWithoutBrackets = dice.replace(
|
|
693
|
-
const
|
|
694
|
-
const optionalComments = optionalCommentsRegex.exec(diceWithoutBrackets);
|
|
689
|
+
const diceWithoutBrackets = dice.replace(BRACKET_COMMENT_REGEX, "");
|
|
690
|
+
const optionalComments = OPTIONAL_COMMENT_REGEX.exec(diceWithoutBrackets);
|
|
695
691
|
const optional = optionalComments?.groups?.comment ? `${optionalComments.groups.comment.trim()}` : "";
|
|
696
692
|
let finalComment = "";
|
|
697
693
|
if (comments && optional) finalComment = `__${comments} ${optional}__ \u2014 `;
|
|
@@ -843,23 +839,27 @@ function calculateSimilarity(str1, str2) {
|
|
|
843
839
|
return (longer.length - distance) / longer.length;
|
|
844
840
|
}
|
|
845
841
|
function levenshteinDistance(str1, str2) {
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
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);
|
|
849
847
|
for (let j = 1; j <= str2.length; j++) {
|
|
848
|
+
curr[0] = j;
|
|
850
849
|
for (let i = 1; i <= str1.length; i++) {
|
|
851
850
|
const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;
|
|
852
|
-
|
|
853
|
-
|
|
851
|
+
curr[i] = Math.min(
|
|
852
|
+
curr[i - 1] + 1,
|
|
854
853
|
// insertion
|
|
855
|
-
|
|
854
|
+
prev[i] + 1,
|
|
856
855
|
// deletion
|
|
857
|
-
|
|
856
|
+
prev[i - 1] + cost
|
|
858
857
|
// substitution
|
|
859
858
|
);
|
|
860
859
|
}
|
|
860
|
+
[prev, curr] = [curr, prev];
|
|
861
861
|
}
|
|
862
|
-
return
|
|
862
|
+
return prev[str1.length];
|
|
863
863
|
}
|
|
864
864
|
function findBestStatMatch(searchTerm, normalizedStats, similarityThreshold = MIN_THRESHOLD_MATCH) {
|
|
865
865
|
const exact = normalizedStats.get(searchTerm);
|
|
@@ -1133,28 +1133,23 @@ function setSortOrder(toRoll, sort) {
|
|
|
1133
1133
|
}
|
|
1134
1134
|
function prepareDice(diceInput) {
|
|
1135
1135
|
let dice = standardizeDice(replaceFormulaInDice(diceInput)).replace(/^\+/, "").replaceAll("=>", ">=").replaceAll("=<", "<=").trimStart();
|
|
1136
|
-
dice = dice.replaceAll(
|
|
1136
|
+
dice = dice.replaceAll(REMOVER_PATTERN.CRITICAL_BLOCK, "").trimEnd();
|
|
1137
1137
|
const explodingSuccess = normalizeExplodingSuccess(dice);
|
|
1138
1138
|
if (explodingSuccess) dice = explodingSuccess.dice;
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
diceDisplay = explodingSuccess?.originalDice ?? mainDice;
|
|
1143
|
-
} else {
|
|
1144
|
-
diceDisplay = explodingSuccess?.originalDice ?? dice;
|
|
1145
|
-
}
|
|
1139
|
+
const sharedSeparatorIndex = dice.indexOf(";");
|
|
1140
|
+
const hasSharedSeparator = sharedSeparatorIndex !== -1;
|
|
1141
|
+
let diceDisplay = explodingSuccess?.originalDice ?? (hasSharedSeparator ? dice.slice(0, sharedSeparatorIndex) : dice);
|
|
1146
1142
|
const curlyBulkMatch = dice.match(/^\{(\d+#.*)\}$/);
|
|
1147
1143
|
const isCurlyBulk = !!curlyBulkMatch;
|
|
1148
1144
|
const bulkContent = isCurlyBulk ? curlyBulkMatch[1] : "";
|
|
1149
|
-
const isSharedRoll = dice.includes(";");
|
|
1150
1145
|
let isSharedCurly = false;
|
|
1151
|
-
if (
|
|
1146
|
+
if (hasSharedSeparator && dice.match(/^\{.*;\s*.*\}$/)) {
|
|
1152
1147
|
dice = dice.slice(1, -1);
|
|
1153
1148
|
isSharedCurly = true;
|
|
1154
1149
|
diceDisplay = diceDisplay.slice(1);
|
|
1155
1150
|
}
|
|
1156
1151
|
let isSimpleCurly = false;
|
|
1157
|
-
if (!isCurlyBulk && !
|
|
1152
|
+
if (!isCurlyBulk && !hasSharedSeparator && dice.match(/^\{.*\}$/)) {
|
|
1158
1153
|
const innerContent = dice.slice(1, -1);
|
|
1159
1154
|
const hasModifiers = innerContent.match(/[+\-*/%^]/);
|
|
1160
1155
|
const hasComparison = innerContent.match(/(([><=!]+\d+f)|([><=]|!=)+\d+)/);
|
|
@@ -1167,7 +1162,7 @@ function prepareDice(diceInput) {
|
|
|
1167
1162
|
dice,
|
|
1168
1163
|
diceDisplay,
|
|
1169
1164
|
explodingSuccess,
|
|
1170
|
-
isSharedRoll,
|
|
1165
|
+
isSharedRoll: hasSharedSeparator,
|
|
1171
1166
|
isSharedCurly,
|
|
1172
1167
|
isCurlyBulk,
|
|
1173
1168
|
bulkContent,
|
|
@@ -1184,10 +1179,15 @@ function getSortOrder(dice) {
|
|
|
1184
1179
|
function handleBulkRolls(dice, isCurlyBulk, bulkContent, compare, explodingSuccess, diceDisplay, engine, sort) {
|
|
1185
1180
|
const bulkProcessContent = isCurlyBulk ? bulkContent : dice;
|
|
1186
1181
|
const diceArray = bulkProcessContent.split("#");
|
|
1182
|
+
if (!isNumber(diceArray[0])) {
|
|
1183
|
+
throw new DiceTypeError(dice, "bulk_number");
|
|
1184
|
+
}
|
|
1187
1185
|
const numberOfDice = Number.parseInt(diceArray[0], 10);
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1186
|
+
if (numberOfDice <= 0) {
|
|
1187
|
+
throw new DiceTypeError(dice, "bulk_zero");
|
|
1188
|
+
}
|
|
1189
|
+
const { dice: diceToRollBase, comment: comments } = splitDiceComment(diceArray[1]);
|
|
1190
|
+
let diceToRoll = diceToRollBase;
|
|
1191
1191
|
let curlyCompare;
|
|
1192
1192
|
if (isCurlyBulk) {
|
|
1193
1193
|
const curlyCompareRegex = diceToRoll.match(SIGN_REGEX_SPACE);
|
|
@@ -1368,11 +1368,12 @@ function handlePitySystem(dice, compare, diceRoll, roller, engine) {
|
|
|
1368
1368
|
isFail = (0, import_mathjs9.evaluate)(`${res.total}${compare.sign}${compare.value}`);
|
|
1369
1369
|
}
|
|
1370
1370
|
}
|
|
1371
|
+
if (!res?.result.length) throw new DiceTypeError(dice, "empty_dice");
|
|
1371
1372
|
return { rerollCount, result: res };
|
|
1372
1373
|
}
|
|
1373
1374
|
|
|
1374
1375
|
// src/roll.ts
|
|
1375
|
-
function roll(dice, engine = import_rpg_dice_roller8.NumberGenerator.engines.nodeCrypto, pity, sort) {
|
|
1376
|
+
function roll(dice, engine = import_rpg_dice_roller8.NumberGenerator.engines.nodeCrypto, pity, sort, comment) {
|
|
1376
1377
|
if (sort === "none" /* None */) sort = void 0;
|
|
1377
1378
|
const prepared = prepareDice(dice);
|
|
1378
1379
|
if (!prepared.dice.includes("d")) return void 0;
|
|
@@ -1415,8 +1416,10 @@ function roll(dice, engine = import_rpg_dice_roller8.NumberGenerator.engines.nod
|
|
|
1415
1416
|
}
|
|
1416
1417
|
const roller = new import_rpg_dice_roller8.DiceRoller();
|
|
1417
1418
|
import_rpg_dice_roller8.NumberGenerator.generator.engine = engine;
|
|
1418
|
-
|
|
1419
|
-
|
|
1419
|
+
const splitResult = splitDiceComment(processedDice);
|
|
1420
|
+
const diceBase = comment !== void 0 ? processedDice.trimEnd() : splitResult.dice;
|
|
1421
|
+
const resolvedComment = comment ?? splitResult.comment;
|
|
1422
|
+
const diceWithoutComment = setSortOrder(diceBase, sort);
|
|
1420
1423
|
let diceRoll;
|
|
1421
1424
|
try {
|
|
1422
1425
|
diceRoll = roller.roll(diceWithoutComment);
|
|
@@ -1432,8 +1435,6 @@ function roll(dice, engine = import_rpg_dice_roller8.NumberGenerator.engines.nod
|
|
|
1432
1435
|
);
|
|
1433
1436
|
compare.trivial = trivial ? true : void 0;
|
|
1434
1437
|
}
|
|
1435
|
-
const commentMatch = processedDice.match(COMMENT_REGEX);
|
|
1436
|
-
const comment = commentMatch ? commentMatch[2] : void 0;
|
|
1437
1438
|
let rerollCount = 0;
|
|
1438
1439
|
let pityResult;
|
|
1439
1440
|
if (pity && compare) {
|
|
@@ -1450,7 +1451,7 @@ function roll(dice, engine = import_rpg_dice_roller8.NumberGenerator.engines.nod
|
|
|
1450
1451
|
return {
|
|
1451
1452
|
...pityResult,
|
|
1452
1453
|
dice: prepared.isSimpleCurly ? finalDiceDisplay : processedDice,
|
|
1453
|
-
comment,
|
|
1454
|
+
comment: resolvedComment,
|
|
1454
1455
|
compare,
|
|
1455
1456
|
modifier: modificator,
|
|
1456
1457
|
pityLogs: rerollCount,
|
|
@@ -1469,10 +1470,11 @@ function roll(dice, engine = import_rpg_dice_roller8.NumberGenerator.engines.nod
|
|
|
1469
1470
|
prepared.explodingSuccess.normalizedSegment,
|
|
1470
1471
|
prepared.explodingSuccess.originalSegment
|
|
1471
1472
|
);
|
|
1473
|
+
if (!resultOutput.length) throw new DiceTypeError(dice, "empty_dice");
|
|
1472
1474
|
return {
|
|
1473
1475
|
dice: prepared.isSimpleCurly ? finalDiceDisplay : prepared.diceDisplay,
|
|
1474
1476
|
result: resultOutput,
|
|
1475
|
-
comment,
|
|
1477
|
+
comment: resolvedComment,
|
|
1476
1478
|
compare: compare ? compare : void 0,
|
|
1477
1479
|
modifier: modificator,
|
|
1478
1480
|
total: successes,
|
|
@@ -1483,7 +1485,7 @@ function roll(dice, engine = import_rpg_dice_roller8.NumberGenerator.engines.nod
|
|
|
1483
1485
|
return {
|
|
1484
1486
|
dice: prepared.isSimpleCurly ? finalDiceDisplay : processedDice,
|
|
1485
1487
|
result: resultOutput,
|
|
1486
|
-
comment,
|
|
1488
|
+
comment: resolvedComment,
|
|
1487
1489
|
compare: compare ? compare : void 0,
|
|
1488
1490
|
modifier: modificator,
|
|
1489
1491
|
total: roller.total,
|
|
@@ -1679,7 +1681,6 @@ function replaceInFormula(element, diceResult, compareResult, res, engine = impo
|
|
|
1679
1681
|
createCriticalCustom,
|
|
1680
1682
|
diceRandomParse,
|
|
1681
1683
|
diceTypeRandomParse,
|
|
1682
|
-
escapeRegex,
|
|
1683
1684
|
evalCombinaison,
|
|
1684
1685
|
evalOneCombinaison,
|
|
1685
1686
|
evalStatsDice,
|
|
@@ -1700,6 +1701,7 @@ function replaceInFormula(element, diceResult, compareResult, res, engine = impo
|
|
|
1700
1701
|
replaceUnknown,
|
|
1701
1702
|
resolveFormulaHint,
|
|
1702
1703
|
roll,
|
|
1704
|
+
splitDiceComment,
|
|
1703
1705
|
standardizeDice,
|
|
1704
1706
|
templateSchema,
|
|
1705
1707
|
testDiceRegistered,
|