@dicelette/core 1.22.2 → 1.23.0
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 +44 -1
- package/dist/index.d.mts +18 -2
- package/dist/index.d.ts +18 -2
- package/dist/index.js +141 -37
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +141 -37
- package/dist/index.mjs.map +1 -1
- package/package.json +59 -59
package/dist/index.mjs
CHANGED
|
@@ -1,17 +1,47 @@
|
|
|
1
1
|
// src/dice.ts
|
|
2
2
|
import { DiceRoller, NumberGenerator } from "@dice-roller/rpg-dice-roller";
|
|
3
3
|
import { evaluate } from "mathjs";
|
|
4
|
-
function
|
|
5
|
-
|
|
4
|
+
function isTrivialComparison(maxValue, minValue, compare) {
|
|
5
|
+
const canSucceed = canComparisonSucceed(maxValue, compare, minValue);
|
|
6
|
+
const canFail = canComparisonFail(maxValue, compare, minValue);
|
|
7
|
+
return !canSucceed || !canFail;
|
|
8
|
+
}
|
|
9
|
+
function canComparisonFail(maxRollValue, compare, minRollValue = 1) {
|
|
10
|
+
switch (compare.sign) {
|
|
11
|
+
case ">":
|
|
12
|
+
return minRollValue <= compare.value;
|
|
13
|
+
// failure if roll <= value
|
|
14
|
+
case ">=":
|
|
15
|
+
return minRollValue < compare.value;
|
|
16
|
+
// failure if roll < value
|
|
17
|
+
case "<":
|
|
18
|
+
return maxRollValue >= compare.value;
|
|
19
|
+
// failure if roll >= value
|
|
20
|
+
case "<=":
|
|
21
|
+
return maxRollValue > compare.value;
|
|
22
|
+
// failure if roll > value
|
|
23
|
+
case "=":
|
|
24
|
+
case "==":
|
|
25
|
+
return minRollValue !== compare.value || maxRollValue !== compare.value;
|
|
26
|
+
// can differ
|
|
27
|
+
case "!=":
|
|
28
|
+
return minRollValue <= compare.value && compare.value <= maxRollValue;
|
|
29
|
+
// equality possible
|
|
30
|
+
default:
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function getCompare(dice, compareRegex, engine = NumberGenerator.engines.nodeCrypto, pity) {
|
|
35
|
+
if (dice.match(/((\{.*,(.*)+\}|([><=!]+\d+f))([><=]|!=)+\d+\}?)|\{(.*)(([><=]|!=)+).*\}/))
|
|
6
36
|
return { dice, compare: void 0 };
|
|
7
37
|
dice = dice.replace(SIGN_REGEX_SPACE, "");
|
|
8
38
|
let compare;
|
|
9
|
-
const calc = compareRegex[
|
|
39
|
+
const calc = compareRegex[2];
|
|
10
40
|
const sign = calc.match(/[+-/*^]/)?.[0];
|
|
11
41
|
const compareSign = compareRegex[0].match(SIGN_REGEX)?.[0];
|
|
12
42
|
if (sign) {
|
|
13
43
|
const toCalc = calc.replace(SIGN_REGEX, "").replace(/\s/g, "").replace(/;(.*)/, "");
|
|
14
|
-
const rCompare = rollCompare(toCalc, engine);
|
|
44
|
+
const rCompare = rollCompare(toCalc, engine, pity);
|
|
15
45
|
const total = evaluate(rCompare.value.toString());
|
|
16
46
|
dice = dice.replace(SIGN_REGEX_SPACE, `${compareSign}${total}`);
|
|
17
47
|
compare = {
|
|
@@ -21,7 +51,7 @@ function getCompare(dice, compareRegex, engine = NumberGenerator.engines.nodeCry
|
|
|
21
51
|
rollValue: rCompare.diceResult
|
|
22
52
|
};
|
|
23
53
|
} else {
|
|
24
|
-
const rcompare = rollCompare(calc, engine);
|
|
54
|
+
const rcompare = rollCompare(calc, engine, pity);
|
|
25
55
|
compare = {
|
|
26
56
|
sign: compareSign,
|
|
27
57
|
value: rcompare.value,
|
|
@@ -31,11 +61,19 @@ function getCompare(dice, compareRegex, engine = NumberGenerator.engines.nodeCry
|
|
|
31
61
|
}
|
|
32
62
|
return { dice, compare };
|
|
33
63
|
}
|
|
34
|
-
function rollCompare(value, engine = NumberGenerator.engines.nodeCrypto) {
|
|
64
|
+
function rollCompare(value, engine = NumberGenerator.engines.nodeCrypto, pity) {
|
|
35
65
|
if (isNumber(value)) return { value: Number.parseInt(value, 10) };
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
66
|
+
if (!value || typeof value === "string" && value.trim() === "") {
|
|
67
|
+
return { value: 0, diceResult: value };
|
|
68
|
+
}
|
|
69
|
+
const rollComp = roll(value, engine, pity);
|
|
70
|
+
if (!rollComp?.total) {
|
|
71
|
+
try {
|
|
72
|
+
return { value: evaluate(value), diceResult: value };
|
|
73
|
+
} catch (error) {
|
|
74
|
+
return { value: 0, diceResult: value };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
39
77
|
return {
|
|
40
78
|
dice: value,
|
|
41
79
|
value: rollComp.total,
|
|
@@ -74,20 +112,20 @@ function getModifier(dice) {
|
|
|
74
112
|
}
|
|
75
113
|
return modificator;
|
|
76
114
|
}
|
|
77
|
-
function roll(dice, engine = NumberGenerator.engines.nodeCrypto) {
|
|
115
|
+
function roll(dice, engine = NumberGenerator.engines.nodeCrypto, pity) {
|
|
78
116
|
dice = standardizeDice(replaceFormulaInDice(dice)).replace(/^\+/, "").replaceAll("=>", ">=").replaceAll("=<", "<=").trimStart();
|
|
79
117
|
if (!dice.includes("d")) return void 0;
|
|
80
118
|
dice = dice.replaceAll(DETECT_CRITICAL, "").trimEnd();
|
|
81
119
|
const compareRegex = dice.match(SIGN_REGEX_SPACE);
|
|
82
120
|
let compare;
|
|
83
121
|
if (dice.includes(";")) return sharedRolls(dice, engine);
|
|
122
|
+
dice = fixParenthesis(dice);
|
|
123
|
+
const modificator = getModifier(dice);
|
|
84
124
|
if (compareRegex) {
|
|
85
|
-
const compareResult = getCompare(dice, compareRegex, engine);
|
|
125
|
+
const compareResult = getCompare(dice, compareRegex, engine, pity);
|
|
86
126
|
dice = compareResult.dice;
|
|
87
127
|
compare = compareResult.compare;
|
|
88
128
|
}
|
|
89
|
-
dice = fixParenthesis(dice);
|
|
90
|
-
const modificator = getModifier(dice);
|
|
91
129
|
if (dice.match(/\d+?#(.*)/)) {
|
|
92
130
|
const diceArray = dice.split("#");
|
|
93
131
|
const numberOfDice = Number.parseInt(diceArray[0], 10);
|
|
@@ -115,22 +153,85 @@ function roll(dice, engine = NumberGenerator.engines.nodeCrypto) {
|
|
|
115
153
|
const roller = new DiceRoller();
|
|
116
154
|
NumberGenerator.generator.engine = engine;
|
|
117
155
|
const diceWithoutComment = dice.replace(COMMENT_REGEX, "").trimEnd();
|
|
156
|
+
let diceRoll;
|
|
118
157
|
try {
|
|
119
|
-
roller.roll(diceWithoutComment);
|
|
158
|
+
diceRoll = roller.roll(diceWithoutComment);
|
|
120
159
|
} catch (error) {
|
|
121
160
|
throw new DiceTypeError(diceWithoutComment, "roll", error);
|
|
122
161
|
}
|
|
162
|
+
if (compare && diceRoll) {
|
|
163
|
+
const currentRoll = Array.isArray(diceRoll) ? diceRoll[0] : diceRoll;
|
|
164
|
+
const maxDiceValue = currentRoll.maxTotal;
|
|
165
|
+
const minDiceValue = currentRoll.minTotal;
|
|
166
|
+
const trivial = isTrivialComparison(maxDiceValue, minDiceValue, compare);
|
|
167
|
+
compare.trivial = trivial ? true : void 0;
|
|
168
|
+
}
|
|
123
169
|
const commentMatch = dice.match(COMMENT_REGEX);
|
|
124
170
|
const comment = commentMatch ? commentMatch[2] : void 0;
|
|
171
|
+
let rerollCount = 0;
|
|
172
|
+
let res;
|
|
173
|
+
if (pity && compare) {
|
|
174
|
+
const currentRoll = Array.isArray(diceRoll) ? diceRoll[0] : diceRoll;
|
|
175
|
+
const maxPossible = currentRoll ? currentRoll.maxTotal : null;
|
|
176
|
+
const isComparisonPossible = maxPossible === null || canComparisonSucceed(maxPossible, compare);
|
|
177
|
+
if (isComparisonPossible) {
|
|
178
|
+
let isFail = evaluate(`${roller.total}${compare.sign}${compare.value}`);
|
|
179
|
+
if (!isFail) {
|
|
180
|
+
const maxReroll = 100;
|
|
181
|
+
while (!isFail && rerollCount < maxReroll) {
|
|
182
|
+
try {
|
|
183
|
+
res = roll(diceWithoutComment, engine, false);
|
|
184
|
+
} catch (error) {
|
|
185
|
+
throw new DiceTypeError(diceWithoutComment, "roll", error);
|
|
186
|
+
}
|
|
187
|
+
rerollCount++;
|
|
188
|
+
if (res && res.total !== void 0)
|
|
189
|
+
isFail = evaluate(`${res.total}${compare.sign}${compare.value}`);
|
|
190
|
+
}
|
|
191
|
+
if (res) {
|
|
192
|
+
return {
|
|
193
|
+
...res,
|
|
194
|
+
dice,
|
|
195
|
+
comment,
|
|
196
|
+
compare,
|
|
197
|
+
modifier: modificator,
|
|
198
|
+
pityLogs: rerollCount
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
} else ;
|
|
203
|
+
}
|
|
125
204
|
return {
|
|
126
205
|
dice,
|
|
127
206
|
result: roller.output,
|
|
128
207
|
comment,
|
|
129
208
|
compare: compare ? compare : void 0,
|
|
130
209
|
modifier: modificator,
|
|
131
|
-
total: roller.total
|
|
210
|
+
total: roller.total,
|
|
211
|
+
pityLogs: rerollCount > 0 ? rerollCount : void 0
|
|
132
212
|
};
|
|
133
213
|
}
|
|
214
|
+
function canComparisonSucceed(maxRollValue, compare, minRollValue) {
|
|
215
|
+
switch (compare.sign) {
|
|
216
|
+
case ">":
|
|
217
|
+
return maxRollValue > compare.value;
|
|
218
|
+
case ">=":
|
|
219
|
+
return maxRollValue >= compare.value;
|
|
220
|
+
case "<":
|
|
221
|
+
return compare.value > (minRollValue ?? 1);
|
|
222
|
+
// Au moins minRollValue possible
|
|
223
|
+
case "<=":
|
|
224
|
+
return compare.value >= (minRollValue ?? 1);
|
|
225
|
+
// Au moins minRollValue possible
|
|
226
|
+
case "=":
|
|
227
|
+
case "==":
|
|
228
|
+
return maxRollValue >= compare.value && compare.value >= (minRollValue ?? 1);
|
|
229
|
+
case "!=":
|
|
230
|
+
return maxRollValue !== compare.value || (minRollValue ?? 1) !== compare.value;
|
|
231
|
+
default:
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
134
235
|
function fixParenthesis(dice) {
|
|
135
236
|
const parenthesisRegex = /d\((\d+)\)/g;
|
|
136
237
|
return dice.replaceAll(parenthesisRegex, (_match, p1) => `d${p1}`);
|
|
@@ -157,7 +258,7 @@ function inverseSign(sign) {
|
|
|
157
258
|
return "==";
|
|
158
259
|
}
|
|
159
260
|
}
|
|
160
|
-
function replaceInFormula(element, diceResult, compareResult, res, engine = NumberGenerator.engines.nodeCrypto) {
|
|
261
|
+
function replaceInFormula(element, diceResult, compareResult, res, engine = NumberGenerator.engines.nodeCrypto, pity) {
|
|
161
262
|
const { formule, diceAll } = replaceText(
|
|
162
263
|
element,
|
|
163
264
|
diceResult.total ?? 0,
|
|
@@ -170,13 +271,13 @@ function replaceInFormula(element, diceResult, compareResult, res, engine = Numb
|
|
|
170
271
|
evaluateRoll = evaluate(compareResult.dice);
|
|
171
272
|
return `${validSign} ${diceAll}: ${formule} = ${evaluateRoll}${invertedSign}${compareResult.compare?.value}`;
|
|
172
273
|
} catch (error) {
|
|
173
|
-
const evaluateRoll2 = roll(compareResult.dice, engine);
|
|
274
|
+
const evaluateRoll2 = roll(compareResult.dice, engine, pity);
|
|
174
275
|
if (evaluateRoll2)
|
|
175
276
|
return `${validSign} ${diceAll}: ${evaluateRoll2.result.split(":").splice(1).join(":")}`;
|
|
176
277
|
return `${validSign} ${diceAll}: ${formule} = ${evaluateRoll2}${invertedSign}${compareResult.compare?.value}`;
|
|
177
278
|
}
|
|
178
279
|
}
|
|
179
|
-
function compareSignFormule(toRoll, compareRegex, element, diceResult, engine = NumberGenerator.engines.nodeCrypto) {
|
|
280
|
+
function compareSignFormule(toRoll, compareRegex, element, diceResult, engine = NumberGenerator.engines.nodeCrypto, pity) {
|
|
180
281
|
let results = "";
|
|
181
282
|
const compareResult = getCompare(toRoll, compareRegex, engine);
|
|
182
283
|
const toCompare = `${compareResult.dice}${compareResult.compare?.sign}${compareResult.compare?.value}`;
|
|
@@ -184,10 +285,10 @@ function compareSignFormule(toRoll, compareRegex, element, diceResult, engine =
|
|
|
184
285
|
try {
|
|
185
286
|
res = evaluate(toCompare);
|
|
186
287
|
} catch (error) {
|
|
187
|
-
res = roll(toCompare, engine);
|
|
288
|
+
res = roll(toCompare, engine, pity);
|
|
188
289
|
}
|
|
189
290
|
if (typeof res === "boolean") {
|
|
190
|
-
results = replaceInFormula(element, diceResult, compareResult, res, engine);
|
|
291
|
+
results = replaceInFormula(element, diceResult, compareResult, res, engine, pity);
|
|
191
292
|
} else if (res instanceof Object) {
|
|
192
293
|
const diceResult2 = res;
|
|
193
294
|
if (diceResult2.compare) {
|
|
@@ -211,17 +312,18 @@ function replaceText(element, total, dice) {
|
|
|
211
312
|
function formatComment(dice) {
|
|
212
313
|
const commentsRegex = /\[(?<comments>.*?)\]/;
|
|
213
314
|
const commentsMatch = commentsRegex.exec(dice);
|
|
214
|
-
const optionalComments = OPTIONAL_COMMENT.exec(dice);
|
|
215
315
|
const comments = commentsMatch?.groups?.comments ? `${commentsMatch.groups.comments}` : "";
|
|
316
|
+
const diceWithoutBrackets = dice.replace(commentsRegex, "");
|
|
317
|
+
const optionalCommentsRegex = /\s+(#|\/\/)(?<comment>.*)/;
|
|
318
|
+
const optionalComments = optionalCommentsRegex.exec(diceWithoutBrackets);
|
|
216
319
|
const optional = optionalComments?.groups?.comment ? `${optionalComments.groups.comment.trim()}` : "";
|
|
217
320
|
let finalComment = "";
|
|
218
|
-
if (comments && optional)
|
|
219
|
-
finalComment = `__${commentsMatch?.groups?.comments} ${optionalComments?.groups?.comment.trim()}__ \u2014 `;
|
|
321
|
+
if (comments && optional) finalComment = `__${comments} ${optional}__ \u2014 `;
|
|
220
322
|
else if (comments) finalComment = `__${comments}__ \u2014 `;
|
|
221
323
|
else if (optional) finalComment = `__${optional}__ \u2014 `;
|
|
222
324
|
return finalComment;
|
|
223
325
|
}
|
|
224
|
-
function sharedRolls(dice, engine = NumberGenerator.engines.nodeCrypto) {
|
|
326
|
+
function sharedRolls(dice, engine = NumberGenerator.engines.nodeCrypto, pity) {
|
|
225
327
|
if (dice.match(/\d+?#(.*?)/))
|
|
226
328
|
throw new DiceTypeError(
|
|
227
329
|
dice,
|
|
@@ -232,8 +334,11 @@ function sharedRolls(dice, engine = NumberGenerator.engines.nodeCrypto) {
|
|
|
232
334
|
const mainComment = /\s+#(?<comment>.*)/.exec(dice)?.groups?.comment?.trimEnd() ?? void 0;
|
|
233
335
|
const split = dice.split(";");
|
|
234
336
|
let diceMain = fixParenthesis(split[0]);
|
|
235
|
-
const
|
|
236
|
-
const
|
|
337
|
+
const commentsRegex = /\[(?<comments>.*?)\]/gi;
|
|
338
|
+
const comments = formatComment(diceMain);
|
|
339
|
+
const diceMainWithoutComments = diceMain.replace(commentsRegex, "").trim();
|
|
340
|
+
const toHideRegex = /\((?<dice>[^)]+)\)/;
|
|
341
|
+
const toHide = toHideRegex.exec(diceMainWithoutComments)?.groups;
|
|
237
342
|
let hidden = false;
|
|
238
343
|
if (toHide?.dice) {
|
|
239
344
|
diceMain = toHide.dice;
|
|
@@ -241,14 +346,13 @@ function sharedRolls(dice, engine = NumberGenerator.engines.nodeCrypto) {
|
|
|
241
346
|
} else if (toHide) {
|
|
242
347
|
diceMain = "1d1";
|
|
243
348
|
hidden = true;
|
|
349
|
+
} else {
|
|
350
|
+
diceMain = diceMainWithoutComments;
|
|
244
351
|
}
|
|
245
|
-
|
|
246
|
-
const comments = formatComment(diceMain);
|
|
247
|
-
diceMain = diceMain.replace(commentsRegex, "").trim();
|
|
248
|
-
let diceResult = roll(diceMain, engine);
|
|
352
|
+
let diceResult = roll(diceMain, engine, pity);
|
|
249
353
|
if (!diceResult || !diceResult.total) {
|
|
250
354
|
if (hidden) {
|
|
251
|
-
diceResult = roll(fixParenthesis(split[0]));
|
|
355
|
+
diceResult = roll(fixParenthesis(split[0]), engine, pity);
|
|
252
356
|
hidden = false;
|
|
253
357
|
} else return void 0;
|
|
254
358
|
}
|
|
@@ -283,7 +387,7 @@ function sharedRolls(dice, engine = NumberGenerator.engines.nodeCrypto) {
|
|
|
283
387
|
results.push(`\u25C8 ${comment}${diceAll}: ${formule} = ${evaluated}`);
|
|
284
388
|
total += Number.parseInt(evaluated, 10);
|
|
285
389
|
} catch (error) {
|
|
286
|
-
const evaluated = roll(toRoll, engine);
|
|
390
|
+
const evaluated = roll(toRoll, engine, pity);
|
|
287
391
|
if (evaluated)
|
|
288
392
|
results.push(
|
|
289
393
|
`\u25C8 ${comment}${diceAll}: ${evaluated.result.split(":").slice(1).join(":")}`
|
|
@@ -372,10 +476,10 @@ var NoStatisticsError = class extends Error {
|
|
|
372
476
|
|
|
373
477
|
// src/interfaces/constant.ts
|
|
374
478
|
var COMMENT_REGEX = /\s+(#|\/{2}|\[|\/\*)(?<comment>.*)/gi;
|
|
375
|
-
var SIGN_REGEX =
|
|
376
|
-
var SIGN_REGEX_SPACE = /[
|
|
479
|
+
var SIGN_REGEX = /==|!=|(?<![!<>])>=|(?<![!<>])<=|(?<!!)(?<![<>])>|(?<!!)(?<![<>])<|(?<!!)(?<![<>])=/;
|
|
480
|
+
var SIGN_REGEX_SPACE = /(==|!=|(?<![!<>])>=|(?<![!<>])<=|(?<!!)(?<![<>])>|(?<!!)(?<![<>])<|(?<!!)(?<![<>])=)(\S+)/;
|
|
377
481
|
var SYMBOL_DICE = "&";
|
|
378
|
-
var DETECT_CRITICAL = /\{\*?c[fs]:[
|
|
482
|
+
var DETECT_CRITICAL = /\{\*?c[fs]:([<>=]|!=)+(.+?)}/gim;
|
|
379
483
|
var OPTIONAL_COMMENT = /\s+(#|\/{2}|\[|\/\*)?(?<comment>.*)/gi;
|
|
380
484
|
|
|
381
485
|
// src/interfaces/zod.ts
|
|
@@ -595,7 +699,7 @@ import { evaluate as evaluate3 } from "mathjs";
|
|
|
595
699
|
import { Random as Random2 } from "random-js";
|
|
596
700
|
import "uniformize";
|
|
597
701
|
import { NumberGenerator as NumberGenerator3 } from "@dice-roller/rpg-dice-roller";
|
|
598
|
-
function evalStatsDice(testDice, allStats, engine = NumberGenerator3.engines.nodeCrypto) {
|
|
702
|
+
function evalStatsDice(testDice, allStats, engine = NumberGenerator3.engines.nodeCrypto, pity) {
|
|
599
703
|
let dice = testDice.trimEnd();
|
|
600
704
|
if (allStats && Object.keys(allStats).length > 0) {
|
|
601
705
|
const names = Object.keys(allStats);
|
|
@@ -608,7 +712,7 @@ function evalStatsDice(testDice, allStats, engine = NumberGenerator3.engines.nod
|
|
|
608
712
|
}
|
|
609
713
|
}
|
|
610
714
|
try {
|
|
611
|
-
if (!roll(replaceFormulaInDice(replaceExpByRandom(dice)), engine))
|
|
715
|
+
if (!roll(replaceFormulaInDice(replaceExpByRandom(dice)), engine, pity))
|
|
612
716
|
throw new DiceTypeError(dice, "evalStatsDice", "no roll result");
|
|
613
717
|
return testDice;
|
|
614
718
|
} catch (error) {
|