@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.
Files changed (51) hide show
  1. package/README.md +57 -0
  2. package/dist/entities/DieType.d.ts +35 -0
  3. package/dist/entities/DieType.d.ts.map +1 -0
  4. package/dist/entities/DieType.js +43 -0
  5. package/dist/entities/Outcome.d.ts +26 -0
  6. package/dist/entities/Outcome.d.ts.map +1 -0
  7. package/dist/entities/Outcome.js +37 -0
  8. package/dist/entities/RollModifier.d.ts +115 -0
  9. package/dist/entities/RollModifier.d.ts.map +1 -0
  10. package/dist/entities/RollModifier.js +147 -0
  11. package/dist/entities/RollType.d.ts +24 -0
  12. package/dist/entities/RollType.d.ts.map +1 -0
  13. package/dist/entities/RollType.js +35 -0
  14. package/dist/entities/TestConditions.d.ts +97 -0
  15. package/dist/entities/TestConditions.d.ts.map +1 -0
  16. package/dist/entities/TestConditions.js +324 -0
  17. package/dist/entities/TestType.d.ts +28 -0
  18. package/dist/entities/TestType.d.ts.map +1 -0
  19. package/dist/entities/TestType.js +39 -0
  20. package/dist/entities/index.d.ts +16 -0
  21. package/dist/entities/index.d.ts.map +1 -0
  22. package/dist/entities/index.js +46 -0
  23. package/dist/index.d.ts +5 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +42 -0
  26. package/dist/roll.d.ts +66 -0
  27. package/dist/roll.d.ts.map +1 -0
  28. package/dist/roll.js +135 -0
  29. package/dist/rollDice.d.ts +48 -0
  30. package/dist/rollDice.d.ts.map +1 -0
  31. package/dist/rollDice.js +92 -0
  32. package/dist/rollDiceMod.d.ts +54 -0
  33. package/dist/rollDiceMod.d.ts.map +1 -0
  34. package/dist/rollDiceMod.js +137 -0
  35. package/dist/rollMod.d.ts +52 -0
  36. package/dist/rollMod.d.ts.map +1 -0
  37. package/dist/rollMod.js +114 -0
  38. package/dist/rollTest.d.ts +40 -0
  39. package/dist/rollTest.d.ts.map +1 -0
  40. package/dist/rollTest.js +100 -0
  41. package/dist/utils/determineOutcome.d.ts +45 -0
  42. package/dist/utils/determineOutcome.d.ts.map +1 -0
  43. package/dist/utils/determineOutcome.js +115 -0
  44. package/dist/utils/generateResult.d.ts +30 -0
  45. package/dist/utils/generateResult.d.ts.map +1 -0
  46. package/dist/utils/generateResult.js +51 -0
  47. package/dist/utils/index.d.ts +5 -0
  48. package/dist/utils/index.d.ts.map +1 -0
  49. package/dist/utils/index.js +26 -0
  50. package/dist-types.d.ts +48 -0
  51. 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"}
@@ -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"}
@@ -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