@dicelette/core 1.22.3 → 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 +128 -28
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +128 -28
- package/dist/index.mjs.map +1 -1
- package/package.json +57 -57
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,7 +61,37 @@ 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
|
|
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) {
|
|
65
95
|
if (dice.match(/((\{.*,(.*)+\}|([><=!]+\d+f))([><=]|!=)+\d+\}?)|\{(.*)(([><=]|!=)+).*\}/))
|
|
66
96
|
return { dice, compare: void 0 };
|
|
67
97
|
dice = dice.replace(SIGN_REGEX_SPACE, "");
|
|
@@ -71,7 +101,7 @@ function getCompare(dice, compareRegex, engine = import_rpg_dice_roller.NumberGe
|
|
|
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) {
|
|
@@ -277,13 +378,12 @@ function formatComment(dice) {
|
|
|
277
378
|
const optionalComments = optionalCommentsRegex.exec(diceWithoutBrackets);
|
|
278
379
|
const optional = optionalComments?.groups?.comment ? `${optionalComments.groups.comment.trim()}` : "";
|
|
279
380
|
let finalComment = "";
|
|
280
|
-
if (comments && optional)
|
|
281
|
-
finalComment = `__${comments} ${optional}__ \u2014 `;
|
|
381
|
+
if (comments && optional) finalComment = `__${comments} ${optional}__ \u2014 `;
|
|
282
382
|
else if (comments) finalComment = `__${comments}__ \u2014 `;
|
|
283
383
|
else if (optional) finalComment = `__${optional}__ \u2014 `;
|
|
284
384
|
return finalComment;
|
|
285
385
|
}
|
|
286
|
-
function sharedRolls(dice, engine = import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto) {
|
|
386
|
+
function sharedRolls(dice, engine = import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto, pity) {
|
|
287
387
|
if (dice.match(/\d+?#(.*?)/))
|
|
288
388
|
throw new DiceTypeError(
|
|
289
389
|
dice,
|
|
@@ -309,10 +409,10 @@ function sharedRolls(dice, engine = import_rpg_dice_roller.NumberGenerator.engin
|
|
|
309
409
|
} else {
|
|
310
410
|
diceMain = diceMainWithoutComments;
|
|
311
411
|
}
|
|
312
|
-
let diceResult = roll(diceMain, engine);
|
|
412
|
+
let diceResult = roll(diceMain, engine, pity);
|
|
313
413
|
if (!diceResult || !diceResult.total) {
|
|
314
414
|
if (hidden) {
|
|
315
|
-
diceResult = roll(fixParenthesis(split[0]));
|
|
415
|
+
diceResult = roll(fixParenthesis(split[0]), engine, pity);
|
|
316
416
|
hidden = false;
|
|
317
417
|
} else return void 0;
|
|
318
418
|
}
|
|
@@ -347,7 +447,7 @@ function sharedRolls(dice, engine = import_rpg_dice_roller.NumberGenerator.engin
|
|
|
347
447
|
results.push(`\u25C8 ${comment}${diceAll}: ${formule} = ${evaluated}`);
|
|
348
448
|
total += Number.parseInt(evaluated, 10);
|
|
349
449
|
} catch (error) {
|
|
350
|
-
const evaluated = roll(toRoll, engine);
|
|
450
|
+
const evaluated = roll(toRoll, engine, pity);
|
|
351
451
|
if (evaluated)
|
|
352
452
|
results.push(
|
|
353
453
|
`\u25C8 ${comment}${diceAll}: ${evaluated.result.split(":").slice(1).join(":")}`
|
|
@@ -436,8 +536,8 @@ var NoStatisticsError = class extends Error {
|
|
|
436
536
|
|
|
437
537
|
// src/interfaces/constant.ts
|
|
438
538
|
var COMMENT_REGEX = /\s+(#|\/{2}|\[|\/\*)(?<comment>.*)/gi;
|
|
439
|
-
var SIGN_REGEX =
|
|
440
|
-
var SIGN_REGEX_SPACE = /(
|
|
539
|
+
var SIGN_REGEX = /==|!=|(?<![!<>])>=|(?<![!<>])<=|(?<!!)(?<![<>])>|(?<!!)(?<![<>])<|(?<!!)(?<![<>])=/;
|
|
540
|
+
var SIGN_REGEX_SPACE = /(==|!=|(?<![!<>])>=|(?<![!<>])<=|(?<!!)(?<![<>])>|(?<!!)(?<![<>])<|(?<!!)(?<![<>])=)(\S+)/;
|
|
441
541
|
var SYMBOL_DICE = "&";
|
|
442
542
|
var DETECT_CRITICAL = /\{\*?c[fs]:([<>=]|!=)+(.+?)}/gim;
|
|
443
543
|
var OPTIONAL_COMMENT = /\s+(#|\/{2}|\[|\/\*)?(?<comment>.*)/gi;
|
|
@@ -659,7 +759,7 @@ var import_mathjs3 = require("mathjs");
|
|
|
659
759
|
var import_random_js2 = require("random-js");
|
|
660
760
|
var import_uniformize2 = require("uniformize");
|
|
661
761
|
var import_rpg_dice_roller3 = require("@dice-roller/rpg-dice-roller");
|
|
662
|
-
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) {
|
|
663
763
|
let dice = testDice.trimEnd();
|
|
664
764
|
if (allStats && Object.keys(allStats).length > 0) {
|
|
665
765
|
const names = Object.keys(allStats);
|
|
@@ -672,7 +772,7 @@ function evalStatsDice(testDice, allStats, engine = import_rpg_dice_roller3.Numb
|
|
|
672
772
|
}
|
|
673
773
|
}
|
|
674
774
|
try {
|
|
675
|
-
if (!roll(replaceFormulaInDice(replaceExpByRandom(dice)), engine))
|
|
775
|
+
if (!roll(replaceFormulaInDice(replaceExpByRandom(dice)), engine, pity))
|
|
676
776
|
throw new DiceTypeError(dice, "evalStatsDice", "no roll result");
|
|
677
777
|
return testDice;
|
|
678
778
|
} catch (error) {
|