@dicelette/core 1.22.3 → 1.23.1
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 +143 -32
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +143 -32
- package/dist/index.mjs.map +1 -1
- package/package.json +58 -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,24 +331,27 @@ 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 = "";
|
|
342
|
+
let trivial = false;
|
|
241
343
|
const compareResult = getCompare(toRoll, compareRegex, engine);
|
|
242
344
|
const toCompare = `${compareResult.dice}${compareResult.compare?.sign}${compareResult.compare?.value}`;
|
|
243
345
|
let res;
|
|
244
346
|
try {
|
|
245
347
|
res = (0, import_mathjs.evaluate)(toCompare);
|
|
246
348
|
} catch (error) {
|
|
247
|
-
res = roll(toCompare, engine);
|
|
349
|
+
res = roll(toCompare, engine, pity);
|
|
248
350
|
}
|
|
249
351
|
if (typeof res === "boolean") {
|
|
250
|
-
|
|
352
|
+
trivial = true;
|
|
353
|
+
if (compareResult.compare) compareResult.compare.trivial = true;
|
|
354
|
+
results = replaceInFormula(element, diceResult, compareResult, res, engine, pity);
|
|
251
355
|
} else if (res instanceof Object) {
|
|
252
356
|
const diceResult2 = res;
|
|
253
357
|
if (diceResult2.compare) {
|
|
@@ -258,9 +362,10 @@ function compareSignFormule(toRoll, compareRegex, element, diceResult, engine =
|
|
|
258
362
|
const invertedSign = toEvaluate ? diceResult2.compare.sign : inverseSign(diceResult2.compare.sign);
|
|
259
363
|
const dice = replaceText(element, 0, diceResult2.dice).diceAll;
|
|
260
364
|
results = `${sign} ${dice}: ${diceResult2.result.split(":").splice(1).join(":").trim()}${invertedSign}${diceResult2.compare.value}`;
|
|
365
|
+
if (diceResult2.compare.trivial) trivial = true;
|
|
261
366
|
}
|
|
262
367
|
}
|
|
263
|
-
return { dice: compareResult.dice, results };
|
|
368
|
+
return { dice: compareResult.dice, results, compare: compareResult.compare, trivial };
|
|
264
369
|
}
|
|
265
370
|
function replaceText(element, total, dice) {
|
|
266
371
|
return {
|
|
@@ -277,13 +382,12 @@ function formatComment(dice) {
|
|
|
277
382
|
const optionalComments = optionalCommentsRegex.exec(diceWithoutBrackets);
|
|
278
383
|
const optional = optionalComments?.groups?.comment ? `${optionalComments.groups.comment.trim()}` : "";
|
|
279
384
|
let finalComment = "";
|
|
280
|
-
if (comments && optional)
|
|
281
|
-
finalComment = `__${comments} ${optional}__ \u2014 `;
|
|
385
|
+
if (comments && optional) finalComment = `__${comments} ${optional}__ \u2014 `;
|
|
282
386
|
else if (comments) finalComment = `__${comments}__ \u2014 `;
|
|
283
387
|
else if (optional) finalComment = `__${optional}__ \u2014 `;
|
|
284
388
|
return finalComment;
|
|
285
389
|
}
|
|
286
|
-
function sharedRolls(dice, engine = import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto) {
|
|
390
|
+
function sharedRolls(dice, engine = import_rpg_dice_roller.NumberGenerator.engines.nodeCrypto, pity) {
|
|
287
391
|
if (dice.match(/\d+?#(.*?)/))
|
|
288
392
|
throw new DiceTypeError(
|
|
289
393
|
dice,
|
|
@@ -309,14 +413,16 @@ function sharedRolls(dice, engine = import_rpg_dice_roller.NumberGenerator.engin
|
|
|
309
413
|
} else {
|
|
310
414
|
diceMain = diceMainWithoutComments;
|
|
311
415
|
}
|
|
312
|
-
let diceResult = roll(diceMain, engine);
|
|
416
|
+
let diceResult = roll(diceMain, engine, pity);
|
|
313
417
|
if (!diceResult || !diceResult.total) {
|
|
314
418
|
if (hidden) {
|
|
315
|
-
diceResult = roll(fixParenthesis(split[0]));
|
|
419
|
+
diceResult = roll(fixParenthesis(split[0]), engine, pity);
|
|
316
420
|
hidden = false;
|
|
317
421
|
} else return void 0;
|
|
318
422
|
}
|
|
319
423
|
if (!diceResult || !diceResult.total) return void 0;
|
|
424
|
+
let aggregatedCompare = diceResult.compare;
|
|
425
|
+
let hasTrivialComparison = diceResult.compare?.trivial === true;
|
|
320
426
|
results.push(`\u203B ${comments}${diceResult.result}`);
|
|
321
427
|
let total = diceResult.total;
|
|
322
428
|
diceResult.comment = mainComment;
|
|
@@ -336,6 +442,8 @@ function sharedRolls(dice, engine = import_rpg_dice_roller.NumberGenerator.engin
|
|
|
336
442
|
);
|
|
337
443
|
toRoll = compareResult.dice;
|
|
338
444
|
results.push(compareResult.results);
|
|
445
|
+
if (!aggregatedCompare && compareResult.compare) aggregatedCompare = compareResult.compare;
|
|
446
|
+
if (compareResult.trivial) hasTrivialComparison = true;
|
|
339
447
|
} else {
|
|
340
448
|
const { formule, diceAll } = replaceText(
|
|
341
449
|
element,
|
|
@@ -347,12 +455,15 @@ function sharedRolls(dice, engine = import_rpg_dice_roller.NumberGenerator.engin
|
|
|
347
455
|
results.push(`\u25C8 ${comment}${diceAll}: ${formule} = ${evaluated}`);
|
|
348
456
|
total += Number.parseInt(evaluated, 10);
|
|
349
457
|
} catch (error) {
|
|
350
|
-
const evaluated = roll(toRoll, engine);
|
|
351
|
-
if (evaluated)
|
|
458
|
+
const evaluated = roll(toRoll, engine, pity);
|
|
459
|
+
if (evaluated) {
|
|
352
460
|
results.push(
|
|
353
461
|
`\u25C8 ${comment}${diceAll}: ${evaluated.result.split(":").slice(1).join(":")}`
|
|
354
462
|
);
|
|
355
|
-
|
|
463
|
+
if (!aggregatedCompare && evaluated.compare)
|
|
464
|
+
aggregatedCompare = evaluated.compare;
|
|
465
|
+
if (evaluated.compare?.trivial) hasTrivialComparison = true;
|
|
466
|
+
} else results.push(`\u25C8 ${comment}${diceAll}: ${formule} = ${evaluated}`);
|
|
356
467
|
total += evaluated?.total ?? 0;
|
|
357
468
|
}
|
|
358
469
|
}
|
|
@@ -363,7 +474,7 @@ function sharedRolls(dice, engine = import_rpg_dice_roller.NumberGenerator.engin
|
|
|
363
474
|
dice: diceMain,
|
|
364
475
|
result: results.join(";"),
|
|
365
476
|
comment: mainComment,
|
|
366
|
-
compare:
|
|
477
|
+
compare: hasTrivialComparison && aggregatedCompare ? { ...aggregatedCompare, trivial: true } : aggregatedCompare,
|
|
367
478
|
modifier: diceResult.modifier,
|
|
368
479
|
total
|
|
369
480
|
};
|
|
@@ -436,8 +547,8 @@ var NoStatisticsError = class extends Error {
|
|
|
436
547
|
|
|
437
548
|
// src/interfaces/constant.ts
|
|
438
549
|
var COMMENT_REGEX = /\s+(#|\/{2}|\[|\/\*)(?<comment>.*)/gi;
|
|
439
|
-
var SIGN_REGEX =
|
|
440
|
-
var SIGN_REGEX_SPACE = /(
|
|
550
|
+
var SIGN_REGEX = /==|!=|(?<![!<>])>=|(?<![!<>])<=|(?<!!)(?<![<>])>|(?<!!)(?<![<>])<|(?<!!)(?<![<>])=/;
|
|
551
|
+
var SIGN_REGEX_SPACE = /(==|!=|(?<![!<>])>=|(?<![!<>])<=|(?<!!)(?<![<>])>|(?<!!)(?<![<>])<|(?<!!)(?<![<>])=)(\S+)/;
|
|
441
552
|
var SYMBOL_DICE = "&";
|
|
442
553
|
var DETECT_CRITICAL = /\{\*?c[fs]:([<>=]|!=)+(.+?)}/gim;
|
|
443
554
|
var OPTIONAL_COMMENT = /\s+(#|\/{2}|\[|\/\*)?(?<comment>.*)/gi;
|
|
@@ -659,7 +770,7 @@ var import_mathjs3 = require("mathjs");
|
|
|
659
770
|
var import_random_js2 = require("random-js");
|
|
660
771
|
var import_uniformize2 = require("uniformize");
|
|
661
772
|
var import_rpg_dice_roller3 = require("@dice-roller/rpg-dice-roller");
|
|
662
|
-
function evalStatsDice(testDice, allStats, engine = import_rpg_dice_roller3.NumberGenerator.engines.nodeCrypto) {
|
|
773
|
+
function evalStatsDice(testDice, allStats, engine = import_rpg_dice_roller3.NumberGenerator.engines.nodeCrypto, pity) {
|
|
663
774
|
let dice = testDice.trimEnd();
|
|
664
775
|
if (allStats && Object.keys(allStats).length > 0) {
|
|
665
776
|
const names = Object.keys(allStats);
|
|
@@ -672,7 +783,7 @@ function evalStatsDice(testDice, allStats, engine = import_rpg_dice_roller3.Numb
|
|
|
672
783
|
}
|
|
673
784
|
}
|
|
674
785
|
try {
|
|
675
|
-
if (!roll(replaceFormulaInDice(replaceExpByRandom(dice)), engine))
|
|
786
|
+
if (!roll(replaceFormulaInDice(replaceExpByRandom(dice)), engine, pity))
|
|
676
787
|
throw new DiceTypeError(dice, "evalStatsDice", "no roll result");
|
|
677
788
|
return testDice;
|
|
678
789
|
} catch (error) {
|