@platonic-dice/core 2.0.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 +57 -0
- package/dist/entities/DieType.d.ts +35 -0
- package/dist/entities/DieType.d.ts.map +1 -0
- package/dist/entities/DieType.js +43 -0
- package/dist/entities/Outcome.d.ts +26 -0
- package/dist/entities/Outcome.d.ts.map +1 -0
- package/dist/entities/Outcome.js +37 -0
- package/dist/entities/RollModifier.d.ts +115 -0
- package/dist/entities/RollModifier.d.ts.map +1 -0
- package/dist/entities/RollModifier.js +147 -0
- package/dist/entities/RollType.d.ts +24 -0
- package/dist/entities/RollType.d.ts.map +1 -0
- package/dist/entities/RollType.js +35 -0
- package/dist/entities/TestConditions.d.ts +97 -0
- package/dist/entities/TestConditions.d.ts.map +1 -0
- package/dist/entities/TestConditions.js +324 -0
- package/dist/entities/TestType.d.ts +28 -0
- package/dist/entities/TestType.d.ts.map +1 -0
- package/dist/entities/TestType.js +39 -0
- package/dist/entities/index.d.ts +16 -0
- package/dist/entities/index.d.ts.map +1 -0
- package/dist/entities/index.js +46 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +42 -0
- package/dist/roll.d.ts +66 -0
- package/dist/roll.d.ts.map +1 -0
- package/dist/roll.js +135 -0
- package/dist/rollDice.d.ts +48 -0
- package/dist/rollDice.d.ts.map +1 -0
- package/dist/rollDice.js +92 -0
- package/dist/rollDiceMod.d.ts +54 -0
- package/dist/rollDiceMod.d.ts.map +1 -0
- package/dist/rollDiceMod.js +137 -0
- package/dist/rollMod.d.ts +52 -0
- package/dist/rollMod.d.ts.map +1 -0
- package/dist/rollMod.js +114 -0
- package/dist/rollTest.d.ts +40 -0
- package/dist/rollTest.d.ts.map +1 -0
- package/dist/rollTest.js +100 -0
- package/dist/utils/determineOutcome.d.ts +45 -0
- package/dist/utils/determineOutcome.d.ts.map +1 -0
- package/dist/utils/determineOutcome.js +115 -0
- package/dist/utils/generateResult.d.ts +30 -0
- package/dist/utils/generateResult.d.ts.map +1 -0
- package/dist/utils/generateResult.js +51 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +26 -0
- package/dist-types.d.ts +48 -0
- package/package.json +46 -0
package/dist/roll.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @platonic-dice/core/src/roll
|
|
3
|
+
* @description
|
|
4
|
+
* Core logic for rolling a single die, with support for advantage/disadvantage
|
|
5
|
+
* and convenience aliases for common dice.
|
|
6
|
+
*
|
|
7
|
+
* Provides the fundamental random roll mechanism used throughout the library.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* import { roll, rollAdv, rollD20 } from "@platonic-dice/core";
|
|
11
|
+
*
|
|
12
|
+
* // Roll a d20 normally
|
|
13
|
+
* const result = roll(DieType.D20);
|
|
14
|
+
*
|
|
15
|
+
* // Roll a d20 with advantage
|
|
16
|
+
* const advantage = roll(DieType.D20, RollType.Advantage);
|
|
17
|
+
*
|
|
18
|
+
* // Roll a d6 using shorthand
|
|
19
|
+
* const six = rollD6();
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const {
|
|
23
|
+
DieType,
|
|
24
|
+
isValidDieType,
|
|
25
|
+
RollType,
|
|
26
|
+
isValidRollType,
|
|
27
|
+
} = require("./entities");
|
|
28
|
+
const utils = require("./utils");
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @typedef {import("./entities/DieType").DieTypeValue} DieTypeValue
|
|
32
|
+
* @typedef {import("./entities/RollType").RollTypeValue} RollTypeValue
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Rolls a single die of the specified type, optionally applying advantage or disadvantage.
|
|
37
|
+
*
|
|
38
|
+
* @function roll
|
|
39
|
+
* @param {DieTypeValue} dieType - The type of die to roll (e.g., `DieType.D20`).
|
|
40
|
+
* @param {RollTypeValue | undefined} [rollType=undefined] - Optional roll mode (`RollType.Advantage` or `RollType.Disadvantage`).
|
|
41
|
+
* @returns {number} The rolled value (integer between 1 and the die's maximum face).
|
|
42
|
+
* @throws {TypeError} If `dieType` or `rollType` are invalid.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* const result = roll(DieType.D20, RollType.Advantage);
|
|
46
|
+
*/
|
|
47
|
+
function roll(dieType, rollType = undefined) {
|
|
48
|
+
// --- Validation ---
|
|
49
|
+
if (!isValidDieType(dieType)) {
|
|
50
|
+
throw new TypeError(`Invalid die type: ${dieType}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (rollType !== undefined && !isValidRollType(rollType)) {
|
|
54
|
+
throw new TypeError(`Invalid roll type: ${rollType}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// --- Core Logic ---
|
|
58
|
+
const roll1 = utils.generateResult(dieType);
|
|
59
|
+
if (rollType === undefined) return roll1;
|
|
60
|
+
|
|
61
|
+
const roll2 = utils.generateResult(dieType);
|
|
62
|
+
return rollType === RollType.Advantage
|
|
63
|
+
? Math.max(roll1, roll2)
|
|
64
|
+
: Math.min(roll1, roll2);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
//
|
|
68
|
+
// --- Convenience Aliases ---
|
|
69
|
+
//
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Rolls a die with advantage.
|
|
73
|
+
* @type {(dieType: DieTypeValue) => number}
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* const result = rollAdv(DieType.D10);
|
|
77
|
+
*/
|
|
78
|
+
const rollAdv = (dieType) => roll(dieType, RollType.Advantage);
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Rolls a die with disadvantage.
|
|
82
|
+
* @type {(dieType: DieTypeValue) => number}
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* const result = rollDis(DieType.D10);
|
|
86
|
+
*/
|
|
87
|
+
const rollDis = (dieType) => roll(dieType, RollType.Disadvantage);
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Rolls a D4 die.
|
|
91
|
+
* @type {(rollType?: RollTypeValue | undefined) => number}
|
|
92
|
+
*/
|
|
93
|
+
const rollD4 = (rollType = undefined) => roll(DieType.D4, rollType);
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Rolls a D6 die.
|
|
97
|
+
* @type {(rollType?: RollTypeValue | undefined) => number}
|
|
98
|
+
*/
|
|
99
|
+
const rollD6 = (rollType = undefined) => roll(DieType.D6, rollType);
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Rolls a D8 die.
|
|
103
|
+
* @type {(rollType?: RollTypeValue | undefined) => number}
|
|
104
|
+
*/
|
|
105
|
+
const rollD8 = (rollType = undefined) => roll(DieType.D8, rollType);
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Rolls a D10 die.
|
|
109
|
+
* @type {(rollType?: RollTypeValue | undefined) => number}
|
|
110
|
+
*/
|
|
111
|
+
const rollD10 = (rollType = undefined) => roll(DieType.D10, rollType);
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Rolls a D12 die.
|
|
115
|
+
* @type {(rollType?: RollTypeValue | undefined) => number}
|
|
116
|
+
*/
|
|
117
|
+
const rollD12 = (rollType = undefined) => roll(DieType.D12, rollType);
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Rolls a D20 die.
|
|
121
|
+
* @type {(rollType?: RollTypeValue | undefined) => number}
|
|
122
|
+
*/
|
|
123
|
+
const rollD20 = (rollType = undefined) => roll(DieType.D20, rollType);
|
|
124
|
+
|
|
125
|
+
module.exports = {
|
|
126
|
+
roll,
|
|
127
|
+
rollAdv,
|
|
128
|
+
rollDis,
|
|
129
|
+
rollD4,
|
|
130
|
+
rollD6,
|
|
131
|
+
rollD8,
|
|
132
|
+
rollD10,
|
|
133
|
+
rollD12,
|
|
134
|
+
rollD20,
|
|
135
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
declare namespace _exports {
|
|
2
|
+
export { DieTypeValue, RollDiceAlias };
|
|
3
|
+
}
|
|
4
|
+
declare namespace _exports {
|
|
5
|
+
export { rollDice };
|
|
6
|
+
}
|
|
7
|
+
export = _exports;
|
|
8
|
+
type DieTypeValue = import("./entities/DieType").DieTypeValue;
|
|
9
|
+
type RollDiceAlias = (dieType: import("./entities/DieType").DieTypeValue) => {
|
|
10
|
+
array: number[];
|
|
11
|
+
sum: number;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {import("./entities/DieType").DieTypeValue} DieTypeValue
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Rolls one or more dice of the specified type.
|
|
18
|
+
*
|
|
19
|
+
* @function rollDice
|
|
20
|
+
* @param {DieTypeValue} dieType - The type of die to roll (e.g., `DieType.D6`, `DieType.D20`).
|
|
21
|
+
* @param {Object} [options] - Optional configuration.
|
|
22
|
+
* @param {number} [options.count=1] - Number of dice to roll. Must be a positive integer.
|
|
23
|
+
* @returns {{ array: number[], sum: number }} An object containing:
|
|
24
|
+
* - `array`: an array of individual die rolls.
|
|
25
|
+
* - `sum`: the total sum of all rolls.
|
|
26
|
+
* @throws {TypeError} If `dieType` is invalid.
|
|
27
|
+
* @throws {TypeError} If `count` is not a positive integer.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* const result = rollDice(DieType.D6, { count: 5 });
|
|
31
|
+
* console.log(result.sum); // e.g., 18
|
|
32
|
+
* console.log(result.array); // e.g., [2, 5, 3, 1, 7]
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* // Roll a single d20
|
|
36
|
+
* const result = rollDice(DieType.D20);
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* // Roll 3d6
|
|
40
|
+
* const result = rollDice(DieType.D6, { count: 3 });
|
|
41
|
+
*/
|
|
42
|
+
declare function rollDice(dieType: DieTypeValue, { count }?: {
|
|
43
|
+
count?: number | undefined;
|
|
44
|
+
}): {
|
|
45
|
+
array: number[];
|
|
46
|
+
sum: number;
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=rollDice.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rollDice.d.ts","sourceRoot":"","sources":["../src/rollDice.js"],"names":[],"mappings":";;;;;;;oBAsBa,OAAO,oBAAoB,EAAE,YAAY;qBAmDzC,CAAC,OAAO,EAAE,OAAO,oBAAoB,EAAE,YAAY,KAAK;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE;AApDrG;;GAEG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,mCAtBW,YAAY,cAEpB;IAAyB,KAAK;CAC9B,GAAU;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAoC5C"}
|
package/dist/rollDice.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @platonic-dice/core/src/rollDice
|
|
3
|
+
* @description
|
|
4
|
+
* Rolls one or more dice of a given type, returning both the individual rolls
|
|
5
|
+
* and their total sum. Includes convenient aliases for common counts (e.g., `roll3x` for 3 dice).
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* import { rollDice, roll3x } from "@platonic-dice/core";
|
|
9
|
+
*
|
|
10
|
+
* // Roll a single d20
|
|
11
|
+
* const result = rollDice(DieType.D20);
|
|
12
|
+
* console.log(result.array, result.sum);
|
|
13
|
+
*
|
|
14
|
+
* // Roll 3d6 using an alias
|
|
15
|
+
* const rolls = roll3x(DieType.D6);
|
|
16
|
+
* console.log(rolls); // [2, 5, 4]
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const { isValidDieType } = require("./entities");
|
|
20
|
+
const r = require("./roll.js");
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @typedef {import("./entities/DieType").DieTypeValue} DieTypeValue
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Rolls one or more dice of the specified type.
|
|
28
|
+
*
|
|
29
|
+
* @function rollDice
|
|
30
|
+
* @param {DieTypeValue} dieType - The type of die to roll (e.g., `DieType.D6`, `DieType.D20`).
|
|
31
|
+
* @param {Object} [options] - Optional configuration.
|
|
32
|
+
* @param {number} [options.count=1] - Number of dice to roll. Must be a positive integer.
|
|
33
|
+
* @returns {{ array: number[], sum: number }} An object containing:
|
|
34
|
+
* - `array`: an array of individual die rolls.
|
|
35
|
+
* - `sum`: the total sum of all rolls.
|
|
36
|
+
* @throws {TypeError} If `dieType` is invalid.
|
|
37
|
+
* @throws {TypeError} If `count` is not a positive integer.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* const result = rollDice(DieType.D6, { count: 5 });
|
|
41
|
+
* console.log(result.sum); // e.g., 18
|
|
42
|
+
* console.log(result.array); // e.g., [2, 5, 3, 1, 7]
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* // Roll a single d20
|
|
46
|
+
* const result = rollDice(DieType.D20);
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* // Roll 3d6
|
|
50
|
+
* const result = rollDice(DieType.D6, { count: 3 });
|
|
51
|
+
*/
|
|
52
|
+
function rollDice(dieType, { count = 1 } = {}) {
|
|
53
|
+
// --- Validation ---
|
|
54
|
+
if (!isValidDieType(dieType)) {
|
|
55
|
+
throw new TypeError(`Invalid die type: ${dieType}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (typeof count !== "number" || !Number.isInteger(count) || count < 1) {
|
|
59
|
+
throw new TypeError(
|
|
60
|
+
`Invalid count: ${count}. Count must be a positive integer.`
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// --- Core logic ---
|
|
65
|
+
const array = Array.from({ length: count }, () => r.roll(dieType));
|
|
66
|
+
const sum = array.reduce((total, n) => total + n, 0);
|
|
67
|
+
|
|
68
|
+
return { array, sum };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** --- Friendly alias generation for convenience --- */
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @typedef {(dieType: import("./entities/DieType").DieTypeValue) => { array: number[], sum: number }} RollDiceAlias
|
|
75
|
+
*/
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* A collection of preconfigured dice roll functions like `roll2x`, `roll3x`, etc.
|
|
79
|
+
* @type {Record<string, RollDiceAlias>}
|
|
80
|
+
*/
|
|
81
|
+
const rollDiceAliases = {};
|
|
82
|
+
|
|
83
|
+
const counts = [2, 3, 4, 5, 6, 7, 8, 9, 10, 25, 50, 100];
|
|
84
|
+
|
|
85
|
+
for (const count of counts) {
|
|
86
|
+
rollDiceAliases[`roll${count}x`] = (dieType) => rollDice(dieType, { count });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
module.exports = {
|
|
90
|
+
rollDice,
|
|
91
|
+
...rollDiceAliases,
|
|
92
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export type DieTypeValue = import("./entities/DieType").DieTypeValue;
|
|
2
|
+
export type RollModifierFunction = import("./entities/RollModifier").RollModifierFunction;
|
|
3
|
+
export type RollModifierInstance = import("./entities/RollModifier").RollModifierInstance;
|
|
4
|
+
export type DiceModifier = import("./entities/RollModifier").DiceModifier;
|
|
5
|
+
export type rollDiceModModifier = RollModifierInstance | RollModifierFunction | DiceModifier;
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {import("./entities/DieType").DieTypeValue} DieTypeValue
|
|
8
|
+
* @typedef {import("./entities/RollModifier").RollModifierFunction} RollModifierFunction
|
|
9
|
+
* @typedef {import("./entities/RollModifier").RollModifierInstance} RollModifierInstance
|
|
10
|
+
* @typedef {import("./entities/RollModifier").DiceModifier} DiceModifier
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* @typedef {RollModifierInstance | RollModifierFunction | DiceModifier} rollDiceModModifier
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Rolls multiple dice with optional per-die (`each`) and net (`net`) modifiers.
|
|
17
|
+
*
|
|
18
|
+
* @function rollDiceMod
|
|
19
|
+
* @param {DieTypeValue} dieType - The die type (e.g., `DieType.D6`).
|
|
20
|
+
* @param {rollDiceModModifier} [modifier={}] - The modifier(s) to apply.
|
|
21
|
+
* @param {{ count?: number }} [options={}] - Optional roll count (default: 1).
|
|
22
|
+
* @returns {{
|
|
23
|
+
* base: { array: number[], sum: number },
|
|
24
|
+
* modified: { each: { array: number[], sum: number }, net: { value: number } }
|
|
25
|
+
* }}
|
|
26
|
+
* @throws {TypeError} If `count` is invalid.
|
|
27
|
+
* @throws {TypeError} If any modifier is invalid.
|
|
28
|
+
*/
|
|
29
|
+
export function rollDiceMod(dieType: DieTypeValue, modifier?: rollDiceModModifier, { count }?: {
|
|
30
|
+
count?: number;
|
|
31
|
+
}): {
|
|
32
|
+
base: {
|
|
33
|
+
array: number[];
|
|
34
|
+
sum: number;
|
|
35
|
+
};
|
|
36
|
+
modified: {
|
|
37
|
+
each: {
|
|
38
|
+
array: number[];
|
|
39
|
+
sum: number;
|
|
40
|
+
};
|
|
41
|
+
net: {
|
|
42
|
+
value: number;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
/** @type {(dieType: DieTypeValue, modifier?: rollDiceModModifier, options?: { count?: number }) => number[]} */
|
|
47
|
+
export const rollDiceModArr: (dieType: DieTypeValue, modifier?: rollDiceModModifier, options?: {
|
|
48
|
+
count?: number;
|
|
49
|
+
}) => number[];
|
|
50
|
+
/** @type {(dieType: DieTypeValue, modifier?: rollDiceModModifier, options?: { count?: number }) => number} */
|
|
51
|
+
export const rollDiceModNet: (dieType: DieTypeValue, modifier?: rollDiceModModifier, options?: {
|
|
52
|
+
count?: number;
|
|
53
|
+
}) => number;
|
|
54
|
+
//# sourceMappingURL=rollDiceMod.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rollDiceMod.d.ts","sourceRoot":"","sources":["../src/rollDiceMod.js"],"names":[],"mappings":"2BAgCa,OAAO,oBAAoB,EAAE,YAAY;mCACzC,OAAO,yBAAyB,EAAE,oBAAoB;mCACtD,OAAO,yBAAyB,EAAE,oBAAoB;2BACtD,OAAO,yBAAyB,EAAE,YAAY;kCAI9C,oBAAoB,GAAG,oBAAoB,GAAG,YAAY;AARvE;;;;;GAKG;AAEH;;GAEG;AAEH;;;;;;;;;;;;;GAaG;AACH,qCAVW,YAAY,aACZ,mBAAmB,cACnB;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAChB;IACR,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACvC,QAAQ,EAAE;QAAE,IAAI,EAAE;YAAE,KAAK,EAAE,MAAM,EAAE,CAAC;YAAC,GAAG,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,GAAG,EAAE;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAA;CAC7E,CA4CH;AA8BD,gHAAgH;AAChH,6BADW,CAAC,OAAO,EAAE,YAAY,EAAE,QAAQ,CAAC,EAAE,mBAAmB,EAAE,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,KAAK,MAAM,EAAE,CAClE;AAE1C,8GAA8G;AAC9G,6BADW,CAAC,OAAO,EAAE,YAAY,EAAE,QAAQ,CAAC,EAAE,mBAAmB,EAAE,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,KAAK,MAAM,CACtE"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @platonic-dice/core/src/rollDiceMod
|
|
3
|
+
* @description
|
|
4
|
+
* Rolls multiple dice with optional per-die (`each`) and total (`net`) modifiers.
|
|
5
|
+
* Returns structured results containing both the raw dice and all modified values.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* import { rollDiceMod, RollModifier } from "@platonic-dice/core";
|
|
9
|
+
* import { DieType } from "@platonic-dice/core/src/entities";
|
|
10
|
+
*
|
|
11
|
+
* // Apply a flat +1 to each die, then a +2 net bonus
|
|
12
|
+
* const result = rollDiceMod(DieType.D6, {
|
|
13
|
+
* each: (n) => n + 1,
|
|
14
|
+
* net: (sum) => sum + 2
|
|
15
|
+
* }, { count: 3 });
|
|
16
|
+
*
|
|
17
|
+
* console.log(result.base); // { array: [2, 4, 1], sum: 7 }
|
|
18
|
+
* console.log(result.modified);
|
|
19
|
+
* // {
|
|
20
|
+
* // each: { array: [3, 5, 2], sum: 10 },
|
|
21
|
+
* // net: { value: 12 }
|
|
22
|
+
* // }
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* // Single RollModifier for net only
|
|
26
|
+
* const bonus = new RollModifier((sum) => sum * 2);
|
|
27
|
+
* const result2 = rollDiceMod(DieType.D6, bonus, { count: 3 });
|
|
28
|
+
*/
|
|
29
|
+
const { normaliseRollModifier, RollModifier } = require("./entities");
|
|
30
|
+
const rd = require("./rollDice.js");
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @typedef {import("./entities/DieType").DieTypeValue} DieTypeValue
|
|
34
|
+
* @typedef {import("./entities/RollModifier").RollModifierFunction} RollModifierFunction
|
|
35
|
+
* @typedef {import("./entities/RollModifier").RollModifierInstance} RollModifierInstance
|
|
36
|
+
* @typedef {import("./entities/RollModifier").DiceModifier} DiceModifier
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @typedef {RollModifierInstance | RollModifierFunction | DiceModifier} rollDiceModModifier
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Rolls multiple dice with optional per-die (`each`) and net (`net`) modifiers.
|
|
45
|
+
*
|
|
46
|
+
* @function rollDiceMod
|
|
47
|
+
* @param {DieTypeValue} dieType - The die type (e.g., `DieType.D6`).
|
|
48
|
+
* @param {rollDiceModModifier} [modifier={}] - The modifier(s) to apply.
|
|
49
|
+
* @param {{ count?: number }} [options={}] - Optional roll count (default: 1).
|
|
50
|
+
* @returns {{
|
|
51
|
+
* base: { array: number[], sum: number },
|
|
52
|
+
* modified: { each: { array: number[], sum: number }, net: { value: number } }
|
|
53
|
+
* }}
|
|
54
|
+
* @throws {TypeError} If `count` is invalid.
|
|
55
|
+
* @throws {TypeError} If any modifier is invalid.
|
|
56
|
+
*/
|
|
57
|
+
function rollDiceMod(dieType, modifier = {}, { count = 1 } = {}) {
|
|
58
|
+
// --- Validation ---
|
|
59
|
+
if (typeof count !== "number" || !Number.isInteger(count) || count < 1) {
|
|
60
|
+
throw new TypeError(`Invalid count: ${count}. Must be a positive integer.`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// --- Normalise modifier ---
|
|
64
|
+
let eachMod, netMod;
|
|
65
|
+
|
|
66
|
+
if (modifier instanceof RollModifier || typeof modifier === "function") {
|
|
67
|
+
// Single modifier → treated as net
|
|
68
|
+
eachMod = normaliseRollModifier(undefined); // identity
|
|
69
|
+
netMod = normaliseRollModifier(modifier);
|
|
70
|
+
} else if (typeof modifier === "object" && modifier !== null) {
|
|
71
|
+
const { each, net } = modifier;
|
|
72
|
+
eachMod = normaliseRollModifier(each);
|
|
73
|
+
netMod = normaliseRollModifier(net);
|
|
74
|
+
} else {
|
|
75
|
+
throw new TypeError(
|
|
76
|
+
`Invalid modifier: ${modifier}. Must be a function, RollModifier, or object.`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// --- Roll dice ---
|
|
81
|
+
const base = rd.rollDice(dieType, { count }); // { array, sum }
|
|
82
|
+
|
|
83
|
+
// --- Apply 'each' modifier ---
|
|
84
|
+
const eachArray = base.array.map((n) => eachMod.apply(n));
|
|
85
|
+
const eachSum = eachArray.reduce((a, b) => a + b, 0);
|
|
86
|
+
|
|
87
|
+
// --- Apply 'net' modifier ---
|
|
88
|
+
const netValue = netMod.apply(eachSum);
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
base,
|
|
92
|
+
modified: {
|
|
93
|
+
each: { array: eachArray, sum: eachSum },
|
|
94
|
+
net: { value: netValue },
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
//
|
|
100
|
+
// --- Convenience Aliases ---
|
|
101
|
+
//
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @private
|
|
105
|
+
* Generates a simple accessor alias for `rollDiceMod`.
|
|
106
|
+
*
|
|
107
|
+
* @template {keyof { eachArray: number[]; net: number }} K
|
|
108
|
+
* @param {K} key
|
|
109
|
+
*/
|
|
110
|
+
function alias(key) {
|
|
111
|
+
/** @type {(...args: Parameters<typeof rollDiceMod>) => K extends 'eachArray' ? number[] : number} */
|
|
112
|
+
return (dieType, modifier = {}, options = {}) => {
|
|
113
|
+
const result = rollDiceMod(dieType, modifier, options);
|
|
114
|
+
switch (key) {
|
|
115
|
+
case "eachArray":
|
|
116
|
+
return /** @type {any} */ (result.modified.each.array);
|
|
117
|
+
case "net":
|
|
118
|
+
return /** @type {any} */ (result.modified.net.value);
|
|
119
|
+
default:
|
|
120
|
+
throw new TypeError(`Unknown alias key: ${key}`);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// --- Exports ---
|
|
126
|
+
|
|
127
|
+
/** @type {(dieType: DieTypeValue, modifier?: rollDiceModModifier, options?: { count?: number }) => number[]} */
|
|
128
|
+
const rollDiceModArr = alias("eachArray");
|
|
129
|
+
|
|
130
|
+
/** @type {(dieType: DieTypeValue, modifier?: rollDiceModModifier, options?: { count?: number }) => number} */
|
|
131
|
+
const rollDiceModNet = alias("net");
|
|
132
|
+
|
|
133
|
+
module.exports = {
|
|
134
|
+
rollDiceMod,
|
|
135
|
+
rollDiceModArr,
|
|
136
|
+
rollDiceModNet,
|
|
137
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
declare namespace _exports {
|
|
2
|
+
export { DieTypeValue, RollTypeValue, RollModifierFunction, RollModifierInstance, DieModifierAlias };
|
|
3
|
+
}
|
|
4
|
+
declare namespace _exports {
|
|
5
|
+
export { rollMod };
|
|
6
|
+
}
|
|
7
|
+
export = _exports;
|
|
8
|
+
type DieTypeValue = import("./entities/DieType").DieTypeValue;
|
|
9
|
+
type RollTypeValue = import("./entities/RollType").RollTypeValue;
|
|
10
|
+
type RollModifierFunction = import("./entities/RollModifier").RollModifierFunction;
|
|
11
|
+
type RollModifierInstance = import("./entities/RollModifier").RollModifierInstance;
|
|
12
|
+
type DieModifierAlias = (rollType?: RollTypeValue | undefined) => number;
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {import("./entities/DieType").DieTypeValue} DieTypeValue
|
|
15
|
+
* @typedef {import("./entities/RollType").RollTypeValue} RollTypeValue
|
|
16
|
+
* @typedef {import("./entities/RollModifier").RollModifierFunction} RollModifierFunction
|
|
17
|
+
* @typedef {import("./entities/RollModifier").RollModifierInstance} RollModifierInstance
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Rolls a single modified die by applying a modifier function or RollModifier.
|
|
21
|
+
*
|
|
22
|
+
* This function first rolls a base value using {@link roll}, then applies
|
|
23
|
+
* the provided modifier — either a function `(n) => number` or a
|
|
24
|
+
* {@link RollModifier} instance — to produce the final result.
|
|
25
|
+
*
|
|
26
|
+
* @function rollMod
|
|
27
|
+
* @param {DieTypeValue} dieType - The type of die to roll (e.g., `DieType.D20`).
|
|
28
|
+
* @param {RollModifierFunction|RollModifierInstance} modifier - The modifier to apply.
|
|
29
|
+
* Can be either:
|
|
30
|
+
* - A RollModifierFunction `(n: number) => number`
|
|
31
|
+
* - A {@link RollModifier} instance
|
|
32
|
+
* @param {RollTypeValue | undefined} [rollType=undefined] - Optional roll mode (`RollType.Advantage` or `RollType.Disadvantage`).
|
|
33
|
+
* @returns {{ base: number, modified: number }} - The unmodified roll (`base`) and the modified result (`modified`).
|
|
34
|
+
* @throws {TypeError} If the modifier is invalid (not a function or RollModifier).
|
|
35
|
+
* @throws {TypeError} If the `dieType` or `rollType` are invalid (delegated to {@link roll}).
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* const result = rollMod(DieType.D20, (n) => n + 2);
|
|
39
|
+
* console.log(result); // { base: 14, modified: 16 }
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* const bonus = new RollModifier((n) => Math.min(n + 3, 20));
|
|
43
|
+
* const result = rollMod(DieType.D20, bonus);
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* const result = rollMod(DieType.D10, (n) => Math.floor(n / 2), RollType.Advantage);
|
|
47
|
+
*/
|
|
48
|
+
declare function rollMod(dieType: DieTypeValue, modifier: RollModifierFunction | RollModifierInstance, rollType?: RollTypeValue | undefined): {
|
|
49
|
+
base: number;
|
|
50
|
+
modified: number;
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=rollMod.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rollMod.d.ts","sourceRoot":"","sources":["../src/rollMod.js"],"names":[],"mappings":";;;;;;;oBAuBa,OAAO,oBAAoB,EAAE,YAAY;qBACzC,OAAO,qBAAqB,EAAE,aAAa;4BAC3C,OAAO,yBAAyB,EAAE,oBAAoB;4BACtD,OAAO,yBAAyB,EAAE,oBAAoB;wBA8CtD,CAAC,QAAQ,CAAC,EAAE,aAAa,GAAG,SAAS,KAAK,MAAM;AAlD7D;;;;;GAKG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,kCArBW,YAAY,YACZ,oBAAoB,GAAC,oBAAoB,aAIzC,aAAa,GAAG,SAAS,GACvB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAsB9C"}
|
package/dist/rollMod.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @platonic-dice/core/src/rollMod
|
|
3
|
+
* @description
|
|
4
|
+
* Core logic for rolling a single die and applying a numeric or functional modifier.
|
|
5
|
+
*
|
|
6
|
+
* Provides the foundation for modified rolls — including bonuses, penalties,
|
|
7
|
+
* and scaling effects — as well as convenience aliases like `rollModP5`
|
|
8
|
+
* or `rollModT2` for fast use in common tabletop scenarios.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* import { rollMod, rollModP2, rollModT2 } from "@platonic-dice/core";
|
|
12
|
+
*
|
|
13
|
+
* // Roll a D20 with a +2 bonus
|
|
14
|
+
* const result = rollMod(DieType.D20, (n) => n + 2);
|
|
15
|
+
*
|
|
16
|
+
* // Or just the modified value
|
|
17
|
+
* const total = rollModP2(DieType.D20);
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const { DieType, RollModifier, normaliseRollModifier } = require("./entities");
|
|
21
|
+
const r = require("./roll.js");
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @typedef {import("./entities/DieType").DieTypeValue} DieTypeValue
|
|
25
|
+
* @typedef {import("./entities/RollType").RollTypeValue} RollTypeValue
|
|
26
|
+
* @typedef {import("./entities/RollModifier").RollModifierFunction} RollModifierFunction
|
|
27
|
+
* @typedef {import("./entities/RollModifier").RollModifierInstance} RollModifierInstance
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Rolls a single modified die by applying a modifier function or RollModifier.
|
|
32
|
+
*
|
|
33
|
+
* This function first rolls a base value using {@link roll}, then applies
|
|
34
|
+
* the provided modifier — either a function `(n) => number` or a
|
|
35
|
+
* {@link RollModifier} instance — to produce the final result.
|
|
36
|
+
*
|
|
37
|
+
* @function rollMod
|
|
38
|
+
* @param {DieTypeValue} dieType - The type of die to roll (e.g., `DieType.D20`).
|
|
39
|
+
* @param {RollModifierFunction|RollModifierInstance} modifier - The modifier to apply.
|
|
40
|
+
* Can be either:
|
|
41
|
+
* - A RollModifierFunction `(n: number) => number`
|
|
42
|
+
* - A {@link RollModifier} instance
|
|
43
|
+
* @param {RollTypeValue | undefined} [rollType=undefined] - Optional roll mode (`RollType.Advantage` or `RollType.Disadvantage`).
|
|
44
|
+
* @returns {{ base: number, modified: number }} - The unmodified roll (`base`) and the modified result (`modified`).
|
|
45
|
+
* @throws {TypeError} If the modifier is invalid (not a function or RollModifier).
|
|
46
|
+
* @throws {TypeError} If the `dieType` or `rollType` are invalid (delegated to {@link roll}).
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* const result = rollMod(DieType.D20, (n) => n + 2);
|
|
50
|
+
* console.log(result); // { base: 14, modified: 16 }
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* const bonus = new RollModifier((n) => Math.min(n + 3, 20));
|
|
54
|
+
* const result = rollMod(DieType.D20, bonus);
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* const result = rollMod(DieType.D10, (n) => Math.floor(n / 2), RollType.Advantage);
|
|
58
|
+
*/
|
|
59
|
+
function rollMod(dieType, modifier, rollType = undefined) {
|
|
60
|
+
const mod = normaliseRollModifier(modifier);
|
|
61
|
+
|
|
62
|
+
const base = r.roll(dieType, rollType);
|
|
63
|
+
const modified = mod.apply(base);
|
|
64
|
+
|
|
65
|
+
return { base, modified };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
//
|
|
69
|
+
// --- Convenience Aliases ---
|
|
70
|
+
//
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @typedef {(rollType?: RollTypeValue | undefined) => number} DieModifierAlias
|
|
74
|
+
*/
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @description
|
|
78
|
+
* Container for all die-type-specific flat bonus and multiplicative aliases.
|
|
79
|
+
* Generated dynamically from DieType values.
|
|
80
|
+
*
|
|
81
|
+
* @type {Record<string, DieModifierAlias>}
|
|
82
|
+
*/
|
|
83
|
+
const dieTypeAliases = {};
|
|
84
|
+
|
|
85
|
+
// --- Flat Bonus Aliases ---
|
|
86
|
+
// Flat modifiers from -10 to +10 for each die type
|
|
87
|
+
for (const [dieKey, dieValue] of Object.entries(DieType)) {
|
|
88
|
+
for (let i = 1; i <= 10; i++) {
|
|
89
|
+
/** @type {DieModifierAlias} */
|
|
90
|
+
dieTypeAliases[`roll${dieKey}P${i}`] = (rollType = undefined) =>
|
|
91
|
+
rollMod(dieValue, (n) => n + i, rollType).modified;
|
|
92
|
+
|
|
93
|
+
/** @type {DieModifierAlias} */
|
|
94
|
+
dieTypeAliases[`roll${dieKey}M${i}`] = (rollType = undefined) =>
|
|
95
|
+
rollMod(dieValue, (n) => n - i, rollType).modified;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// --- Multiplicative Aliases ---
|
|
100
|
+
// Multiplicative modifiers such as ×2, ×10, ×100
|
|
101
|
+
const multipliers = [2, 3, 5, 10, 50, 100];
|
|
102
|
+
for (const [dieKey, dieValue] of Object.entries(DieType)) {
|
|
103
|
+
for (const m of multipliers) {
|
|
104
|
+
/** @type {DieModifierAlias} */
|
|
105
|
+
dieTypeAliases[`roll${dieKey}T${m}`] = (rollType = undefined) =>
|
|
106
|
+
rollMod(dieValue, (n) => n * m, rollType).modified;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// --- Export all aliases ---
|
|
111
|
+
module.exports = {
|
|
112
|
+
rollMod,
|
|
113
|
+
...dieTypeAliases,
|
|
114
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
declare namespace _exports {
|
|
2
|
+
export { DieTypeValue, OutcomeValue, RollTypeValue, TestTypeValue, TestConditionsInstance };
|
|
3
|
+
}
|
|
4
|
+
declare namespace _exports {
|
|
5
|
+
export { rollTest };
|
|
6
|
+
}
|
|
7
|
+
export = _exports;
|
|
8
|
+
type DieTypeValue = import("./entities/DieType").DieTypeValue;
|
|
9
|
+
type OutcomeValue = import("./entities/Outcome").OutcomeValue;
|
|
10
|
+
type RollTypeValue = import("./entities/RollType").RollTypeValue;
|
|
11
|
+
type TestTypeValue = import("./entities/TestType").TestTypeValue;
|
|
12
|
+
type TestConditionsInstance = import("./entities/TestConditions").TestConditionsInstance;
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {import("./entities/DieType").DieTypeValue} DieTypeValue
|
|
15
|
+
* @typedef {import("./entities/Outcome").OutcomeValue} OutcomeValue
|
|
16
|
+
* @typedef {import("./entities/RollType").RollTypeValue} RollTypeValue
|
|
17
|
+
* @typedef {import("./entities/TestType").TestTypeValue} TestTypeValue
|
|
18
|
+
* @typedef {import("./entities/TestConditions").TestConditionsInstance} TestConditionsInstance
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Rolls a die and evaluates it against specified test conditions.
|
|
22
|
+
*
|
|
23
|
+
* @function rollTest
|
|
24
|
+
* @param {DieTypeValue} dieType - The type of die to roll (e.g., `DieType.D6`, `DieType.D20`).
|
|
25
|
+
* @param {TestConditionsInstance|{ testType: TestTypeValue, [key: string]: any }} testConditions
|
|
26
|
+
* Can be:
|
|
27
|
+
* - A `TestConditions` instance.
|
|
28
|
+
* - A plain object `{ testType, ...conditions }`.
|
|
29
|
+
* @param {RollTypeValue} [rollType=undefined] - Optional roll mode (`RollType.Advantage` or `RollType.Disadvantage`).
|
|
30
|
+
* @returns {{ base: number, outcome: OutcomeValue }} The raw roll and its evaluated outcome.
|
|
31
|
+
* @throws {TypeError} If `dieType` or `testConditions` are invalid.
|
|
32
|
+
*/
|
|
33
|
+
declare function rollTest(dieType: DieTypeValue, testConditions: TestConditionsInstance | {
|
|
34
|
+
testType: TestTypeValue;
|
|
35
|
+
[key: string]: any;
|
|
36
|
+
}, rollType?: RollTypeValue): {
|
|
37
|
+
base: number;
|
|
38
|
+
outcome: OutcomeValue;
|
|
39
|
+
};
|
|
40
|
+
//# sourceMappingURL=rollTest.d.ts.map
|