@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/README.md
CHANGED
|
@@ -8,6 +8,47 @@ This README documents the public API exported by the `core` package. It lists ty
|
|
|
8
8
|
|
|
9
9
|
The `core` module provides small utilities to parse and evaluate dice notation, to generate and replace statistical values in dice expressions, and to validate statistical templates. The API is intended to be consumed by higher-level modules (bot, CLI, etc.).
|
|
10
10
|
|
|
11
|
+
## Pity System
|
|
12
|
+
|
|
13
|
+
The pity system is an optional feature that automatically re-rolls dice when a comparison fails, ensuring successful outcomes. This is useful in role-playing games where certain rolls must succeed (e.g., a critical save action).
|
|
14
|
+
|
|
15
|
+
### How It Works
|
|
16
|
+
|
|
17
|
+
When the `pity` parameter is set to `true` in the `roll()` function and the dice expression includes a comparison operator (e.g., `1d6>=5`), the system:
|
|
18
|
+
|
|
19
|
+
1. Evaluates the initial roll against the comparison condition
|
|
20
|
+
2. If the condition is **not met**, automatically re-rolls and repeats until success
|
|
21
|
+
3. Tracks the number of re-rolls in the `pityLogs` field of the result
|
|
22
|
+
|
|
23
|
+
### Limitations
|
|
24
|
+
|
|
25
|
+
The pity system only activates when a **comparison is theoretically possible**. For example:
|
|
26
|
+
|
|
27
|
+
- `1d6>=7` — **Ignored** (impossible: maximum roll is 6)
|
|
28
|
+
- `1d6>=5` — **Active** (possible: can roll 5 or 6)
|
|
29
|
+
- `2d6>=12` — **Active** (possible: can roll 12)
|
|
30
|
+
- `1d20>20` — **Ignored** (impossible: maximum roll is 20, not greater than 20)
|
|
31
|
+
|
|
32
|
+
### Example
|
|
33
|
+
|
|
34
|
+
```javascript
|
|
35
|
+
const result = roll("1d6>=5", null, true);
|
|
36
|
+
// If first roll is 3, pity system re-rolls
|
|
37
|
+
// If second roll is 2, pity system re-rolls again
|
|
38
|
+
// If third roll is 6, comparison succeeds and stops
|
|
39
|
+
// result.total = 6
|
|
40
|
+
// result.pityLogs = 2 (number of re-rolls)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Without pity (`pity = false` or `undefined`), a roll of `1d6>=5` would return the result regardless of comparison outcome, with no re-rolls.
|
|
44
|
+
|
|
45
|
+
### Usage Notes
|
|
46
|
+
|
|
47
|
+
- The pity system only applies when the roll includes a comparison operator
|
|
48
|
+
- The system has a safety limit of 100 re-rolls to prevent infinite loops
|
|
49
|
+
- The `pityLogs` field is only present in the result if at least one re-roll occurred
|
|
50
|
+
- Impossible comparisons are silently ignored (no error thrown)
|
|
51
|
+
|
|
11
52
|
## Public API
|
|
12
53
|
|
|
13
54
|
Note: when a parameter `engine` is shown it usually defaults to the `NumberGenerator.engines.nodeCrypto` engine (from `@dice-roller/rpg-dice-roller`) unless otherwise specified.
|
|
@@ -21,6 +62,7 @@ Note: when a parameter `engine` is shown it usually defaults to the `NumberGener
|
|
|
21
62
|
- compare?: ComparedValue — Optional comparison attached to the roll
|
|
22
63
|
- modifier?: Modifier — Optional modifier applied to the roll
|
|
23
64
|
- total?: number — Optional numeric total of the roll
|
|
65
|
+
- pityLogs?: number — Optional count of re-rolls triggered by the pity system (see Pity System below)
|
|
24
66
|
|
|
25
67
|
#### Interface: Compare
|
|
26
68
|
- sign: "<" | ">" | ">=" | "<=" | "=" | "!=" | "=="
|
|
@@ -112,9 +154,10 @@ Note: when a parameter `engine` is shown it usually defaults to the `NumberGener
|
|
|
112
154
|
|
|
113
155
|
### Dice functions (`src/dice.ts`)
|
|
114
156
|
|
|
115
|
-
#### Function: roll(dice: string, engine?: Engine | null): Resultat | undefined
|
|
157
|
+
#### Function: roll(dice: string, engine?: Engine | null, pity?: boolean): Resultat | undefined
|
|
116
158
|
- Parse a dice notation string and perform the roll(s) using `rpg-dice-roller`.
|
|
117
159
|
- Supports comments, grouped/shared rolls, comparisons, modifiers and custom notation (see module docs).
|
|
160
|
+
- If `pity` is `true` and the roll includes a comparison (e.g., `1d6>=5`), automatically re-rolls until the comparison succeeds (see Pity System below).
|
|
118
161
|
- Returns `Resultat` when a dice expression is recognized, otherwise `undefined`.
|
|
119
162
|
- Throws `DiceTypeError` when the expression cannot be parsed or rolled.
|
|
120
163
|
|
package/dist/index.d.mts
CHANGED
|
@@ -13,8 +13,10 @@ declare function createCriticalCustom(dice: string, customCritical: CustomCritic
|
|
|
13
13
|
/**
|
|
14
14
|
* Parse the string provided and turn it as a readable dice for dice parser
|
|
15
15
|
* @param dice {string}
|
|
16
|
+
* @param engine
|
|
17
|
+
* @param pity
|
|
16
18
|
*/
|
|
17
|
-
declare function roll(dice: string, engine?: Engine | null): Resultat | undefined;
|
|
19
|
+
declare function roll(dice: string, engine?: Engine | null, pity?: boolean): Resultat | undefined;
|
|
18
20
|
/**
|
|
19
21
|
* Evaluate a formula and replace "^" by "**" if any
|
|
20
22
|
* @param {Sign} sign
|
|
@@ -84,6 +86,7 @@ interface Resultat {
|
|
|
84
86
|
* Total of the roll
|
|
85
87
|
*/
|
|
86
88
|
total?: number;
|
|
89
|
+
pityLogs?: number;
|
|
87
90
|
}
|
|
88
91
|
interface Compare {
|
|
89
92
|
/**
|
|
@@ -94,6 +97,11 @@ interface Compare {
|
|
|
94
97
|
* Value of the comparison
|
|
95
98
|
*/
|
|
96
99
|
value: number;
|
|
100
|
+
/**
|
|
101
|
+
* Indicate if the comparison is "trivial"
|
|
102
|
+
* aka if the comparaison is always true or always false
|
|
103
|
+
*/
|
|
104
|
+
trivial?: boolean;
|
|
97
105
|
}
|
|
98
106
|
/**
|
|
99
107
|
* Sign format for calculation of modifier
|
|
@@ -355,13 +363,16 @@ declare function getEngine(engine: "nativeMath" | "browserCrypto" | "nodeCrypto"
|
|
|
355
363
|
* Verify if the provided dice work with random value
|
|
356
364
|
* @param testDice {string}
|
|
357
365
|
* @param allStats {Record<string,number>}
|
|
366
|
+
* @param engine
|
|
367
|
+
* @param pity
|
|
358
368
|
*/
|
|
359
|
-
declare function evalStatsDice(testDice: string, allStats?: Record<string, number>, engine?: Engine | null): string;
|
|
369
|
+
declare function evalStatsDice(testDice: string, allStats?: Record<string, number>, engine?: Engine | null, pity?: boolean): string;
|
|
360
370
|
/**
|
|
361
371
|
* Generate a random dice and remove the formula (+ evaluate it)
|
|
362
372
|
* Used for diceDamage only
|
|
363
373
|
* @param value {string}
|
|
364
374
|
* @param template {StatisticalTemplate}
|
|
375
|
+
* @param engine
|
|
365
376
|
* @returns
|
|
366
377
|
*/
|
|
367
378
|
declare function diceRandomParse(value: string, template: StatisticalTemplate, engine?: Engine | null): string;
|
|
@@ -369,6 +380,7 @@ declare function diceRandomParse(value: string, template: StatisticalTemplate, e
|
|
|
369
380
|
* Same as damageDice but for DiceType
|
|
370
381
|
* @param dice {string}
|
|
371
382
|
* @param template {StatisticalTemplate}
|
|
383
|
+
* @param engine
|
|
372
384
|
*/
|
|
373
385
|
declare function diceTypeRandomParse(dice: string, template: StatisticalTemplate, engine?: Engine | null): string;
|
|
374
386
|
/**
|
|
@@ -387,17 +399,20 @@ declare function evalOneCombinaison(combinaison: string, stats: Record<string, n
|
|
|
387
399
|
* Parse the provided JSON and verify each field to check if everything could work when rolling
|
|
388
400
|
* @param {unknown} template
|
|
389
401
|
* @param {boolean} verify - If true, will roll the dices to check if everything is valid
|
|
402
|
+
* @param engine
|
|
390
403
|
* @returns {StatisticalTemplate}
|
|
391
404
|
*/
|
|
392
405
|
declare function verifyTemplateValue(template: unknown, verify?: boolean, engine?: Engine | null): StatisticalTemplate;
|
|
393
406
|
/**
|
|
394
407
|
* Test each damage roll from the template.damage
|
|
395
408
|
* @param {StatisticalTemplate} template
|
|
409
|
+
* @param engine
|
|
396
410
|
*/
|
|
397
411
|
declare function testDiceRegistered(template: StatisticalTemplate, engine?: Engine | null): void;
|
|
398
412
|
/**
|
|
399
413
|
* Test all combinaison with generated random value
|
|
400
414
|
* @param {StatisticalTemplate} template
|
|
415
|
+
* @param engine
|
|
401
416
|
*/
|
|
402
417
|
declare function testStatCombinaison(template: StatisticalTemplate, engine?: Engine | null): void;
|
|
403
418
|
/**
|
|
@@ -405,6 +420,7 @@ declare function testStatCombinaison(template: StatisticalTemplate, engine?: Eng
|
|
|
405
420
|
* @param {number|undefined} total
|
|
406
421
|
* @param {number | undefined} max
|
|
407
422
|
* @param {number | undefined} min
|
|
423
|
+
* @param engine
|
|
408
424
|
* @returns
|
|
409
425
|
*/
|
|
410
426
|
declare function generateRandomStat(total?: number | undefined, max?: number, min?: number, engine?: Engine | null): number;
|
package/dist/index.d.ts
CHANGED
|
@@ -13,8 +13,10 @@ declare function createCriticalCustom(dice: string, customCritical: CustomCritic
|
|
|
13
13
|
/**
|
|
14
14
|
* Parse the string provided and turn it as a readable dice for dice parser
|
|
15
15
|
* @param dice {string}
|
|
16
|
+
* @param engine
|
|
17
|
+
* @param pity
|
|
16
18
|
*/
|
|
17
|
-
declare function roll(dice: string, engine?: Engine | null): Resultat | undefined;
|
|
19
|
+
declare function roll(dice: string, engine?: Engine | null, pity?: boolean): Resultat | undefined;
|
|
18
20
|
/**
|
|
19
21
|
* Evaluate a formula and replace "^" by "**" if any
|
|
20
22
|
* @param {Sign} sign
|
|
@@ -84,6 +86,7 @@ interface Resultat {
|
|
|
84
86
|
* Total of the roll
|
|
85
87
|
*/
|
|
86
88
|
total?: number;
|
|
89
|
+
pityLogs?: number;
|
|
87
90
|
}
|
|
88
91
|
interface Compare {
|
|
89
92
|
/**
|
|
@@ -94,6 +97,11 @@ interface Compare {
|
|
|
94
97
|
* Value of the comparison
|
|
95
98
|
*/
|
|
96
99
|
value: number;
|
|
100
|
+
/**
|
|
101
|
+
* Indicate if the comparison is "trivial"
|
|
102
|
+
* aka if the comparaison is always true or always false
|
|
103
|
+
*/
|
|
104
|
+
trivial?: boolean;
|
|
97
105
|
}
|
|
98
106
|
/**
|
|
99
107
|
* Sign format for calculation of modifier
|
|
@@ -355,13 +363,16 @@ declare function getEngine(engine: "nativeMath" | "browserCrypto" | "nodeCrypto"
|
|
|
355
363
|
* Verify if the provided dice work with random value
|
|
356
364
|
* @param testDice {string}
|
|
357
365
|
* @param allStats {Record<string,number>}
|
|
366
|
+
* @param engine
|
|
367
|
+
* @param pity
|
|
358
368
|
*/
|
|
359
|
-
declare function evalStatsDice(testDice: string, allStats?: Record<string, number>, engine?: Engine | null): string;
|
|
369
|
+
declare function evalStatsDice(testDice: string, allStats?: Record<string, number>, engine?: Engine | null, pity?: boolean): string;
|
|
360
370
|
/**
|
|
361
371
|
* Generate a random dice and remove the formula (+ evaluate it)
|
|
362
372
|
* Used for diceDamage only
|
|
363
373
|
* @param value {string}
|
|
364
374
|
* @param template {StatisticalTemplate}
|
|
375
|
+
* @param engine
|
|
365
376
|
* @returns
|
|
366
377
|
*/
|
|
367
378
|
declare function diceRandomParse(value: string, template: StatisticalTemplate, engine?: Engine | null): string;
|
|
@@ -369,6 +380,7 @@ declare function diceRandomParse(value: string, template: StatisticalTemplate, e
|
|
|
369
380
|
* Same as damageDice but for DiceType
|
|
370
381
|
* @param dice {string}
|
|
371
382
|
* @param template {StatisticalTemplate}
|
|
383
|
+
* @param engine
|
|
372
384
|
*/
|
|
373
385
|
declare function diceTypeRandomParse(dice: string, template: StatisticalTemplate, engine?: Engine | null): string;
|
|
374
386
|
/**
|
|
@@ -387,17 +399,20 @@ declare function evalOneCombinaison(combinaison: string, stats: Record<string, n
|
|
|
387
399
|
* Parse the provided JSON and verify each field to check if everything could work when rolling
|
|
388
400
|
* @param {unknown} template
|
|
389
401
|
* @param {boolean} verify - If true, will roll the dices to check if everything is valid
|
|
402
|
+
* @param engine
|
|
390
403
|
* @returns {StatisticalTemplate}
|
|
391
404
|
*/
|
|
392
405
|
declare function verifyTemplateValue(template: unknown, verify?: boolean, engine?: Engine | null): StatisticalTemplate;
|
|
393
406
|
/**
|
|
394
407
|
* Test each damage roll from the template.damage
|
|
395
408
|
* @param {StatisticalTemplate} template
|
|
409
|
+
* @param engine
|
|
396
410
|
*/
|
|
397
411
|
declare function testDiceRegistered(template: StatisticalTemplate, engine?: Engine | null): void;
|
|
398
412
|
/**
|
|
399
413
|
* Test all combinaison with generated random value
|
|
400
414
|
* @param {StatisticalTemplate} template
|
|
415
|
+
* @param engine
|
|
401
416
|
*/
|
|
402
417
|
declare function testStatCombinaison(template: StatisticalTemplate, engine?: Engine | null): void;
|
|
403
418
|
/**
|
|
@@ -405,6 +420,7 @@ declare function testStatCombinaison(template: StatisticalTemplate, engine?: Eng
|
|
|
405
420
|
* @param {number|undefined} total
|
|
406
421
|
* @param {number | undefined} max
|
|
407
422
|
* @param {number | undefined} min
|
|
423
|
+
* @param engine
|
|
408
424
|
* @returns
|
|
409
425
|
*/
|
|
410
426
|
declare function generateRandomStat(total?: number | undefined, max?: number, min?: number, engine?: Engine | null): number;
|
package/dist/index.js
CHANGED
|
@@ -61,17 +61,47 @@ module.exports = __toCommonJS(index_exports);
|
|
|
61
61
|
// src/dice.ts
|
|
62
62
|
var import_rpg_dice_roller = require("@dice-roller/rpg-dice-roller");
|
|
63
63
|
var import_mathjs = require("mathjs");
|
|
64
|
-
function
|
|
65
|
-
|
|
64
|
+
function isTrivialComparison(maxValue, minValue, compare) {
|
|
65
|
+
const canSucceed = canComparisonSucceed(maxValue, compare, minValue);
|
|
66
|
+
const canFail = canComparisonFail(maxValue, compare, minValue);
|
|
67
|
+
return !canSucceed || !canFail;
|
|
68
|
+
}
|
|
69
|
+
function canComparisonFail(maxRollValue, compare, minRollValue = 1) {
|
|
70
|
+
switch (compare.sign) {
|
|
71
|
+
case ">":
|
|
72
|
+
return minRollValue <= compare.value;
|
|
73
|
+
// failure if roll <= value
|
|
74
|
+
case ">=":
|
|
75
|
+
return minRollValue < compare.value;
|
|
76
|
+
// failure if roll < value
|
|
77
|
+
case "<":
|
|
78
|
+
return maxRollValue >= compare.value;
|
|
79
|
+
// failure if roll >= value
|
|
80
|
+
case "<=":
|
|
81
|
+
return maxRollValue > compare.value;
|
|
82
|
+
// failure if roll > value
|
|
83
|
+
case "=":
|
|
84
|
+
case "==":
|
|
85
|
+
return minRollValue !== compare.value || maxRollValue !== compare.value;
|
|
86
|
+
// can differ
|
|
87
|
+
case "!=":
|
|
88
|
+
return minRollValue <= compare.value && compare.value <= maxRollValue;
|
|
89
|
+
// equality possible
|
|
90
|
+
default:
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function getCompare(dice, compareRegex, engine = import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto, pity) {
|
|
95
|
+
if (dice.match(/((\{.*,(.*)+\}|([><=!]+\d+f))([><=]|!=)+\d+\}?)|\{(.*)(([><=]|!=)+).*\}/))
|
|
66
96
|
return { dice, compare: void 0 };
|
|
67
97
|
dice = dice.replace(SIGN_REGEX_SPACE, "");
|
|
68
98
|
let compare;
|
|
69
|
-
const calc = compareRegex[
|
|
99
|
+
const calc = compareRegex[2];
|
|
70
100
|
const sign = calc.match(/[+-/*^]/)?.[0];
|
|
71
101
|
const compareSign = compareRegex[0].match(SIGN_REGEX)?.[0];
|
|
72
102
|
if (sign) {
|
|
73
103
|
const toCalc = calc.replace(SIGN_REGEX, "").replace(/\s/g, "").replace(/;(.*)/, "");
|
|
74
|
-
const rCompare = rollCompare(toCalc, engine);
|
|
104
|
+
const rCompare = rollCompare(toCalc, engine, pity);
|
|
75
105
|
const total = (0, import_mathjs.evaluate)(rCompare.value.toString());
|
|
76
106
|
dice = dice.replace(SIGN_REGEX_SPACE, `${compareSign}${total}`);
|
|
77
107
|
compare = {
|
|
@@ -81,7 +111,7 @@ function getCompare(dice, compareRegex, engine = import_rpg_dice_roller.NumberGe
|
|
|
81
111
|
rollValue: rCompare.diceResult
|
|
82
112
|
};
|
|
83
113
|
} else {
|
|
84
|
-
const rcompare = rollCompare(calc, engine);
|
|
114
|
+
const rcompare = rollCompare(calc, engine, pity);
|
|
85
115
|
compare = {
|
|
86
116
|
sign: compareSign,
|
|
87
117
|
value: rcompare.value,
|
|
@@ -91,11 +121,19 @@ function getCompare(dice, compareRegex, engine = import_rpg_dice_roller.NumberGe
|
|
|
91
121
|
}
|
|
92
122
|
return { dice, compare };
|
|
93
123
|
}
|
|
94
|
-
function rollCompare(value, engine = import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto) {
|
|
124
|
+
function rollCompare(value, engine = import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto, pity) {
|
|
95
125
|
if (isNumber(value)) return { value: Number.parseInt(value, 10) };
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
126
|
+
if (!value || typeof value === "string" && value.trim() === "") {
|
|
127
|
+
return { value: 0, diceResult: value };
|
|
128
|
+
}
|
|
129
|
+
const rollComp = roll(value, engine, pity);
|
|
130
|
+
if (!rollComp?.total) {
|
|
131
|
+
try {
|
|
132
|
+
return { value: (0, import_mathjs.evaluate)(value), diceResult: value };
|
|
133
|
+
} catch (error) {
|
|
134
|
+
return { value: 0, diceResult: value };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
99
137
|
return {
|
|
100
138
|
dice: value,
|
|
101
139
|
value: rollComp.total,
|
|
@@ -134,20 +172,20 @@ function getModifier(dice) {
|
|
|
134
172
|
}
|
|
135
173
|
return modificator;
|
|
136
174
|
}
|
|
137
|
-
function roll(dice, engine = import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto) {
|
|
175
|
+
function roll(dice, engine = import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto, pity) {
|
|
138
176
|
dice = standardizeDice(replaceFormulaInDice(dice)).replace(/^\+/, "").replaceAll("=>", ">=").replaceAll("=<", "<=").trimStart();
|
|
139
177
|
if (!dice.includes("d")) return void 0;
|
|
140
178
|
dice = dice.replaceAll(DETECT_CRITICAL, "").trimEnd();
|
|
141
179
|
const compareRegex = dice.match(SIGN_REGEX_SPACE);
|
|
142
180
|
let compare;
|
|
143
181
|
if (dice.includes(";")) return sharedRolls(dice, engine);
|
|
182
|
+
dice = fixParenthesis(dice);
|
|
183
|
+
const modificator = getModifier(dice);
|
|
144
184
|
if (compareRegex) {
|
|
145
|
-
const compareResult = getCompare(dice, compareRegex, engine);
|
|
185
|
+
const compareResult = getCompare(dice, compareRegex, engine, pity);
|
|
146
186
|
dice = compareResult.dice;
|
|
147
187
|
compare = compareResult.compare;
|
|
148
188
|
}
|
|
149
|
-
dice = fixParenthesis(dice);
|
|
150
|
-
const modificator = getModifier(dice);
|
|
151
189
|
if (dice.match(/\d+?#(.*)/)) {
|
|
152
190
|
const diceArray = dice.split("#");
|
|
153
191
|
const numberOfDice = Number.parseInt(diceArray[0], 10);
|
|
@@ -175,22 +213,85 @@ function roll(dice, engine = import_rpg_dice_roller.NumberGenerator.engines.node
|
|
|
175
213
|
const roller = new import_rpg_dice_roller.DiceRoller();
|
|
176
214
|
import_rpg_dice_roller.NumberGenerator.generator.engine = engine;
|
|
177
215
|
const diceWithoutComment = dice.replace(COMMENT_REGEX, "").trimEnd();
|
|
216
|
+
let diceRoll;
|
|
178
217
|
try {
|
|
179
|
-
roller.roll(diceWithoutComment);
|
|
218
|
+
diceRoll = roller.roll(diceWithoutComment);
|
|
180
219
|
} catch (error) {
|
|
181
220
|
throw new DiceTypeError(diceWithoutComment, "roll", error);
|
|
182
221
|
}
|
|
222
|
+
if (compare && diceRoll) {
|
|
223
|
+
const currentRoll = Array.isArray(diceRoll) ? diceRoll[0] : diceRoll;
|
|
224
|
+
const maxDiceValue = currentRoll.maxTotal;
|
|
225
|
+
const minDiceValue = currentRoll.minTotal;
|
|
226
|
+
const trivial = isTrivialComparison(maxDiceValue, minDiceValue, compare);
|
|
227
|
+
compare.trivial = trivial ? true : void 0;
|
|
228
|
+
}
|
|
183
229
|
const commentMatch = dice.match(COMMENT_REGEX);
|
|
184
230
|
const comment = commentMatch ? commentMatch[2] : void 0;
|
|
231
|
+
let rerollCount = 0;
|
|
232
|
+
let res;
|
|
233
|
+
if (pity && compare) {
|
|
234
|
+
const currentRoll = Array.isArray(diceRoll) ? diceRoll[0] : diceRoll;
|
|
235
|
+
const maxPossible = currentRoll ? currentRoll.maxTotal : null;
|
|
236
|
+
const isComparisonPossible = maxPossible === null || canComparisonSucceed(maxPossible, compare);
|
|
237
|
+
if (isComparisonPossible) {
|
|
238
|
+
let isFail = (0, import_mathjs.evaluate)(`${roller.total}${compare.sign}${compare.value}`);
|
|
239
|
+
if (!isFail) {
|
|
240
|
+
const maxReroll = 100;
|
|
241
|
+
while (!isFail && rerollCount < maxReroll) {
|
|
242
|
+
try {
|
|
243
|
+
res = roll(diceWithoutComment, engine, false);
|
|
244
|
+
} catch (error) {
|
|
245
|
+
throw new DiceTypeError(diceWithoutComment, "roll", error);
|
|
246
|
+
}
|
|
247
|
+
rerollCount++;
|
|
248
|
+
if (res && res.total !== void 0)
|
|
249
|
+
isFail = (0, import_mathjs.evaluate)(`${res.total}${compare.sign}${compare.value}`);
|
|
250
|
+
}
|
|
251
|
+
if (res) {
|
|
252
|
+
return {
|
|
253
|
+
...res,
|
|
254
|
+
dice,
|
|
255
|
+
comment,
|
|
256
|
+
compare,
|
|
257
|
+
modifier: modificator,
|
|
258
|
+
pityLogs: rerollCount
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
} else ;
|
|
263
|
+
}
|
|
185
264
|
return {
|
|
186
265
|
dice,
|
|
187
266
|
result: roller.output,
|
|
188
267
|
comment,
|
|
189
268
|
compare: compare ? compare : void 0,
|
|
190
269
|
modifier: modificator,
|
|
191
|
-
total: roller.total
|
|
270
|
+
total: roller.total,
|
|
271
|
+
pityLogs: rerollCount > 0 ? rerollCount : void 0
|
|
192
272
|
};
|
|
193
273
|
}
|
|
274
|
+
function canComparisonSucceed(maxRollValue, compare, minRollValue) {
|
|
275
|
+
switch (compare.sign) {
|
|
276
|
+
case ">":
|
|
277
|
+
return maxRollValue > compare.value;
|
|
278
|
+
case ">=":
|
|
279
|
+
return maxRollValue >= compare.value;
|
|
280
|
+
case "<":
|
|
281
|
+
return compare.value > (minRollValue ?? 1);
|
|
282
|
+
// Au moins minRollValue possible
|
|
283
|
+
case "<=":
|
|
284
|
+
return compare.value >= (minRollValue ?? 1);
|
|
285
|
+
// Au moins minRollValue possible
|
|
286
|
+
case "=":
|
|
287
|
+
case "==":
|
|
288
|
+
return maxRollValue >= compare.value && compare.value >= (minRollValue ?? 1);
|
|
289
|
+
case "!=":
|
|
290
|
+
return maxRollValue !== compare.value || (minRollValue ?? 1) !== compare.value;
|
|
291
|
+
default:
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
194
295
|
function fixParenthesis(dice) {
|
|
195
296
|
const parenthesisRegex = /d\((\d+)\)/g;
|
|
196
297
|
return dice.replaceAll(parenthesisRegex, (_match, p1) => `d${p1}`);
|
|
@@ -217,7 +318,7 @@ function inverseSign(sign) {
|
|
|
217
318
|
return "==";
|
|
218
319
|
}
|
|
219
320
|
}
|
|
220
|
-
function replaceInFormula(element, diceResult, compareResult, res, engine = import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto) {
|
|
321
|
+
function replaceInFormula(element, diceResult, compareResult, res, engine = import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto, pity) {
|
|
221
322
|
const { formule, diceAll } = replaceText(
|
|
222
323
|
element,
|
|
223
324
|
diceResult.total ?? 0,
|
|
@@ -230,13 +331,13 @@ function replaceInFormula(element, diceResult, compareResult, res, engine = impo
|
|
|
230
331
|
evaluateRoll = (0, import_mathjs.evaluate)(compareResult.dice);
|
|
231
332
|
return `${validSign} ${diceAll}: ${formule} = ${evaluateRoll}${invertedSign}${compareResult.compare?.value}`;
|
|
232
333
|
} catch (error) {
|
|
233
|
-
const evaluateRoll2 = roll(compareResult.dice, engine);
|
|
334
|
+
const evaluateRoll2 = roll(compareResult.dice, engine, pity);
|
|
234
335
|
if (evaluateRoll2)
|
|
235
336
|
return `${validSign} ${diceAll}: ${evaluateRoll2.result.split(":").splice(1).join(":")}`;
|
|
236
337
|
return `${validSign} ${diceAll}: ${formule} = ${evaluateRoll2}${invertedSign}${compareResult.compare?.value}`;
|
|
237
338
|
}
|
|
238
339
|
}
|
|
239
|
-
function compareSignFormule(toRoll, compareRegex, element, diceResult, engine = import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto) {
|
|
340
|
+
function compareSignFormule(toRoll, compareRegex, element, diceResult, engine = import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto, pity) {
|
|
240
341
|
let results = "";
|
|
241
342
|
const compareResult = getCompare(toRoll, compareRegex, engine);
|
|
242
343
|
const toCompare = `${compareResult.dice}${compareResult.compare?.sign}${compareResult.compare?.value}`;
|
|
@@ -244,10 +345,10 @@ function compareSignFormule(toRoll, compareRegex, element, diceResult, engine =
|
|
|
244
345
|
try {
|
|
245
346
|
res = (0, import_mathjs.evaluate)(toCompare);
|
|
246
347
|
} catch (error) {
|
|
247
|
-
res = roll(toCompare, engine);
|
|
348
|
+
res = roll(toCompare, engine, pity);
|
|
248
349
|
}
|
|
249
350
|
if (typeof res === "boolean") {
|
|
250
|
-
results = replaceInFormula(element, diceResult, compareResult, res, engine);
|
|
351
|
+
results = replaceInFormula(element, diceResult, compareResult, res, engine, pity);
|
|
251
352
|
} else if (res instanceof Object) {
|
|
252
353
|
const diceResult2 = res;
|
|
253
354
|
if (diceResult2.compare) {
|
|
@@ -271,17 +372,18 @@ function replaceText(element, total, dice) {
|
|
|
271
372
|
function formatComment(dice) {
|
|
272
373
|
const commentsRegex = /\[(?<comments>.*?)\]/;
|
|
273
374
|
const commentsMatch = commentsRegex.exec(dice);
|
|
274
|
-
const optionalComments = OPTIONAL_COMMENT.exec(dice);
|
|
275
375
|
const comments = commentsMatch?.groups?.comments ? `${commentsMatch.groups.comments}` : "";
|
|
376
|
+
const diceWithoutBrackets = dice.replace(commentsRegex, "");
|
|
377
|
+
const optionalCommentsRegex = /\s+(#|\/\/)(?<comment>.*)/;
|
|
378
|
+
const optionalComments = optionalCommentsRegex.exec(diceWithoutBrackets);
|
|
276
379
|
const optional = optionalComments?.groups?.comment ? `${optionalComments.groups.comment.trim()}` : "";
|
|
277
380
|
let finalComment = "";
|
|
278
|
-
if (comments && optional)
|
|
279
|
-
finalComment = `__${commentsMatch?.groups?.comments} ${optionalComments?.groups?.comment.trim()}__ \u2014 `;
|
|
381
|
+
if (comments && optional) finalComment = `__${comments} ${optional}__ \u2014 `;
|
|
280
382
|
else if (comments) finalComment = `__${comments}__ \u2014 `;
|
|
281
383
|
else if (optional) finalComment = `__${optional}__ \u2014 `;
|
|
282
384
|
return finalComment;
|
|
283
385
|
}
|
|
284
|
-
function sharedRolls(dice, engine = import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto) {
|
|
386
|
+
function sharedRolls(dice, engine = import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto, pity) {
|
|
285
387
|
if (dice.match(/\d+?#(.*?)/))
|
|
286
388
|
throw new DiceTypeError(
|
|
287
389
|
dice,
|
|
@@ -292,8 +394,11 @@ function sharedRolls(dice, engine = import_rpg_dice_roller.NumberGenerator.engin
|
|
|
292
394
|
const mainComment = /\s+#(?<comment>.*)/.exec(dice)?.groups?.comment?.trimEnd() ?? void 0;
|
|
293
395
|
const split = dice.split(";");
|
|
294
396
|
let diceMain = fixParenthesis(split[0]);
|
|
295
|
-
const
|
|
296
|
-
const
|
|
397
|
+
const commentsRegex = /\[(?<comments>.*?)\]/gi;
|
|
398
|
+
const comments = formatComment(diceMain);
|
|
399
|
+
const diceMainWithoutComments = diceMain.replace(commentsRegex, "").trim();
|
|
400
|
+
const toHideRegex = /\((?<dice>[^)]+)\)/;
|
|
401
|
+
const toHide = toHideRegex.exec(diceMainWithoutComments)?.groups;
|
|
297
402
|
let hidden = false;
|
|
298
403
|
if (toHide?.dice) {
|
|
299
404
|
diceMain = toHide.dice;
|
|
@@ -301,14 +406,13 @@ function sharedRolls(dice, engine = import_rpg_dice_roller.NumberGenerator.engin
|
|
|
301
406
|
} else if (toHide) {
|
|
302
407
|
diceMain = "1d1";
|
|
303
408
|
hidden = true;
|
|
409
|
+
} else {
|
|
410
|
+
diceMain = diceMainWithoutComments;
|
|
304
411
|
}
|
|
305
|
-
|
|
306
|
-
const comments = formatComment(diceMain);
|
|
307
|
-
diceMain = diceMain.replace(commentsRegex, "").trim();
|
|
308
|
-
let diceResult = roll(diceMain, engine);
|
|
412
|
+
let diceResult = roll(diceMain, engine, pity);
|
|
309
413
|
if (!diceResult || !diceResult.total) {
|
|
310
414
|
if (hidden) {
|
|
311
|
-
diceResult = roll(fixParenthesis(split[0]));
|
|
415
|
+
diceResult = roll(fixParenthesis(split[0]), engine, pity);
|
|
312
416
|
hidden = false;
|
|
313
417
|
} else return void 0;
|
|
314
418
|
}
|
|
@@ -343,7 +447,7 @@ function sharedRolls(dice, engine = import_rpg_dice_roller.NumberGenerator.engin
|
|
|
343
447
|
results.push(`\u25C8 ${comment}${diceAll}: ${formule} = ${evaluated}`);
|
|
344
448
|
total += Number.parseInt(evaluated, 10);
|
|
345
449
|
} catch (error) {
|
|
346
|
-
const evaluated = roll(toRoll, engine);
|
|
450
|
+
const evaluated = roll(toRoll, engine, pity);
|
|
347
451
|
if (evaluated)
|
|
348
452
|
results.push(
|
|
349
453
|
`\u25C8 ${comment}${diceAll}: ${evaluated.result.split(":").slice(1).join(":")}`
|
|
@@ -432,10 +536,10 @@ var NoStatisticsError = class extends Error {
|
|
|
432
536
|
|
|
433
537
|
// src/interfaces/constant.ts
|
|
434
538
|
var COMMENT_REGEX = /\s+(#|\/{2}|\[|\/\*)(?<comment>.*)/gi;
|
|
435
|
-
var SIGN_REGEX =
|
|
436
|
-
var SIGN_REGEX_SPACE = /[
|
|
539
|
+
var SIGN_REGEX = /==|!=|(?<![!<>])>=|(?<![!<>])<=|(?<!!)(?<![<>])>|(?<!!)(?<![<>])<|(?<!!)(?<![<>])=/;
|
|
540
|
+
var SIGN_REGEX_SPACE = /(==|!=|(?<![!<>])>=|(?<![!<>])<=|(?<!!)(?<![<>])>|(?<!!)(?<![<>])<|(?<!!)(?<![<>])=)(\S+)/;
|
|
437
541
|
var SYMBOL_DICE = "&";
|
|
438
|
-
var DETECT_CRITICAL = /\{\*?c[fs]:[
|
|
542
|
+
var DETECT_CRITICAL = /\{\*?c[fs]:([<>=]|!=)+(.+?)}/gim;
|
|
439
543
|
var OPTIONAL_COMMENT = /\s+(#|\/{2}|\[|\/\*)?(?<comment>.*)/gi;
|
|
440
544
|
|
|
441
545
|
// src/interfaces/zod.ts
|
|
@@ -655,7 +759,7 @@ var import_mathjs3 = require("mathjs");
|
|
|
655
759
|
var import_random_js2 = require("random-js");
|
|
656
760
|
var import_uniformize2 = require("uniformize");
|
|
657
761
|
var import_rpg_dice_roller3 = require("@dice-roller/rpg-dice-roller");
|
|
658
|
-
function evalStatsDice(testDice, allStats, engine = import_rpg_dice_roller3.NumberGenerator.engines.nodeCrypto) {
|
|
762
|
+
function evalStatsDice(testDice, allStats, engine = import_rpg_dice_roller3.NumberGenerator.engines.nodeCrypto, pity) {
|
|
659
763
|
let dice = testDice.trimEnd();
|
|
660
764
|
if (allStats && Object.keys(allStats).length > 0) {
|
|
661
765
|
const names = Object.keys(allStats);
|
|
@@ -668,7 +772,7 @@ function evalStatsDice(testDice, allStats, engine = import_rpg_dice_roller3.Numb
|
|
|
668
772
|
}
|
|
669
773
|
}
|
|
670
774
|
try {
|
|
671
|
-
if (!roll(replaceFormulaInDice(replaceExpByRandom(dice)), engine))
|
|
775
|
+
if (!roll(replaceFormulaInDice(replaceExpByRandom(dice)), engine, pity))
|
|
672
776
|
throw new DiceTypeError(dice, "evalStatsDice", "no roll result");
|
|
673
777
|
return testDice;
|
|
674
778
|
} catch (error) {
|