@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.d.mts +26 -15
- package/dist/index.d.ts +26 -15
- package/dist/index.js +502 -429
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +496 -424
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -205,211 +205,259 @@ function includeDiceType(dice, diceType, userStats) {
|
|
|
205
205
|
}
|
|
206
206
|
|
|
207
207
|
// src/roll.ts
|
|
208
|
-
import { DiceRoller as DiceRoller3, NumberGenerator as
|
|
209
|
-
import { evaluate as
|
|
208
|
+
import { DiceRoller as DiceRoller3, NumberGenerator as NumberGenerator8 } from "@dice-roller/rpg-dice-roller";
|
|
209
|
+
import { evaluate as evaluate10 } from "mathjs";
|
|
210
210
|
|
|
211
211
|
// src/dice/bulk.ts
|
|
212
|
-
import { DiceRoller as DiceRoller2, NumberGenerator as
|
|
213
|
-
import { evaluate as
|
|
212
|
+
import { DiceRoller as DiceRoller2, NumberGenerator as NumberGenerator7 } from "@dice-roller/rpg-dice-roller";
|
|
213
|
+
import { evaluate as evaluate8 } from "mathjs";
|
|
214
214
|
|
|
215
215
|
// src/dice/compare.ts
|
|
216
|
-
import { NumberGenerator as
|
|
216
|
+
import { NumberGenerator as NumberGenerator4 } from "@dice-roller/rpg-dice-roller";
|
|
217
217
|
import { evaluate as evaluate2 } from "mathjs";
|
|
218
218
|
|
|
219
219
|
// src/utils.ts
|
|
220
|
+
import "uniformize";
|
|
221
|
+
import { NumberGenerator as NumberGenerator3 } from "@dice-roller/rpg-dice-roller";
|
|
222
|
+
import { Random as Random2 } from "random-js";
|
|
223
|
+
|
|
224
|
+
// src/verify_template.ts
|
|
220
225
|
import { evaluate } from "mathjs";
|
|
226
|
+
import { Random } from "random-js";
|
|
221
227
|
import "uniformize";
|
|
222
228
|
import { NumberGenerator as NumberGenerator2 } from "@dice-roller/rpg-dice-roller";
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
}
|
|
233
|
-
function levenshteinDistance(str1, str2) {
|
|
234
|
-
const matrix = Array(str2.length + 1).fill(null).map(() => Array(str1.length + 1).fill(null));
|
|
235
|
-
for (let i = 0; i <= str1.length; i++) matrix[0][i] = i;
|
|
236
|
-
for (let j = 0; j <= str2.length; j++) matrix[j][0] = j;
|
|
237
|
-
for (let j = 1; j <= str2.length; j++) {
|
|
238
|
-
for (let i = 1; i <= str1.length; i++) {
|
|
239
|
-
const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;
|
|
240
|
-
matrix[j][i] = Math.min(
|
|
241
|
-
matrix[j][i - 1] + 1,
|
|
242
|
-
// insertion
|
|
243
|
-
matrix[j - 1][i] + 1,
|
|
244
|
-
// deletion
|
|
245
|
-
matrix[j - 1][i - 1] + cost
|
|
246
|
-
// substitution
|
|
247
|
-
);
|
|
229
|
+
function evalStatsDice(testDice, allStats, engine = NumberGenerator2.engines.nodeCrypto, pity) {
|
|
230
|
+
let dice = testDice.trimEnd();
|
|
231
|
+
if (allStats && Object.keys(allStats).length > 0) {
|
|
232
|
+
const names = Object.keys(allStats);
|
|
233
|
+
for (const name of names) {
|
|
234
|
+
const regex = new RegExp(escapeRegex(name.standardize()), "gi");
|
|
235
|
+
if (dice.standardize().match(regex)) {
|
|
236
|
+
const statValue = allStats[name];
|
|
237
|
+
dice = dice.standardize().replace(regex, statValue.toString()).trimEnd();
|
|
238
|
+
}
|
|
248
239
|
}
|
|
249
240
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
for (const [normalizedKey, original] of normalizedStats) {
|
|
257
|
-
if (normalizedKey.startsWith(searchTerm))
|
|
258
|
-
candidates.push([original, calculateSimilarity(searchTerm, normalizedKey)]);
|
|
259
|
-
}
|
|
260
|
-
if (candidates.length === 1) return candidates[0][0];
|
|
261
|
-
if (candidates.length > 0) {
|
|
262
|
-
candidates.sort((a, b) => b[1] - a[1]);
|
|
263
|
-
if (candidates[0][1] >= similarityThreshold) return candidates[0][0];
|
|
264
|
-
}
|
|
265
|
-
let bestMatch;
|
|
266
|
-
let bestSimilarity = 0;
|
|
267
|
-
for (const [normalizedKey, original] of normalizedStats) {
|
|
268
|
-
const similarity = calculateSimilarity(searchTerm, normalizedKey);
|
|
269
|
-
if (similarity === 1) return original;
|
|
270
|
-
if (similarity > bestSimilarity && similarity >= similarityThreshold) {
|
|
271
|
-
bestSimilarity = similarity;
|
|
272
|
-
bestMatch = original;
|
|
273
|
-
}
|
|
241
|
+
try {
|
|
242
|
+
if (!roll(replaceFormulaInDice(replaceExpByRandom(dice)), engine, pity))
|
|
243
|
+
throw new DiceTypeError(dice, "evalStatsDice", "no roll result");
|
|
244
|
+
return testDice;
|
|
245
|
+
} catch (error) {
|
|
246
|
+
throw new DiceTypeError(dice, "evalStatsDice", error);
|
|
274
247
|
}
|
|
275
|
-
return bestMatch;
|
|
276
248
|
}
|
|
277
|
-
function
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
249
|
+
function diceRandomParse(value, template, engine = NumberGenerator2.engines.nodeCrypto) {
|
|
250
|
+
if (!template.statistics) return replaceFormulaInDice(value.standardize());
|
|
251
|
+
value = value.standardize();
|
|
252
|
+
const statNames = Object.keys(template.statistics);
|
|
253
|
+
let newDice = value;
|
|
254
|
+
for (const name of statNames) {
|
|
255
|
+
const regex = new RegExp(escapeRegex(name.standardize()), "gi");
|
|
256
|
+
if (value.match(regex)) {
|
|
257
|
+
let max;
|
|
258
|
+
let min;
|
|
259
|
+
const foundStat = template.statistics?.[name];
|
|
260
|
+
if (foundStat) {
|
|
261
|
+
max = foundStat.max;
|
|
262
|
+
min = foundStat.min;
|
|
263
|
+
}
|
|
264
|
+
const total = template.total || 100;
|
|
265
|
+
const randomStatValue = generateRandomStat(total, max, min, engine);
|
|
266
|
+
newDice = value.replace(regex, randomStatValue.toString());
|
|
267
|
+
}
|
|
281
268
|
}
|
|
282
|
-
return
|
|
283
|
-
searchTerm.standardize(),
|
|
284
|
-
normalizeRecord,
|
|
285
|
-
similarityThreshold
|
|
286
|
-
) || null;
|
|
269
|
+
return replaceFormulaInDice(newDice);
|
|
287
270
|
}
|
|
288
|
-
function
|
|
289
|
-
|
|
271
|
+
function diceTypeRandomParse(dice, template, engine = NumberGenerator2.engines.nodeCrypto) {
|
|
272
|
+
dice = replaceExpByRandom(dice);
|
|
273
|
+
if (!template.statistics) return dice;
|
|
274
|
+
const firstStatNotcombinaison = Object.keys(template.statistics).find(
|
|
275
|
+
(stat) => !template.statistics?.[stat].combinaison
|
|
276
|
+
);
|
|
277
|
+
if (!firstStatNotcombinaison) return dice;
|
|
278
|
+
const stats = template.statistics[firstStatNotcombinaison];
|
|
279
|
+
const { min, max } = stats;
|
|
280
|
+
const total = template.total || 100;
|
|
281
|
+
const randomStatValue = generateRandomStat(total, max, min, engine);
|
|
282
|
+
return replaceFormulaInDice(dice.replaceAll("$", randomStatValue.toString()));
|
|
290
283
|
}
|
|
291
|
-
function
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
|
|
284
|
+
function evalCombinaison(combinaison, stats) {
|
|
285
|
+
const newStats = {};
|
|
286
|
+
for (const [stat, combin] of Object.entries(combinaison)) {
|
|
287
|
+
let formula = combin.standardize();
|
|
288
|
+
for (const [statName, value] of Object.entries(stats)) {
|
|
289
|
+
const regex = new RegExp(statName.standardize(), "gi");
|
|
290
|
+
formula = formula.replace(regex, value.toString());
|
|
291
|
+
}
|
|
292
|
+
try {
|
|
293
|
+
newStats[stat] = evaluate(formula);
|
|
294
|
+
} catch (error) {
|
|
295
|
+
throw new FormulaError(stat, "evalCombinaison", error);
|
|
296
|
+
}
|
|
298
297
|
}
|
|
299
|
-
return
|
|
298
|
+
return newStats;
|
|
300
299
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
300
|
+
function evalOneCombinaison(combinaison, stats) {
|
|
301
|
+
let formula = combinaison.standardize();
|
|
302
|
+
for (const [statName, value] of Object.entries(stats)) {
|
|
303
|
+
const regex = new RegExp(statName.standardize(), "gi");
|
|
304
|
+
formula = formula.replace(regex, value.toString());
|
|
305
|
+
}
|
|
306
|
+
try {
|
|
307
|
+
return evaluate(formula);
|
|
308
|
+
} catch (error) {
|
|
309
|
+
throw new FormulaError(combinaison, "evalOneCombinaison", error);
|
|
310
|
+
}
|
|
305
311
|
}
|
|
306
|
-
function
|
|
307
|
-
return
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
);
|
|
312
|
+
function convertNumber(number) {
|
|
313
|
+
if (number === void 0 || number === null) return void 0;
|
|
314
|
+
if (number.toString().length === 0 || Number.isNaN(Number.parseInt(number.toString(), 10)))
|
|
315
|
+
return void 0;
|
|
316
|
+
if (isNumber(number)) return Number.parseInt(number.toString(), 10);
|
|
317
|
+
return void 0;
|
|
311
318
|
}
|
|
312
|
-
function
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
319
|
+
function verifyTemplateValue(template, verify = true, engine = NumberGenerator2.engines.nodeCrypto) {
|
|
320
|
+
const parsedTemplate = templateSchema.parse(template);
|
|
321
|
+
const { success, failure } = parsedTemplate.critical ?? {};
|
|
322
|
+
const criticicalVal = {
|
|
323
|
+
success: convertNumber(success),
|
|
324
|
+
failure: convertNumber(failure)
|
|
325
|
+
};
|
|
326
|
+
const statistiqueTemplate = {
|
|
327
|
+
diceType: parsedTemplate.diceType,
|
|
328
|
+
statistics: parsedTemplate.statistics,
|
|
329
|
+
critical: criticicalVal,
|
|
330
|
+
total: parsedTemplate.total,
|
|
331
|
+
charName: parsedTemplate.charName,
|
|
332
|
+
damage: parsedTemplate.damage,
|
|
333
|
+
customCritical: parsedTemplate.customCritical,
|
|
334
|
+
forceDistrib: parsedTemplate.forceDistrib
|
|
335
|
+
};
|
|
336
|
+
if (!verify) return statistiqueTemplate;
|
|
337
|
+
if (statistiqueTemplate.diceType) {
|
|
338
|
+
if (statistiqueTemplate.diceType.match(DETECT_CRITICAL)) {
|
|
339
|
+
throw new DiceTypeError(
|
|
340
|
+
statistiqueTemplate.diceType,
|
|
341
|
+
"critical_dice_type",
|
|
342
|
+
"contains critical detection: should be in custom critical instead"
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
const cleanedDice2 = diceTypeRandomParse(
|
|
346
|
+
statistiqueTemplate.diceType,
|
|
347
|
+
statistiqueTemplate,
|
|
348
|
+
engine
|
|
349
|
+
);
|
|
350
|
+
const rolled = roll(cleanedDice2, engine);
|
|
351
|
+
if (!rolled) throw new DiceTypeError(cleanedDice2, "no_roll_result", "no roll result");
|
|
321
352
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
const [,
|
|
328
|
-
|
|
353
|
+
if (statistiqueTemplate.customCritical) {
|
|
354
|
+
if (!statistiqueTemplate.diceType) {
|
|
355
|
+
throw new DiceTypeError("no_dice_type", "no_dice_type", "no dice type");
|
|
356
|
+
}
|
|
357
|
+
const customCritical = statistiqueTemplate.customCritical;
|
|
358
|
+
for (const [, custom] of Object.entries(customCritical)) {
|
|
359
|
+
const cleanedDice2 = createCriticalCustom(
|
|
360
|
+
statistiqueTemplate.diceType,
|
|
361
|
+
custom,
|
|
362
|
+
statistiqueTemplate,
|
|
363
|
+
engine
|
|
364
|
+
);
|
|
365
|
+
const rolled = roll(cleanedDice2, engine);
|
|
366
|
+
if (!rolled)
|
|
367
|
+
throw new DiceTypeError(cleanedDice2, "verifyTemplateValue", "no roll result");
|
|
368
|
+
}
|
|
329
369
|
}
|
|
330
|
-
|
|
370
|
+
testDiceRegistered(statistiqueTemplate, engine);
|
|
371
|
+
testStatCombinaison(statistiqueTemplate, engine);
|
|
372
|
+
return statistiqueTemplate;
|
|
331
373
|
}
|
|
332
|
-
function
|
|
333
|
-
|
|
334
|
-
if (
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
const outsideText = match[2];
|
|
346
|
-
if (insideBrackets) {
|
|
347
|
-
result += insideBrackets;
|
|
348
|
-
continue;
|
|
349
|
-
}
|
|
350
|
-
if (!outsideText) {
|
|
351
|
-
continue;
|
|
352
|
-
}
|
|
353
|
-
const tokenRegex = /(\$?[\p{L}\p{N}_.]+)/gu;
|
|
354
|
-
let lastIndex = 0;
|
|
355
|
-
let tokenMatch;
|
|
356
|
-
while ((tokenMatch = tokenRegex.exec(outsideText)) !== null) {
|
|
357
|
-
result += outsideText.slice(lastIndex, tokenMatch.index);
|
|
358
|
-
const token = tokenMatch[0];
|
|
359
|
-
const tokenHasDollar = token.startsWith("$");
|
|
360
|
-
const tokenForCompare = tokenHasDollar ? token.slice(1) : token;
|
|
361
|
-
const tokenStd = tokenForCompare.standardize();
|
|
362
|
-
const diceReplacement = handleDiceAfterD(tokenStd, normalizedStats);
|
|
363
|
-
if (diceReplacement) {
|
|
364
|
-
result += diceReplacement;
|
|
365
|
-
lastIndex = tokenRegex.lastIndex;
|
|
366
|
-
continue;
|
|
367
|
-
}
|
|
368
|
-
result += handleSimpleToken(tokenStd, token, normalizedStats, minThreshold);
|
|
369
|
-
lastIndex = tokenRegex.lastIndex;
|
|
370
|
-
}
|
|
371
|
-
result += outsideText.slice(lastIndex);
|
|
374
|
+
function testDiceRegistered(template, engine = NumberGenerator2.engines.nodeCrypto) {
|
|
375
|
+
if (!template.damage) return;
|
|
376
|
+
if (Object.keys(template.damage).length === 0) throw new EmptyObjectError();
|
|
377
|
+
if (Object.keys(template.damage).length > 25) throw new TooManyDice();
|
|
378
|
+
for (const [name, dice] of Object.entries(template.damage)) {
|
|
379
|
+
if (!dice) continue;
|
|
380
|
+
const diceReplaced = replaceExpByRandom(dice);
|
|
381
|
+
const randomDiceParsed = diceRandomParse(diceReplaced, template, engine);
|
|
382
|
+
try {
|
|
383
|
+
const rolled = roll(randomDiceParsed, engine);
|
|
384
|
+
if (!rolled) throw new DiceTypeError(name, "no_roll_result", dice);
|
|
385
|
+
} catch (error) {
|
|
386
|
+
throw new DiceTypeError(name, "testDiceRegistered", error);
|
|
372
387
|
}
|
|
373
|
-
dice = result;
|
|
374
388
|
}
|
|
375
|
-
if (dollarValue) dice = dice.replaceAll("$", dollarValue);
|
|
376
|
-
return replaceFormulaInDice(dice);
|
|
377
389
|
}
|
|
378
|
-
function
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
390
|
+
function testStatCombinaison(template, engine = NumberGenerator2.engines.nodeCrypto) {
|
|
391
|
+
if (!template.statistics) return;
|
|
392
|
+
const onlycombinaisonStats = Object.fromEntries(
|
|
393
|
+
Object.entries(template.statistics).filter(
|
|
394
|
+
([_, value]) => value.combinaison !== void 0
|
|
395
|
+
)
|
|
396
|
+
);
|
|
397
|
+
const allOtherStats = Object.fromEntries(
|
|
398
|
+
Object.entries(template.statistics).filter(([_, value]) => !value.combinaison)
|
|
399
|
+
);
|
|
400
|
+
if (Object.keys(onlycombinaisonStats).length === 0) return;
|
|
401
|
+
const allStats = Object.keys(template.statistics).filter(
|
|
402
|
+
(stat) => !template.statistics[stat].combinaison
|
|
403
|
+
);
|
|
404
|
+
if (allStats.length === 0) throw new NoStatisticsError();
|
|
405
|
+
const error = [];
|
|
406
|
+
for (const [stat, value] of Object.entries(onlycombinaisonStats)) {
|
|
407
|
+
let formula = value.combinaison;
|
|
408
|
+
for (const [other, data] of Object.entries(allOtherStats)) {
|
|
409
|
+
const { max, min } = data;
|
|
410
|
+
const total = template.total || 100;
|
|
411
|
+
const randomStatValue = generateRandomStat(total, max, min, engine);
|
|
412
|
+
const regex = new RegExp(other, "gi");
|
|
413
|
+
formula = formula.replace(regex, randomStatValue.toString());
|
|
414
|
+
}
|
|
415
|
+
try {
|
|
416
|
+
evaluate(formula);
|
|
417
|
+
} catch (e) {
|
|
418
|
+
error.push(stat);
|
|
391
419
|
}
|
|
392
420
|
}
|
|
393
|
-
|
|
421
|
+
if (error.length > 0) throw new FormulaError(error.join(", "), "testStatCombinaison");
|
|
422
|
+
return;
|
|
394
423
|
}
|
|
395
|
-
function
|
|
396
|
-
|
|
424
|
+
function generateRandomStat(total = 100, max, min, engine = NumberGenerator2.engines.nodeCrypto) {
|
|
425
|
+
let randomStatValue = total + 1;
|
|
426
|
+
const random = new Random(engine || NumberGenerator2.engines.nodeCrypto);
|
|
427
|
+
while (randomStatValue >= total || randomStatValue === 0) {
|
|
428
|
+
if (max && min) randomStatValue = randomInt(min, max, engine, random);
|
|
429
|
+
else if (max) randomStatValue = randomInt(1, max, engine, random);
|
|
430
|
+
else if (min) randomStatValue = randomInt(min, total, engine, random);
|
|
431
|
+
else randomStatValue = randomInt(1, total, engine, random);
|
|
432
|
+
}
|
|
433
|
+
return randomStatValue;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// src/utils.ts
|
|
437
|
+
function escapeRegex(string) {
|
|
438
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
439
|
+
}
|
|
440
|
+
function standardizeDice(dice) {
|
|
441
|
+
return dice.replace(
|
|
442
|
+
/(\[[^\]]+])|([^[]+)/g,
|
|
443
|
+
(_match, insideBrackets, outsideText) => insideBrackets ? insideBrackets : outsideText.standardize().replaceAll("df", "dF")
|
|
444
|
+
);
|
|
397
445
|
}
|
|
398
446
|
function isNumber(value) {
|
|
399
447
|
return value !== void 0 && (typeof value === "number" || !Number.isNaN(Number(value)) && typeof value === "string" && value.trim().length > 0);
|
|
400
448
|
}
|
|
401
|
-
function replaceExpByRandom(dice, engine =
|
|
449
|
+
function replaceExpByRandom(dice, engine = NumberGenerator3.engines.nodeCrypto) {
|
|
402
450
|
const diceRegex = /\{exp( ?\|\| ?(?<default>\d+))?}/gi;
|
|
403
451
|
return dice.replace(diceRegex, (_match, _p1, _p2, _offset, _string, groups) => {
|
|
404
452
|
const defaultValue = groups?.default;
|
|
405
453
|
return defaultValue ?? randomInt(1, 999, engine).toString();
|
|
406
454
|
});
|
|
407
455
|
}
|
|
408
|
-
function randomInt(min, max, engine =
|
|
409
|
-
if (!rng) rng = new
|
|
456
|
+
function randomInt(min, max, engine = NumberGenerator3.engines.nodeCrypto, rng) {
|
|
457
|
+
if (!rng) rng = new Random2(engine || void 0);
|
|
410
458
|
return rng.integer(min, max);
|
|
411
459
|
}
|
|
412
|
-
function createCriticalCustom(dice, customCritical, template, engine =
|
|
460
|
+
function createCriticalCustom(dice, customCritical, template, engine = NumberGenerator3.engines.nodeCrypto) {
|
|
413
461
|
const compareRegex = dice.match(SIGN_REGEX_SPACE);
|
|
414
462
|
let customDice = dice;
|
|
415
463
|
const compareValue = diceTypeRandomParse(customCritical.value, template, engine);
|
|
@@ -452,7 +500,7 @@ function canComparisonFail(maxRollValue, compare, minRollValue = 1) {
|
|
|
452
500
|
return true;
|
|
453
501
|
}
|
|
454
502
|
}
|
|
455
|
-
function rollCompare(value, engine =
|
|
503
|
+
function rollCompare(value, engine = NumberGenerator4.engines.nodeCrypto, pity) {
|
|
456
504
|
if (isNumber(value)) return { value: Number.parseInt(value, 10) };
|
|
457
505
|
if (!value || typeof value === "string" && value.trim() === "") {
|
|
458
506
|
return { value: 0, diceResult: value };
|
|
@@ -471,7 +519,7 @@ function rollCompare(value, engine = NumberGenerator3.engines.nodeCrypto, pity)
|
|
|
471
519
|
diceResult: rollComp?.result
|
|
472
520
|
};
|
|
473
521
|
}
|
|
474
|
-
function getCompare(dice, compareRegex, engine =
|
|
522
|
+
function getCompare(dice, compareRegex, engine = NumberGenerator4.engines.nodeCrypto, pity) {
|
|
475
523
|
if (dice.match(/((\{.*,(.*)+\}|([><=!]+\d+f))([><=]|!=)+\d+\}?)|\{(.*)(([><=]|!=)+).*\}/))
|
|
476
524
|
return { dice, compare: void 0 };
|
|
477
525
|
dice = dice.replace(SIGN_REGEX_SPACE, "");
|
|
@@ -527,7 +575,7 @@ function canComparisonSucceed(maxRollValue, compare, minRollValue) {
|
|
|
527
575
|
import { evaluate as evaluate4 } from "mathjs";
|
|
528
576
|
|
|
529
577
|
// src/dice/signs.ts
|
|
530
|
-
import { NumberGenerator as
|
|
578
|
+
import { NumberGenerator as NumberGenerator5 } from "@dice-roller/rpg-dice-roller";
|
|
531
579
|
import { evaluate as evaluate3 } from "mathjs";
|
|
532
580
|
|
|
533
581
|
// src/dice/replace.ts
|
|
@@ -617,7 +665,7 @@ function inverseSign(sign) {
|
|
|
617
665
|
return "==";
|
|
618
666
|
}
|
|
619
667
|
}
|
|
620
|
-
function compareSignFormule(toRoll, compareRegex, element, diceResult, engine =
|
|
668
|
+
function compareSignFormule(toRoll, compareRegex, element, diceResult, engine = NumberGenerator5.engines.nodeCrypto, pity, rollBounds) {
|
|
621
669
|
let results = "";
|
|
622
670
|
let trivial = false;
|
|
623
671
|
const compareResult = getCompare(toRoll, compareRegex, engine);
|
|
@@ -678,43 +726,278 @@ function normalizeExplodingSuccess(dice) {
|
|
|
678
726
|
parsedValue = 0;
|
|
679
727
|
}
|
|
680
728
|
}
|
|
681
|
-
const normalizedSegment = "!";
|
|
682
|
-
const replacedDice = dice.replace(match[0], normalizedSegment);
|
|
683
|
-
return {
|
|
684
|
-
dice: replacedDice,
|
|
685
|
-
originalDice: dice,
|
|
686
|
-
sign: normalizedSign,
|
|
687
|
-
value: parsedValue,
|
|
688
|
-
normalizedSegment,
|
|
689
|
-
originalSegment: match[0]
|
|
690
|
-
};
|
|
729
|
+
const normalizedSegment = "!";
|
|
730
|
+
const replacedDice = dice.replace(match[0], normalizedSegment);
|
|
731
|
+
return {
|
|
732
|
+
dice: replacedDice,
|
|
733
|
+
originalDice: dice,
|
|
734
|
+
sign: normalizedSign,
|
|
735
|
+
value: parsedValue,
|
|
736
|
+
normalizedSegment,
|
|
737
|
+
originalSegment: match[0]
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
function countExplodingSuccesses(diceRoll, sign, value) {
|
|
741
|
+
const rollsArray = Array.isArray(diceRoll) ? diceRoll : [diceRoll];
|
|
742
|
+
const flatValues = [];
|
|
743
|
+
for (const dr of rollsArray) {
|
|
744
|
+
const groups = dr.rolls ?? [];
|
|
745
|
+
for (const group of groups) {
|
|
746
|
+
const innerRolls = group.rolls ?? [];
|
|
747
|
+
for (const roll2 of innerRolls) {
|
|
748
|
+
if (typeof roll2.value === "number") flatValues.push(roll2.value);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
return flatValues.reduce(
|
|
753
|
+
(acc, current) => acc + (matchComparison(sign, current, value) ? 1 : 0),
|
|
754
|
+
0
|
|
755
|
+
);
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
// src/dice/extract.ts
|
|
759
|
+
import { DiceRoller, NumberGenerator as NumberGenerator6 } from "@dice-roller/rpg-dice-roller";
|
|
760
|
+
|
|
761
|
+
// src/similarities/generateStatsDice.ts
|
|
762
|
+
import { evaluate as evaluate5 } from "mathjs";
|
|
763
|
+
|
|
764
|
+
// src/similarities/similarity.ts
|
|
765
|
+
function calculateSimilarity(str1, str2) {
|
|
766
|
+
const longer = str1.length > str2.length ? str1 : str2;
|
|
767
|
+
const shorter = str1.length > str2.length ? str2 : str1;
|
|
768
|
+
if (longer.length === 0) return 1;
|
|
769
|
+
const distance = levenshteinDistance(longer, shorter);
|
|
770
|
+
return (longer.length - distance) / longer.length;
|
|
771
|
+
}
|
|
772
|
+
function levenshteinDistance(str1, str2) {
|
|
773
|
+
const matrix = Array(str2.length + 1).fill(null).map(() => Array(str1.length + 1).fill(null));
|
|
774
|
+
for (let i = 0; i <= str1.length; i++) matrix[0][i] = i;
|
|
775
|
+
for (let j = 0; j <= str2.length; j++) matrix[j][0] = j;
|
|
776
|
+
for (let j = 1; j <= str2.length; j++) {
|
|
777
|
+
for (let i = 1; i <= str1.length; i++) {
|
|
778
|
+
const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;
|
|
779
|
+
matrix[j][i] = Math.min(
|
|
780
|
+
matrix[j][i - 1] + 1,
|
|
781
|
+
// insertion
|
|
782
|
+
matrix[j - 1][i] + 1,
|
|
783
|
+
// deletion
|
|
784
|
+
matrix[j - 1][i - 1] + cost
|
|
785
|
+
// substitution
|
|
786
|
+
);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
return matrix[str2.length][str1.length];
|
|
790
|
+
}
|
|
791
|
+
function findBestStatMatch(searchTerm, normalizedStats, similarityThreshold = MIN_THRESHOLD_MATCH) {
|
|
792
|
+
const exact = normalizedStats.get(searchTerm);
|
|
793
|
+
if (exact) return exact;
|
|
794
|
+
const candidates = [];
|
|
795
|
+
for (const [normalizedKey, original] of normalizedStats) {
|
|
796
|
+
if (normalizedKey.startsWith(searchTerm))
|
|
797
|
+
candidates.push([original, calculateSimilarity(searchTerm, normalizedKey)]);
|
|
798
|
+
}
|
|
799
|
+
if (candidates.length === 1) return candidates[0][0];
|
|
800
|
+
if (candidates.length > 0) {
|
|
801
|
+
candidates.sort((a, b) => b[1] - a[1]);
|
|
802
|
+
if (candidates[0][1] >= similarityThreshold) return candidates[0][0];
|
|
803
|
+
}
|
|
804
|
+
let bestMatch;
|
|
805
|
+
let bestSimilarity = 0;
|
|
806
|
+
for (const [normalizedKey, original] of normalizedStats) {
|
|
807
|
+
const similarity = calculateSimilarity(searchTerm, normalizedKey);
|
|
808
|
+
if (similarity === 1) return original;
|
|
809
|
+
if (similarity > bestSimilarity && similarity >= similarityThreshold) {
|
|
810
|
+
bestSimilarity = similarity;
|
|
811
|
+
bestMatch = original;
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
return bestMatch;
|
|
815
|
+
}
|
|
816
|
+
function findBestRecord(record, searchTerm, similarityThreshold = MIN_THRESHOLD_MATCH) {
|
|
817
|
+
const normalizeRecord = /* @__PURE__ */ new Map();
|
|
818
|
+
for (const key of Object.keys(record)) {
|
|
819
|
+
normalizeRecord.set(key.standardize(), key);
|
|
820
|
+
}
|
|
821
|
+
return findBestStatMatch(
|
|
822
|
+
searchTerm.standardize(),
|
|
823
|
+
normalizeRecord,
|
|
824
|
+
similarityThreshold
|
|
825
|
+
) || null;
|
|
826
|
+
}
|
|
827
|
+
function replaceUnknown(dice, replacer) {
|
|
828
|
+
return dice.replaceAll(REMOVER_PATTERN.STAT_MATCHER, replacer).replaceAll("+0", "").replaceAll("-0", "");
|
|
829
|
+
}
|
|
830
|
+
function verifyStatMatcherPattern(dice, replaceUnknow) {
|
|
831
|
+
if (REMOVER_PATTERN.STAT_MATCHER.test(dice)) {
|
|
832
|
+
if (replaceUnknow)
|
|
833
|
+
return replaceUnknown(dice, replaceUnknow);
|
|
834
|
+
const matched = dice.matchAll(new RegExp(REMOVER_PATTERN.STAT_MATCHER));
|
|
835
|
+
const stats = matched ? Array.from(matched, (m) => m?.[0]).map((s) => `\`${s}\``).join(", ") : "unknown";
|
|
836
|
+
throw new DiceTypeError(stats, "unknown_stats");
|
|
837
|
+
}
|
|
838
|
+
return dice.replaceAll("+0", "").replaceAll("-0", "");
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
// src/similarities/generateStatsDice.ts
|
|
842
|
+
function handleDiceAfterD(tokenStd, normalizedStats) {
|
|
843
|
+
const diceMatch = /^(\d*)d(.+)$/i.exec(tokenStd);
|
|
844
|
+
if (!diceMatch) return null;
|
|
845
|
+
const diceCount = diceMatch[1] || "";
|
|
846
|
+
const afterD = diceMatch[2];
|
|
847
|
+
const bestMatch = findBestStatMatch(afterD, normalizedStats, 1);
|
|
848
|
+
if (bestMatch) {
|
|
849
|
+
const [, value] = bestMatch;
|
|
850
|
+
return `${diceCount}d${value.toString()}`;
|
|
851
|
+
}
|
|
852
|
+
return null;
|
|
853
|
+
}
|
|
854
|
+
function handleSimpleToken(tokenStd, token, normalizedStats, minThreshold) {
|
|
855
|
+
const bestMatch = findBestStatMatch(tokenStd, normalizedStats, minThreshold);
|
|
856
|
+
if (bestMatch) {
|
|
857
|
+
const [, value] = bestMatch;
|
|
858
|
+
return value.toString();
|
|
859
|
+
}
|
|
860
|
+
return token;
|
|
861
|
+
}
|
|
862
|
+
function generateStatsDice(originalDice, stats, minThreshold = 0.6, dollarValue) {
|
|
863
|
+
let dice = originalDice.standardize();
|
|
864
|
+
if (stats && Object.keys(stats).length > 0) {
|
|
865
|
+
const normalizedStats = /* @__PURE__ */ new Map();
|
|
866
|
+
for (const [key, value] of Object.entries(stats)) {
|
|
867
|
+
const normalized = key.standardize();
|
|
868
|
+
normalizedStats.set(normalized, [key, value]);
|
|
869
|
+
}
|
|
870
|
+
const partsRegex = /(\[[^\]]+])|([^[]+)/g;
|
|
871
|
+
let result = "";
|
|
872
|
+
let match;
|
|
873
|
+
while ((match = partsRegex.exec(dice)) !== null) {
|
|
874
|
+
const insideBrackets = match[1];
|
|
875
|
+
const outsideText = match[2];
|
|
876
|
+
if (insideBrackets) {
|
|
877
|
+
result += insideBrackets;
|
|
878
|
+
continue;
|
|
879
|
+
}
|
|
880
|
+
if (!outsideText) {
|
|
881
|
+
continue;
|
|
882
|
+
}
|
|
883
|
+
const tokenRegex = /(\$?[\p{L}\p{N}_.]+)/gu;
|
|
884
|
+
let lastIndex = 0;
|
|
885
|
+
let tokenMatch;
|
|
886
|
+
while ((tokenMatch = tokenRegex.exec(outsideText)) !== null) {
|
|
887
|
+
result += outsideText.slice(lastIndex, tokenMatch.index);
|
|
888
|
+
const token = tokenMatch[0];
|
|
889
|
+
const tokenHasDollar = token.startsWith("$");
|
|
890
|
+
const tokenForCompare = tokenHasDollar ? token.slice(1) : token;
|
|
891
|
+
const tokenStd = tokenForCompare.standardize();
|
|
892
|
+
const diceReplacement = handleDiceAfterD(tokenStd, normalizedStats);
|
|
893
|
+
if (diceReplacement) {
|
|
894
|
+
result += diceReplacement;
|
|
895
|
+
lastIndex = tokenRegex.lastIndex;
|
|
896
|
+
continue;
|
|
897
|
+
}
|
|
898
|
+
result += handleSimpleToken(tokenStd, token, normalizedStats, minThreshold);
|
|
899
|
+
lastIndex = tokenRegex.lastIndex;
|
|
900
|
+
}
|
|
901
|
+
result += outsideText.slice(lastIndex);
|
|
902
|
+
}
|
|
903
|
+
dice = result;
|
|
904
|
+
}
|
|
905
|
+
if (dollarValue) dice = dice.replaceAll("$", dollarValue);
|
|
906
|
+
return replaceFormulaInDice(dice);
|
|
907
|
+
}
|
|
908
|
+
function replaceFormulaInDice(dice) {
|
|
909
|
+
const formula = /(?<formula>\{{2}(.+?)}{2})/gim;
|
|
910
|
+
let match;
|
|
911
|
+
let modifiedDice = dice;
|
|
912
|
+
while ((match = formula.exec(dice)) !== null) {
|
|
913
|
+
if (match.groups?.formula) {
|
|
914
|
+
const formulae = match.groups.formula.replaceAll("{{", "").replaceAll("}}", "");
|
|
915
|
+
try {
|
|
916
|
+
const result = evaluate5(formulae);
|
|
917
|
+
modifiedDice = modifiedDice.replace(match.groups.formula, result.toString());
|
|
918
|
+
} catch (error) {
|
|
919
|
+
throw new FormulaError(match.groups.formula, "replaceFormulasInDice", error);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
return cleanedDice(modifiedDice);
|
|
691
924
|
}
|
|
692
|
-
function
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
925
|
+
function cleanedDice(dice) {
|
|
926
|
+
return dice.replaceAll("+-", "-").replaceAll("--", "+").replaceAll("++", "+").replaceAll("=>", ">=").replaceAll("=<", "<=").trimEnd();
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
// src/similarities/resolveFormula.ts
|
|
930
|
+
import { evaluate as evaluate6 } from "mathjs";
|
|
931
|
+
function toFiniteNumber(value) {
|
|
932
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
933
|
+
return void 0;
|
|
934
|
+
}
|
|
935
|
+
function substituteFormulaTokens(expr, resolvedStats, similarityThreshold = MIN_THRESHOLD_MATCH) {
|
|
936
|
+
return expr.replace(/([\p{L}\p{M}._-]+)/gu, (token) => {
|
|
937
|
+
const match = findBestStatMatch(token, resolvedStats, similarityThreshold);
|
|
938
|
+
return match !== void 0 ? match.toString() : token;
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
function resolveFormulaHint(formula, allAttributes, similarityThreshold = MIN_THRESHOLD_MATCH) {
|
|
942
|
+
const trimmed = formula.trim();
|
|
943
|
+
if (!trimmed) return { kind: "not-formula" };
|
|
944
|
+
if (isNumber(trimmed)) return { kind: "not-formula" };
|
|
945
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
946
|
+
const pending = /* @__PURE__ */ new Map();
|
|
947
|
+
for (const [name, val] of Object.entries(allAttributes)) {
|
|
948
|
+
const norm = name.standardize();
|
|
949
|
+
if (typeof val === "number") {
|
|
950
|
+
if (Number.isFinite(val)) resolved.set(norm, val);
|
|
951
|
+
continue;
|
|
952
|
+
}
|
|
953
|
+
const t = val.trim();
|
|
954
|
+
if (!t) continue;
|
|
955
|
+
if (isNumber(t)) {
|
|
956
|
+
const numeric = Number(t);
|
|
957
|
+
if (Number.isFinite(numeric)) resolved.set(norm, numeric);
|
|
958
|
+
} else {
|
|
959
|
+
pending.set(norm, t.standardize());
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
for (const [normName, expr2] of pending) {
|
|
963
|
+
pending.set(normName, substituteFormulaTokens(expr2, resolved, similarityThreshold));
|
|
964
|
+
}
|
|
965
|
+
let progress = true;
|
|
966
|
+
while (pending.size > 0 && progress) {
|
|
967
|
+
progress = false;
|
|
968
|
+
for (const [normName, expr2] of pending) {
|
|
969
|
+
try {
|
|
970
|
+
const result = toFiniteNumber(evaluate6(expr2));
|
|
971
|
+
if (result === void 0) continue;
|
|
972
|
+
resolved.set(normName, result);
|
|
973
|
+
pending.delete(normName);
|
|
974
|
+
progress = true;
|
|
975
|
+
for (const [otherNorm, otherExpr] of pending) {
|
|
976
|
+
pending.set(
|
|
977
|
+
otherNorm,
|
|
978
|
+
substituteFormulaTokens(otherExpr, resolved, similarityThreshold)
|
|
979
|
+
);
|
|
980
|
+
}
|
|
981
|
+
} catch {
|
|
701
982
|
}
|
|
702
983
|
}
|
|
703
984
|
}
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
985
|
+
const normFormula = trimmed.standardize();
|
|
986
|
+
const expr = substituteFormulaTokens(normFormula, resolved, similarityThreshold);
|
|
987
|
+
try {
|
|
988
|
+
const result = toFiniteNumber(evaluate6(expr));
|
|
989
|
+
if (result !== void 0) return { kind: "resolved", value: result };
|
|
990
|
+
return { kind: "error" };
|
|
991
|
+
} catch {
|
|
992
|
+
return { kind: "error" };
|
|
993
|
+
}
|
|
708
994
|
}
|
|
709
995
|
|
|
710
|
-
// src/dice/extract.ts
|
|
711
|
-
import { DiceRoller, NumberGenerator as NumberGenerator5 } from "@dice-roller/rpg-dice-roller";
|
|
712
|
-
|
|
713
996
|
// src/dice/calculator.ts
|
|
714
|
-
import { evaluate as
|
|
997
|
+
import { evaluate as evaluate7 } from "mathjs";
|
|
715
998
|
function calculator(sign, value, total) {
|
|
716
999
|
if (sign === "^") sign = "**";
|
|
717
|
-
return
|
|
1000
|
+
return evaluate7(`${total} ${sign} ${value}`);
|
|
718
1001
|
}
|
|
719
1002
|
|
|
720
1003
|
// src/dice/extract.ts
|
|
@@ -749,10 +1032,10 @@ function extractValuesFromOutput(output) {
|
|
|
749
1032
|
}
|
|
750
1033
|
return values;
|
|
751
1034
|
}
|
|
752
|
-
function getRollBounds(dice, engine =
|
|
1035
|
+
function getRollBounds(dice, engine = NumberGenerator6.engines.nodeCrypto) {
|
|
753
1036
|
try {
|
|
754
1037
|
const roller = new DiceRoller();
|
|
755
|
-
|
|
1038
|
+
NumberGenerator6.generator.engine = engine;
|
|
756
1039
|
const rollResult = roller.roll(dice);
|
|
757
1040
|
const instance = Array.isArray(rollResult) ? rollResult[0] : rollResult;
|
|
758
1041
|
const { minTotal, maxTotal } = instance;
|
|
@@ -866,7 +1149,7 @@ function handleBulkRolls(dice, isCurlyBulk, bulkContent, compare, explodingSucce
|
|
|
866
1149
|
);
|
|
867
1150
|
}
|
|
868
1151
|
const roller = new DiceRoller2();
|
|
869
|
-
|
|
1152
|
+
NumberGenerator7.generator.engine = engine;
|
|
870
1153
|
for (let i = 0; i < numberOfDice; i++) {
|
|
871
1154
|
try {
|
|
872
1155
|
roller.roll(diceToRoll);
|
|
@@ -889,7 +1172,7 @@ function handleBulkRollsWithComparison(numberOfDice, diceToRoll, comments, activ
|
|
|
889
1172
|
const results = [];
|
|
890
1173
|
let successCount = 0;
|
|
891
1174
|
const roller = new DiceRoller2();
|
|
892
|
-
|
|
1175
|
+
NumberGenerator7.generator.engine = engine;
|
|
893
1176
|
let trivialComparisonDetected = false;
|
|
894
1177
|
const formatOutput = (output, addStar) => {
|
|
895
1178
|
const formatted = addStar && isCurlyBulk ? output.replace(
|
|
@@ -923,7 +1206,7 @@ function handleBulkRollsWithComparison(numberOfDice, diceToRoll, comments, activ
|
|
|
923
1206
|
results.push(formattedRollOutput);
|
|
924
1207
|
} else {
|
|
925
1208
|
const rollTotal = rollInstance.total;
|
|
926
|
-
const isSuccess =
|
|
1209
|
+
const isSuccess = evaluate8(
|
|
927
1210
|
`${rollTotal}${activeCompare.sign}${activeCompare.value}`
|
|
928
1211
|
);
|
|
929
1212
|
if (isSuccess) successCount++;
|
|
@@ -986,7 +1269,7 @@ function handleBulkRollsWithComparison(numberOfDice, diceToRoll, comments, activ
|
|
|
986
1269
|
}
|
|
987
1270
|
|
|
988
1271
|
// src/dice/pity.ts
|
|
989
|
-
import { evaluate as
|
|
1272
|
+
import { evaluate as evaluate9 } from "mathjs";
|
|
990
1273
|
function handlePitySystem(dice, compare, diceRoll, roller, engine) {
|
|
991
1274
|
const currentRoll = Array.isArray(diceRoll) ? diceRoll[0] : diceRoll;
|
|
992
1275
|
const maxPossible = currentRoll ? currentRoll.maxTotal : null;
|
|
@@ -994,7 +1277,7 @@ function handlePitySystem(dice, compare, diceRoll, roller, engine) {
|
|
|
994
1277
|
if (!isComparisonPossible) {
|
|
995
1278
|
return { rerollCount: 0 };
|
|
996
1279
|
}
|
|
997
|
-
let isFail =
|
|
1280
|
+
let isFail = evaluate9(`${roller.total}${compare.sign}${compare.value}`);
|
|
998
1281
|
if (isFail) {
|
|
999
1282
|
return { rerollCount: 0 };
|
|
1000
1283
|
}
|
|
@@ -1009,14 +1292,14 @@ function handlePitySystem(dice, compare, diceRoll, roller, engine) {
|
|
|
1009
1292
|
}
|
|
1010
1293
|
rerollCount++;
|
|
1011
1294
|
if (res && res.total !== void 0) {
|
|
1012
|
-
isFail =
|
|
1295
|
+
isFail = evaluate9(`${res.total}${compare.sign}${compare.value}`);
|
|
1013
1296
|
}
|
|
1014
1297
|
}
|
|
1015
1298
|
return { rerollCount, result: res };
|
|
1016
1299
|
}
|
|
1017
1300
|
|
|
1018
1301
|
// src/roll.ts
|
|
1019
|
-
function roll(dice, engine =
|
|
1302
|
+
function roll(dice, engine = NumberGenerator8.engines.nodeCrypto, pity, sort) {
|
|
1020
1303
|
if (sort === "none" /* None */) sort = void 0;
|
|
1021
1304
|
const prepared = prepareDice(dice);
|
|
1022
1305
|
if (!prepared.dice.includes("d")) return void 0;
|
|
@@ -1058,7 +1341,7 @@ function roll(dice, engine = NumberGenerator7.engines.nodeCrypto, pity, sort) {
|
|
|
1058
1341
|
);
|
|
1059
1342
|
}
|
|
1060
1343
|
const roller = new DiceRoller3();
|
|
1061
|
-
|
|
1344
|
+
NumberGenerator8.generator.engine = engine;
|
|
1062
1345
|
let diceWithoutComment = processedDice.replace(COMMENT_REGEX, "").trimEnd();
|
|
1063
1346
|
diceWithoutComment = setSortOrder(diceWithoutComment, sort);
|
|
1064
1347
|
let diceRoll;
|
|
@@ -1135,7 +1418,7 @@ function roll(dice, engine = NumberGenerator7.engines.nodeCrypto, pity, sort) {
|
|
|
1135
1418
|
trivial: compare?.trivial ? true : void 0
|
|
1136
1419
|
};
|
|
1137
1420
|
}
|
|
1138
|
-
function sharedRolls(dice, engine =
|
|
1421
|
+
function sharedRolls(dice, engine = NumberGenerator8.engines.nodeCrypto, pity, explodingSuccessMain, diceDisplay, isSharedCurly, sort) {
|
|
1139
1422
|
if (!explodingSuccessMain)
|
|
1140
1423
|
explodingSuccessMain = normalizeExplodingSuccess(dice.split(";")[0] ?? dice);
|
|
1141
1424
|
if (explodingSuccessMain) {
|
|
@@ -1170,13 +1453,13 @@ function sharedRolls(dice, engine = NumberGenerator7.engines.nodeCrypto, pity, e
|
|
|
1170
1453
|
const sortFromMain = getSortOrder(diceMain);
|
|
1171
1454
|
const rollBounds = getRollBounds(diceMain, engine);
|
|
1172
1455
|
let diceResult = roll(diceMain, engine, pity, sort);
|
|
1173
|
-
if (!diceResult
|
|
1456
|
+
if (!diceResult?.total) {
|
|
1174
1457
|
if (hidden) {
|
|
1175
1458
|
diceResult = roll(fixParenthesis(split[0]), engine, pity, sort);
|
|
1176
1459
|
hidden = false;
|
|
1177
1460
|
} else return void 0;
|
|
1178
1461
|
}
|
|
1179
|
-
if (!diceResult
|
|
1462
|
+
if (!diceResult?.total) return void 0;
|
|
1180
1463
|
if (explodingSuccessMain && diceResult.result) {
|
|
1181
1464
|
const values = extractValuesFromOutput(diceResult.result);
|
|
1182
1465
|
diceResult.total = values.filter(
|
|
@@ -1218,7 +1501,7 @@ function sharedRolls(dice, engine = NumberGenerator7.engines.nodeCrypto, pity, e
|
|
|
1218
1501
|
const { diceAll } = replaceText(element, diceResult.total, diceResult.dice);
|
|
1219
1502
|
let successCount = 0;
|
|
1220
1503
|
try {
|
|
1221
|
-
const evaluated =
|
|
1504
|
+
const evaluated = evaluate10(toRoll);
|
|
1222
1505
|
successCount = evaluated ? 1 : 0;
|
|
1223
1506
|
} catch (error) {
|
|
1224
1507
|
const evaluated = roll(toRoll, engine, pity);
|
|
@@ -1252,7 +1535,7 @@ function sharedRolls(dice, engine = NumberGenerator7.engines.nodeCrypto, pity, e
|
|
|
1252
1535
|
diceResult.dice
|
|
1253
1536
|
);
|
|
1254
1537
|
try {
|
|
1255
|
-
const evaluated =
|
|
1538
|
+
const evaluated = evaluate10(toRoll);
|
|
1256
1539
|
results.push(`\u25C8 ${comment}${diceAll}: ${formule} = ${evaluated}`);
|
|
1257
1540
|
total += Number.parseInt(evaluated, 10);
|
|
1258
1541
|
} catch (error) {
|
|
@@ -1281,7 +1564,7 @@ function sharedRolls(dice, engine = NumberGenerator7.engines.nodeCrypto, pity, e
|
|
|
1281
1564
|
trivial: hasTrivialComparison ? true : void 0
|
|
1282
1565
|
};
|
|
1283
1566
|
}
|
|
1284
|
-
function replaceInFormula(element, diceResult, compareResult, res, engine =
|
|
1567
|
+
function replaceInFormula(element, diceResult, compareResult, res, engine = NumberGenerator8.engines.nodeCrypto, pity) {
|
|
1285
1568
|
const { formule, diceAll } = replaceText(
|
|
1286
1569
|
element,
|
|
1287
1570
|
diceResult.total ?? 0,
|
|
@@ -1291,7 +1574,7 @@ function replaceInFormula(element, diceResult, compareResult, res, engine = Numb
|
|
|
1291
1574
|
const invertedSign = res ? compareResult.compare.sign : inverseSign(compareResult.compare.sign);
|
|
1292
1575
|
let evaluateRoll;
|
|
1293
1576
|
try {
|
|
1294
|
-
evaluateRoll =
|
|
1577
|
+
evaluateRoll = evaluate10(compareResult.dice);
|
|
1295
1578
|
return `${validSign} ${diceAll}: ${formule} = ${evaluateRoll}${invertedSign}${compareResult.compare?.value}`;
|
|
1296
1579
|
} catch (error) {
|
|
1297
1580
|
const evaluateRoll2 = roll(compareResult.dice, engine, pity);
|
|
@@ -1300,218 +1583,6 @@ function replaceInFormula(element, diceResult, compareResult, res, engine = Numb
|
|
|
1300
1583
|
return `${validSign} ${diceAll}: ${formule} = ${evaluateRoll2}${invertedSign}${compareResult.compare?.value}`;
|
|
1301
1584
|
}
|
|
1302
1585
|
}
|
|
1303
|
-
|
|
1304
|
-
// src/verify_template.ts
|
|
1305
|
-
import { evaluate as evaluate9 } from "mathjs";
|
|
1306
|
-
import { Random as Random2 } from "random-js";
|
|
1307
|
-
import "uniformize";
|
|
1308
|
-
import { NumberGenerator as NumberGenerator8 } from "@dice-roller/rpg-dice-roller";
|
|
1309
|
-
function evalStatsDice(testDice, allStats, engine = NumberGenerator8.engines.nodeCrypto, pity) {
|
|
1310
|
-
let dice = testDice.trimEnd();
|
|
1311
|
-
if (allStats && Object.keys(allStats).length > 0) {
|
|
1312
|
-
const names = Object.keys(allStats);
|
|
1313
|
-
for (const name of names) {
|
|
1314
|
-
const regex = new RegExp(escapeRegex(name.standardize()), "gi");
|
|
1315
|
-
if (dice.standardize().match(regex)) {
|
|
1316
|
-
const statValue = allStats[name];
|
|
1317
|
-
dice = dice.standardize().replace(regex, statValue.toString()).trimEnd();
|
|
1318
|
-
}
|
|
1319
|
-
}
|
|
1320
|
-
}
|
|
1321
|
-
try {
|
|
1322
|
-
if (!roll(replaceFormulaInDice(replaceExpByRandom(dice)), engine, pity))
|
|
1323
|
-
throw new DiceTypeError(dice, "evalStatsDice", "no roll result");
|
|
1324
|
-
return testDice;
|
|
1325
|
-
} catch (error) {
|
|
1326
|
-
throw new DiceTypeError(dice, "evalStatsDice", error);
|
|
1327
|
-
}
|
|
1328
|
-
}
|
|
1329
|
-
function diceRandomParse(value, template, engine = NumberGenerator8.engines.nodeCrypto) {
|
|
1330
|
-
if (!template.statistics) return replaceFormulaInDice(value.standardize());
|
|
1331
|
-
value = value.standardize();
|
|
1332
|
-
const statNames = Object.keys(template.statistics);
|
|
1333
|
-
let newDice = value;
|
|
1334
|
-
for (const name of statNames) {
|
|
1335
|
-
const regex = new RegExp(escapeRegex(name.standardize()), "gi");
|
|
1336
|
-
if (value.match(regex)) {
|
|
1337
|
-
let max;
|
|
1338
|
-
let min;
|
|
1339
|
-
const foundStat = template.statistics?.[name];
|
|
1340
|
-
if (foundStat) {
|
|
1341
|
-
max = foundStat.max;
|
|
1342
|
-
min = foundStat.min;
|
|
1343
|
-
}
|
|
1344
|
-
const total = template.total || 100;
|
|
1345
|
-
const randomStatValue = generateRandomStat(total, max, min, engine);
|
|
1346
|
-
newDice = value.replace(regex, randomStatValue.toString());
|
|
1347
|
-
}
|
|
1348
|
-
}
|
|
1349
|
-
return replaceFormulaInDice(newDice);
|
|
1350
|
-
}
|
|
1351
|
-
function diceTypeRandomParse(dice, template, engine = NumberGenerator8.engines.nodeCrypto) {
|
|
1352
|
-
dice = replaceExpByRandom(dice);
|
|
1353
|
-
if (!template.statistics) return dice;
|
|
1354
|
-
const firstStatNotcombinaison = Object.keys(template.statistics).find(
|
|
1355
|
-
(stat) => !template.statistics?.[stat].combinaison
|
|
1356
|
-
);
|
|
1357
|
-
if (!firstStatNotcombinaison) return dice;
|
|
1358
|
-
const stats = template.statistics[firstStatNotcombinaison];
|
|
1359
|
-
const { min, max } = stats;
|
|
1360
|
-
const total = template.total || 100;
|
|
1361
|
-
const randomStatValue = generateRandomStat(total, max, min, engine);
|
|
1362
|
-
return replaceFormulaInDice(dice.replaceAll("$", randomStatValue.toString()));
|
|
1363
|
-
}
|
|
1364
|
-
function evalCombinaison(combinaison, stats) {
|
|
1365
|
-
const newStats = {};
|
|
1366
|
-
for (const [stat, combin] of Object.entries(combinaison)) {
|
|
1367
|
-
let formula = combin.standardize();
|
|
1368
|
-
for (const [statName, value] of Object.entries(stats)) {
|
|
1369
|
-
const regex = new RegExp(statName.standardize(), "gi");
|
|
1370
|
-
formula = formula.replace(regex, value.toString());
|
|
1371
|
-
}
|
|
1372
|
-
try {
|
|
1373
|
-
newStats[stat] = evaluate9(formula);
|
|
1374
|
-
} catch (error) {
|
|
1375
|
-
throw new FormulaError(stat, "evalCombinaison", error);
|
|
1376
|
-
}
|
|
1377
|
-
}
|
|
1378
|
-
return newStats;
|
|
1379
|
-
}
|
|
1380
|
-
function evalOneCombinaison(combinaison, stats) {
|
|
1381
|
-
let formula = combinaison.standardize();
|
|
1382
|
-
for (const [statName, value] of Object.entries(stats)) {
|
|
1383
|
-
const regex = new RegExp(statName.standardize(), "gi");
|
|
1384
|
-
formula = formula.replace(regex, value.toString());
|
|
1385
|
-
}
|
|
1386
|
-
try {
|
|
1387
|
-
return evaluate9(formula);
|
|
1388
|
-
} catch (error) {
|
|
1389
|
-
throw new FormulaError(combinaison, "evalOneCombinaison", error);
|
|
1390
|
-
}
|
|
1391
|
-
}
|
|
1392
|
-
function convertNumber(number) {
|
|
1393
|
-
if (number === void 0 || number === null) return void 0;
|
|
1394
|
-
if (number.toString().length === 0 || Number.isNaN(Number.parseInt(number.toString(), 10)))
|
|
1395
|
-
return void 0;
|
|
1396
|
-
if (isNumber(number)) return Number.parseInt(number.toString(), 10);
|
|
1397
|
-
return void 0;
|
|
1398
|
-
}
|
|
1399
|
-
function verifyTemplateValue(template, verify = true, engine = NumberGenerator8.engines.nodeCrypto) {
|
|
1400
|
-
const parsedTemplate = templateSchema.parse(template);
|
|
1401
|
-
const { success, failure } = parsedTemplate.critical ?? {};
|
|
1402
|
-
const criticicalVal = {
|
|
1403
|
-
success: convertNumber(success),
|
|
1404
|
-
failure: convertNumber(failure)
|
|
1405
|
-
};
|
|
1406
|
-
const statistiqueTemplate = {
|
|
1407
|
-
diceType: parsedTemplate.diceType,
|
|
1408
|
-
statistics: parsedTemplate.statistics,
|
|
1409
|
-
critical: criticicalVal,
|
|
1410
|
-
total: parsedTemplate.total,
|
|
1411
|
-
charName: parsedTemplate.charName,
|
|
1412
|
-
damage: parsedTemplate.damage,
|
|
1413
|
-
customCritical: parsedTemplate.customCritical,
|
|
1414
|
-
forceDistrib: parsedTemplate.forceDistrib
|
|
1415
|
-
};
|
|
1416
|
-
if (!verify) return statistiqueTemplate;
|
|
1417
|
-
if (statistiqueTemplate.diceType) {
|
|
1418
|
-
if (statistiqueTemplate.diceType.match(DETECT_CRITICAL)) {
|
|
1419
|
-
throw new DiceTypeError(
|
|
1420
|
-
statistiqueTemplate.diceType,
|
|
1421
|
-
"critical_dice_type",
|
|
1422
|
-
"contains critical detection: should be in custom critical instead"
|
|
1423
|
-
);
|
|
1424
|
-
}
|
|
1425
|
-
const cleanedDice2 = diceTypeRandomParse(
|
|
1426
|
-
statistiqueTemplate.diceType,
|
|
1427
|
-
statistiqueTemplate,
|
|
1428
|
-
engine
|
|
1429
|
-
);
|
|
1430
|
-
const rolled = roll(cleanedDice2, engine);
|
|
1431
|
-
if (!rolled) throw new DiceTypeError(cleanedDice2, "no_roll_result", "no roll result");
|
|
1432
|
-
}
|
|
1433
|
-
if (statistiqueTemplate.customCritical) {
|
|
1434
|
-
if (!statistiqueTemplate.diceType) {
|
|
1435
|
-
throw new DiceTypeError("no_dice_type", "no_dice_type", "no dice type");
|
|
1436
|
-
}
|
|
1437
|
-
const customCritical = statistiqueTemplate.customCritical;
|
|
1438
|
-
for (const [, custom] of Object.entries(customCritical)) {
|
|
1439
|
-
const cleanedDice2 = createCriticalCustom(
|
|
1440
|
-
statistiqueTemplate.diceType,
|
|
1441
|
-
custom,
|
|
1442
|
-
statistiqueTemplate,
|
|
1443
|
-
engine
|
|
1444
|
-
);
|
|
1445
|
-
const rolled = roll(cleanedDice2, engine);
|
|
1446
|
-
if (!rolled)
|
|
1447
|
-
throw new DiceTypeError(cleanedDice2, "verifyTemplateValue", "no roll result");
|
|
1448
|
-
}
|
|
1449
|
-
}
|
|
1450
|
-
testDiceRegistered(statistiqueTemplate, engine);
|
|
1451
|
-
testStatCombinaison(statistiqueTemplate, engine);
|
|
1452
|
-
return statistiqueTemplate;
|
|
1453
|
-
}
|
|
1454
|
-
function testDiceRegistered(template, engine = NumberGenerator8.engines.nodeCrypto) {
|
|
1455
|
-
if (!template.damage) return;
|
|
1456
|
-
if (Object.keys(template.damage).length === 0) throw new EmptyObjectError();
|
|
1457
|
-
if (Object.keys(template.damage).length > 25) throw new TooManyDice();
|
|
1458
|
-
for (const [name, dice] of Object.entries(template.damage)) {
|
|
1459
|
-
if (!dice) continue;
|
|
1460
|
-
const diceReplaced = replaceExpByRandom(dice);
|
|
1461
|
-
const randomDiceParsed = diceRandomParse(diceReplaced, template, engine);
|
|
1462
|
-
try {
|
|
1463
|
-
const rolled = roll(randomDiceParsed, engine);
|
|
1464
|
-
if (!rolled) throw new DiceTypeError(name, "no_roll_result", dice);
|
|
1465
|
-
} catch (error) {
|
|
1466
|
-
throw new DiceTypeError(name, "testDiceRegistered", error);
|
|
1467
|
-
}
|
|
1468
|
-
}
|
|
1469
|
-
}
|
|
1470
|
-
function testStatCombinaison(template, engine = NumberGenerator8.engines.nodeCrypto) {
|
|
1471
|
-
if (!template.statistics) return;
|
|
1472
|
-
const onlycombinaisonStats = Object.fromEntries(
|
|
1473
|
-
Object.entries(template.statistics).filter(
|
|
1474
|
-
([_, value]) => value.combinaison !== void 0
|
|
1475
|
-
)
|
|
1476
|
-
);
|
|
1477
|
-
const allOtherStats = Object.fromEntries(
|
|
1478
|
-
Object.entries(template.statistics).filter(([_, value]) => !value.combinaison)
|
|
1479
|
-
);
|
|
1480
|
-
if (Object.keys(onlycombinaisonStats).length === 0) return;
|
|
1481
|
-
const allStats = Object.keys(template.statistics).filter(
|
|
1482
|
-
(stat) => !template.statistics[stat].combinaison
|
|
1483
|
-
);
|
|
1484
|
-
if (allStats.length === 0) throw new NoStatisticsError();
|
|
1485
|
-
const error = [];
|
|
1486
|
-
for (const [stat, value] of Object.entries(onlycombinaisonStats)) {
|
|
1487
|
-
let formula = value.combinaison;
|
|
1488
|
-
for (const [other, data] of Object.entries(allOtherStats)) {
|
|
1489
|
-
const { max, min } = data;
|
|
1490
|
-
const total = template.total || 100;
|
|
1491
|
-
const randomStatValue = generateRandomStat(total, max, min, engine);
|
|
1492
|
-
const regex = new RegExp(other, "gi");
|
|
1493
|
-
formula = formula.replace(regex, randomStatValue.toString());
|
|
1494
|
-
}
|
|
1495
|
-
try {
|
|
1496
|
-
evaluate9(formula);
|
|
1497
|
-
} catch (e) {
|
|
1498
|
-
error.push(stat);
|
|
1499
|
-
}
|
|
1500
|
-
}
|
|
1501
|
-
if (error.length > 0) throw new FormulaError(error.join(", "), "testStatCombinaison");
|
|
1502
|
-
return;
|
|
1503
|
-
}
|
|
1504
|
-
function generateRandomStat(total = 100, max, min, engine = NumberGenerator8.engines.nodeCrypto) {
|
|
1505
|
-
let randomStatValue = total + 1;
|
|
1506
|
-
const random = new Random2(engine || NumberGenerator8.engines.nodeCrypto);
|
|
1507
|
-
while (randomStatValue >= total || randomStatValue === 0) {
|
|
1508
|
-
if (max && min) randomStatValue = randomInt(min, max, engine, random);
|
|
1509
|
-
else if (max) randomStatValue = randomInt(1, max, engine, random);
|
|
1510
|
-
else if (min) randomStatValue = randomInt(min, total, engine, random);
|
|
1511
|
-
else randomStatValue = randomInt(1, total, engine, random);
|
|
1512
|
-
}
|
|
1513
|
-
return randomStatValue;
|
|
1514
|
-
}
|
|
1515
1586
|
export {
|
|
1516
1587
|
COMMENT_REGEX,
|
|
1517
1588
|
DETECT_CRITICAL,
|
|
@@ -1553,6 +1624,7 @@ export {
|
|
|
1553
1624
|
replaceFormulaInDice,
|
|
1554
1625
|
replaceInFormula,
|
|
1555
1626
|
replaceUnknown,
|
|
1627
|
+
resolveFormulaHint,
|
|
1556
1628
|
roll,
|
|
1557
1629
|
standardizeDice,
|
|
1558
1630
|
templateSchema,
|