@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/README.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# @platonic-dice/core
|
|
2
|
+
|
|
3
|
+
Core JavaScript/TypeScript library providing dice-roll logic, modifiers, and test evaluation for tabletop RPGs.
|
|
4
|
+
|
|
5
|
+
This package contains the pure logic used by higher-level packages (for example `@platonic-dice/dice`). It exports rolling helpers, entities (die types, roll types, outcomes), and utility functions.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Install from npm:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @platonic-dice/core
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick usage
|
|
16
|
+
|
|
17
|
+
CommonJS:
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
const { roll, rollDice, DieType, RollType } = require('@platonic-dice/core');
|
|
21
|
+
|
|
22
|
+
console.log( roll(DieType.D20) );
|
|
23
|
+
console.log( rollDice(DieType.D6, { count: 3 }) );
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
ESM / TypeScript:
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { roll, DieType } from '@platonic-dice/core';
|
|
30
|
+
console.log( roll(DieType.D20) );
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Build & Test
|
|
34
|
+
|
|
35
|
+
This package's sources live under `src/`. To run tests or build from the monorepo root:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# from repo root
|
|
39
|
+
npm run build
|
|
40
|
+
npm test
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Or run package-local scripts:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
cd packages/core
|
|
47
|
+
# run unit tests
|
|
48
|
+
npm test
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Contributing
|
|
52
|
+
|
|
53
|
+
See the repository root `README.md` for contribution guidelines. Keep changes backwards-compatible where possible and include tests.
|
|
54
|
+
|
|
55
|
+
## License
|
|
56
|
+
|
|
57
|
+
MIT — see the repository `LICENSE` file.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export type DieTypeKey = keyof typeof DieType;
|
|
2
|
+
export type DieTypeValue = (typeof DieType)[keyof typeof DieType];
|
|
3
|
+
/**
|
|
4
|
+
* *
|
|
5
|
+
*/
|
|
6
|
+
export type DieType = string;
|
|
7
|
+
/**
|
|
8
|
+
* @module @platonic-dice/core/src/entities/DieType
|
|
9
|
+
* @description
|
|
10
|
+
* Enum for die types used in rolling functions.
|
|
11
|
+
*
|
|
12
|
+
* @readonly
|
|
13
|
+
* @enum {string}
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* import { DieType } from "@platonic-dice/core/src/entities";
|
|
17
|
+
* const result = roll(DieType.D20);
|
|
18
|
+
*/
|
|
19
|
+
export const DieType: Readonly<{
|
|
20
|
+
D4: "d4";
|
|
21
|
+
D6: "d6";
|
|
22
|
+
D8: "d8";
|
|
23
|
+
D10: "d10";
|
|
24
|
+
D12: "d12";
|
|
25
|
+
D20: "d20";
|
|
26
|
+
}>;
|
|
27
|
+
/**
|
|
28
|
+
* Checks whether a given value is a valid `DieType`.
|
|
29
|
+
*
|
|
30
|
+
* @function isValidDieType
|
|
31
|
+
* @param {DieTypeValue | null | undefined} dieType
|
|
32
|
+
* @returns {boolean}
|
|
33
|
+
*/
|
|
34
|
+
export function isValidDieType(dieType: DieTypeValue | null | undefined): boolean;
|
|
35
|
+
//# sourceMappingURL=DieType.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DieType.d.ts","sourceRoot":"","sources":["../../src/entities/DieType.js"],"names":[],"mappings":"yBAmCa,MAAM,OAAO,OAAO;2BACpB,CAAA,OAAO,OAAO,EAAC,MAAM,OAAO,OAAO,CAAC;;;;sBA7BvC,MAAM;AANhB;;;;;;;;;;;GAWG;AACH;;;;;;;GAOG;AAEH;;;;;;GAMG;AACH,wCAHW,YAAY,GAAG,IAAI,GAAG,SAAS,GAC7B,OAAO,CAKnB"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @module @platonic-dice/core/src/entities/DieType
|
|
4
|
+
* @description
|
|
5
|
+
* Enum for die types used in rolling functions.
|
|
6
|
+
*
|
|
7
|
+
* @readonly
|
|
8
|
+
* @enum {string}
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* import { DieType } from "@platonic-dice/core/src/entities";
|
|
12
|
+
* const result = roll(DieType.D20);
|
|
13
|
+
*/
|
|
14
|
+
const DieType = Object.freeze({
|
|
15
|
+
D4: "d4",
|
|
16
|
+
D6: "d6",
|
|
17
|
+
D8: "d8",
|
|
18
|
+
D10: "d10",
|
|
19
|
+
D12: "d12",
|
|
20
|
+
D20: "d20",
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Checks whether a given value is a valid `DieType`.
|
|
25
|
+
*
|
|
26
|
+
* @function isValidDieType
|
|
27
|
+
* @param {DieTypeValue | null | undefined} dieType
|
|
28
|
+
* @returns {boolean}
|
|
29
|
+
*/
|
|
30
|
+
function isValidDieType(dieType) {
|
|
31
|
+
if (!dieType) return false;
|
|
32
|
+
return Object.values(DieType).includes(dieType);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @typedef {keyof typeof DieType} DieTypeKey
|
|
37
|
+
* @typedef {typeof DieType[keyof typeof DieType]} DieTypeValue
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
module.exports = {
|
|
41
|
+
DieType,
|
|
42
|
+
isValidDieType,
|
|
43
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type OutcomeKey = keyof typeof Outcome;
|
|
2
|
+
export type OutcomeValue = (typeof Outcome)[keyof typeof Outcome];
|
|
3
|
+
export type Outcome = string;
|
|
4
|
+
/**
|
|
5
|
+
* @module @platonic-dice/core/src/entities/Outcome
|
|
6
|
+
* @description
|
|
7
|
+
* Enum for possible roll outcomes.
|
|
8
|
+
*
|
|
9
|
+
* @readonly
|
|
10
|
+
* @enum {string}
|
|
11
|
+
*/
|
|
12
|
+
export const Outcome: Readonly<{
|
|
13
|
+
Success: "success";
|
|
14
|
+
Failure: "failure";
|
|
15
|
+
CriticalSuccess: "critical_success";
|
|
16
|
+
CriticalFailure: "critical_failure";
|
|
17
|
+
}>;
|
|
18
|
+
/**
|
|
19
|
+
* Checks whether a given value is a valid `Outcome`.
|
|
20
|
+
*
|
|
21
|
+
* @function isValidOutcome
|
|
22
|
+
* @param {OutcomeValue | null | undefined} outcome
|
|
23
|
+
* @returns {boolean}
|
|
24
|
+
*/
|
|
25
|
+
export function isValidOutcome(outcome: OutcomeValue | null | undefined): boolean;
|
|
26
|
+
//# sourceMappingURL=Outcome.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Outcome.d.ts","sourceRoot":"","sources":["../../src/entities/Outcome.js"],"names":[],"mappings":"yBA6Ba,MAAM,OAAO,OAAO;2BACpB,CAAA,OAAO,OAAO,EAAC,MAAM,OAAO,OAAO,CAAC;sBAvBvC,MAAM;AANhB;;;;;;;GAOG;AACH;;;;;GAKG;AAEH;;;;;;GAMG;AACH,wCAHW,YAAY,GAAG,IAAI,GAAG,SAAS,GAC7B,OAAO,CAKnB"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @module @platonic-dice/core/src/entities/Outcome
|
|
4
|
+
* @description
|
|
5
|
+
* Enum for possible roll outcomes.
|
|
6
|
+
*
|
|
7
|
+
* @readonly
|
|
8
|
+
* @enum {string}
|
|
9
|
+
*/
|
|
10
|
+
const Outcome = Object.freeze({
|
|
11
|
+
Success: "success",
|
|
12
|
+
Failure: "failure",
|
|
13
|
+
CriticalSuccess: "critical_success",
|
|
14
|
+
CriticalFailure: "critical_failure",
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Checks whether a given value is a valid `Outcome`.
|
|
19
|
+
*
|
|
20
|
+
* @function isValidOutcome
|
|
21
|
+
* @param {OutcomeValue | null | undefined} outcome
|
|
22
|
+
* @returns {boolean}
|
|
23
|
+
*/
|
|
24
|
+
function isValidOutcome(outcome) {
|
|
25
|
+
if (!outcome) return false;
|
|
26
|
+
return Object.values(Outcome).includes(outcome);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @typedef {keyof typeof Outcome} OutcomeKey
|
|
31
|
+
* @typedef {typeof Outcome[keyof typeof Outcome]} OutcomeValue
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
module.exports = {
|
|
35
|
+
Outcome,
|
|
36
|
+
isValidOutcome,
|
|
37
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
export type RollModifierFunction = (n: number) => number;
|
|
2
|
+
export type DiceModifier = {
|
|
3
|
+
/**
|
|
4
|
+
* Function or {@link RollModifier} applied to each individual die.
|
|
5
|
+
*/
|
|
6
|
+
each?: RollModifierFunction | RollModifier | null | undefined;
|
|
7
|
+
/**
|
|
8
|
+
* Function or {@link RollModifier} applied to the total (sum) of all dice.
|
|
9
|
+
*/
|
|
10
|
+
net?: RollModifierFunction | RollModifier | null | undefined;
|
|
11
|
+
};
|
|
12
|
+
export type RollModifierInstance = InstanceType<typeof RollModifier>;
|
|
13
|
+
/**
|
|
14
|
+
* @module @platonic-dice/core/src/entities/RollModifier
|
|
15
|
+
* @description
|
|
16
|
+
* Represents a numeric modifier applied to dice rolls.
|
|
17
|
+
*
|
|
18
|
+
* A `RollModifier` wraps a pure function `(n: number) => number`
|
|
19
|
+
* that takes a base roll and returns a modified value.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* const bonus = new RollModifier(n => n + 2);
|
|
23
|
+
* const result = bonus.apply(10); // 12
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* @typedef {(n: number) => number} RollModifierFunction
|
|
27
|
+
* @description
|
|
28
|
+
* A function that takes a single numeric input (the base roll or the total sum)
|
|
29
|
+
* and returns a numeric result. Implementations SHOULD:
|
|
30
|
+
* - declare exactly one parameter (helps static checks: `fn.length === 1`)
|
|
31
|
+
* - accept a number and return a number (ideally integer for dice use-cases)
|
|
32
|
+
*
|
|
33
|
+
* Examples:
|
|
34
|
+
* - Per-die modifier: `(n) => n + 1`
|
|
35
|
+
* - Net modifier: `(sum) => Math.floor(sum * 1.5)`
|
|
36
|
+
*
|
|
37
|
+
* Notes:
|
|
38
|
+
* - The runtime `isValidRollModifier` performs a light validation:
|
|
39
|
+
* checks the function arity and performs a test call `fn(1)` to ensure
|
|
40
|
+
* a numeric, integer-like result is returned. Keep modifiers pure.
|
|
41
|
+
*/
|
|
42
|
+
/**
|
|
43
|
+
* @typedef {Object} DiceModifier
|
|
44
|
+
* @property {RollModifierFunction | RollModifier | null | undefined} [each]
|
|
45
|
+
* Function or {@link RollModifier} applied to each individual die.
|
|
46
|
+
* @property {RollModifierFunction | RollModifier | null | undefined} [net]
|
|
47
|
+
* Function or {@link RollModifier} applied to the total (sum) of all dice.
|
|
48
|
+
*
|
|
49
|
+
* @description
|
|
50
|
+
* Represents the composite modifier structure used by {@link rollDiceMod}.
|
|
51
|
+
* Each field is optional and defaults to the identity modifier if omitted.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* const modifier = {
|
|
55
|
+
* each: (n) => n + 1,
|
|
56
|
+
* net: (sum) => sum + 2,
|
|
57
|
+
* };
|
|
58
|
+
*
|
|
59
|
+
* const mod2 = {
|
|
60
|
+
* each: new RollModifier((n) => n * 2),
|
|
61
|
+
* };
|
|
62
|
+
*/
|
|
63
|
+
/**
|
|
64
|
+
* Represents a numeric modifier applied to dice rolls.
|
|
65
|
+
*/
|
|
66
|
+
export class RollModifier {
|
|
67
|
+
/**
|
|
68
|
+
* @param {RollModifierFunction} fn - Modifier function.
|
|
69
|
+
* @throws {TypeError} If the function is not a valid roll modifier.
|
|
70
|
+
*/
|
|
71
|
+
constructor(fn: RollModifierFunction);
|
|
72
|
+
/** @type {RollModifierFunction} */
|
|
73
|
+
fn: RollModifierFunction;
|
|
74
|
+
/**
|
|
75
|
+
* Applies this modifier to a roll result.
|
|
76
|
+
* @param {number} baseValue - The base roll result.
|
|
77
|
+
* @returns {number} - The modified roll result.
|
|
78
|
+
*/
|
|
79
|
+
apply(baseValue: number): number;
|
|
80
|
+
/**
|
|
81
|
+
* Validates that this modifier still conforms to spec.
|
|
82
|
+
* (Useful if modifiers are loaded dynamically or serialized.)
|
|
83
|
+
* @throws {TypeError} If the modifier is invalid.
|
|
84
|
+
*/
|
|
85
|
+
validate(): void;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Checks whether a given function is a valid roll modifier.
|
|
89
|
+
*
|
|
90
|
+
* @function isValidRollModifier
|
|
91
|
+
* @param {Function | null} m
|
|
92
|
+
* @returns {boolean}
|
|
93
|
+
*/
|
|
94
|
+
export function isValidRollModifier(m: Function | null): boolean;
|
|
95
|
+
/**
|
|
96
|
+
* This function ensures all modifiers conform to the correct structure:
|
|
97
|
+
* - A {@link RollModifier} instance → returned as-is.
|
|
98
|
+
* - A function `(n: number) => number` → wrapped in a new {@link RollModifier}.
|
|
99
|
+
* - `null` or `undefined` → treated as an identity modifier.
|
|
100
|
+
*
|
|
101
|
+
* @function normaliseRollModifier
|
|
102
|
+
* @param {RollModifier | ((n: number) => number) | null | undefined} m
|
|
103
|
+
* The input modifier to normalise.
|
|
104
|
+
* @returns {RollModifier}
|
|
105
|
+
* A valid {@link RollModifier} instance.
|
|
106
|
+
* @throws {TypeError}
|
|
107
|
+
* If the input is invalid (not a RollModifier, function, or null/undefined).
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* const rm1 = normaliseRollModifier(); // → identity modifier
|
|
111
|
+
* const rm2 = normaliseRollModifier(x => x + 1); // → RollModifier wrapping function
|
|
112
|
+
* const rm3 = normaliseRollModifier(new RollModifier(x => x * 2)); // → same instance
|
|
113
|
+
*/
|
|
114
|
+
export function normaliseRollModifier(m: RollModifier | ((n: number) => number) | null | undefined): RollModifier;
|
|
115
|
+
//# sourceMappingURL=RollModifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RollModifier.d.ts","sourceRoot":"","sources":["../../src/entities/RollModifier.js"],"names":[],"mappings":"mCAca,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM;;;;;WAmBpB,oBAAoB,GAAG,YAAY,GAAG,IAAI,GAAG,SAAS;;;;UAEtD,oBAAoB,GAAG,YAAY,GAAG,IAAI,GAAG,SAAS;;mCAwGvD,YAAY,CAAC,OAAO,YAAY,CAAC;AA3I9C;;;;;;;;;;;GAWG;AAEH;;;;;;;;;;;;;;;;GAgBG;AAEH;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;GAEG;AACH;IACE;;;OAGG;IACH,gBAHW,oBAAoB,EAY9B;IAFC,mCAAmC;IACnC,IADW,oBAAoB,CACnB;IAGd;;;;OAIG;IACH,iBAHW,MAAM,GACJ,MAAM,CAIlB;IAED;;;;OAIG;IACH,iBAIC;CACF;AAED;;;;;;GAMG;AACH,uCAHW,WAAW,IAAI,GACb,OAAO,CAWnB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,yCAZW,YAAY,GAAG,CAAC,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC,GAAG,IAAI,GAAG,SAAS,GAEvD,YAAY,CAgBxB"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @platonic-dice/core/src/entities/RollModifier
|
|
3
|
+
* @description
|
|
4
|
+
* Represents a numeric modifier applied to dice rolls.
|
|
5
|
+
*
|
|
6
|
+
* A `RollModifier` wraps a pure function `(n: number) => number`
|
|
7
|
+
* that takes a base roll and returns a modified value.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* const bonus = new RollModifier(n => n + 2);
|
|
11
|
+
* const result = bonus.apply(10); // 12
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {(n: number) => number} RollModifierFunction
|
|
16
|
+
* @description
|
|
17
|
+
* A function that takes a single numeric input (the base roll or the total sum)
|
|
18
|
+
* and returns a numeric result. Implementations SHOULD:
|
|
19
|
+
* - declare exactly one parameter (helps static checks: `fn.length === 1`)
|
|
20
|
+
* - accept a number and return a number (ideally integer for dice use-cases)
|
|
21
|
+
*
|
|
22
|
+
* Examples:
|
|
23
|
+
* - Per-die modifier: `(n) => n + 1`
|
|
24
|
+
* - Net modifier: `(sum) => Math.floor(sum * 1.5)`
|
|
25
|
+
*
|
|
26
|
+
* Notes:
|
|
27
|
+
* - The runtime `isValidRollModifier` performs a light validation:
|
|
28
|
+
* checks the function arity and performs a test call `fn(1)` to ensure
|
|
29
|
+
* a numeric, integer-like result is returned. Keep modifiers pure.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @typedef {Object} DiceModifier
|
|
34
|
+
* @property {RollModifierFunction | RollModifier | null | undefined} [each]
|
|
35
|
+
* Function or {@link RollModifier} applied to each individual die.
|
|
36
|
+
* @property {RollModifierFunction | RollModifier | null | undefined} [net]
|
|
37
|
+
* Function or {@link RollModifier} applied to the total (sum) of all dice.
|
|
38
|
+
*
|
|
39
|
+
* @description
|
|
40
|
+
* Represents the composite modifier structure used by {@link rollDiceMod}.
|
|
41
|
+
* Each field is optional and defaults to the identity modifier if omitted.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* const modifier = {
|
|
45
|
+
* each: (n) => n + 1,
|
|
46
|
+
* net: (sum) => sum + 2,
|
|
47
|
+
* };
|
|
48
|
+
*
|
|
49
|
+
* const mod2 = {
|
|
50
|
+
* each: new RollModifier((n) => n * 2),
|
|
51
|
+
* };
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Represents a numeric modifier applied to dice rolls.
|
|
56
|
+
*/
|
|
57
|
+
class RollModifier {
|
|
58
|
+
/**
|
|
59
|
+
* @param {RollModifierFunction} fn - Modifier function.
|
|
60
|
+
* @throws {TypeError} If the function is not a valid roll modifier.
|
|
61
|
+
*/
|
|
62
|
+
constructor(fn) {
|
|
63
|
+
if (!isValidRollModifier(fn)) {
|
|
64
|
+
throw new TypeError(
|
|
65
|
+
"Invalid roll modifier: must be a function accepting one numeric argument and returning a number."
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** @type {RollModifierFunction} */
|
|
70
|
+
this.fn = fn;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Applies this modifier to a roll result.
|
|
75
|
+
* @param {number} baseValue - The base roll result.
|
|
76
|
+
* @returns {number} - The modified roll result.
|
|
77
|
+
*/
|
|
78
|
+
apply(baseValue) {
|
|
79
|
+
return this.fn(baseValue);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Validates that this modifier still conforms to spec.
|
|
84
|
+
* (Useful if modifiers are loaded dynamically or serialized.)
|
|
85
|
+
* @throws {TypeError} If the modifier is invalid.
|
|
86
|
+
*/
|
|
87
|
+
validate() {
|
|
88
|
+
if (!isValidRollModifier(this.fn)) {
|
|
89
|
+
throw new TypeError("Invalid roll modifier function shape.");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Checks whether a given function is a valid roll modifier.
|
|
96
|
+
*
|
|
97
|
+
* @function isValidRollModifier
|
|
98
|
+
* @param {Function | null} m
|
|
99
|
+
* @returns {boolean}
|
|
100
|
+
*/
|
|
101
|
+
function isValidRollModifier(m) {
|
|
102
|
+
if (!m || typeof m !== "function") return false;
|
|
103
|
+
|
|
104
|
+
/** ---Validate modifier shape --- */
|
|
105
|
+
if (m.length !== 1) return false; // Must declare exactly 1 parameter
|
|
106
|
+
|
|
107
|
+
// Quick runtime check: apply to a number and verify return is an integer.
|
|
108
|
+
const testValue = m(1);
|
|
109
|
+
return typeof testValue === "number" && Number.isInteger(testValue);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* This function ensures all modifiers conform to the correct structure:
|
|
114
|
+
* - A {@link RollModifier} instance → returned as-is.
|
|
115
|
+
* - A function `(n: number) => number` → wrapped in a new {@link RollModifier}.
|
|
116
|
+
* - `null` or `undefined` → treated as an identity modifier.
|
|
117
|
+
*
|
|
118
|
+
* @function normaliseRollModifier
|
|
119
|
+
* @param {RollModifier | ((n: number) => number) | null | undefined} m
|
|
120
|
+
* The input modifier to normalise.
|
|
121
|
+
* @returns {RollModifier}
|
|
122
|
+
* A valid {@link RollModifier} instance.
|
|
123
|
+
* @throws {TypeError}
|
|
124
|
+
* If the input is invalid (not a RollModifier, function, or null/undefined).
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* const rm1 = normaliseRollModifier(); // → identity modifier
|
|
128
|
+
* const rm2 = normaliseRollModifier(x => x + 1); // → RollModifier wrapping function
|
|
129
|
+
* const rm3 = normaliseRollModifier(new RollModifier(x => x * 2)); // → same instance
|
|
130
|
+
*/
|
|
131
|
+
function normaliseRollModifier(m) {
|
|
132
|
+
if (!m) return new RollModifier((n) => n); // identity modifier
|
|
133
|
+
if (m instanceof RollModifier) return m;
|
|
134
|
+
if (typeof m === "function" && isValidRollModifier(m))
|
|
135
|
+
return new RollModifier(m);
|
|
136
|
+
throw new TypeError("Invalid RollModifier");
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* @typedef {InstanceType<typeof RollModifier>} RollModifierInstance
|
|
141
|
+
*/
|
|
142
|
+
|
|
143
|
+
module.exports = {
|
|
144
|
+
RollModifier,
|
|
145
|
+
isValidRollModifier,
|
|
146
|
+
normaliseRollModifier,
|
|
147
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type RollTypeKey = keyof typeof RollType;
|
|
2
|
+
export type RollTypeValue = (typeof RollType)[keyof typeof RollType];
|
|
3
|
+
export type RollType = string;
|
|
4
|
+
/**
|
|
5
|
+
* @module @platonic-dice/core/src/entities/RollType
|
|
6
|
+
* @description
|
|
7
|
+
* Enum for roll modes (normal, advantage, disadvantage).
|
|
8
|
+
*
|
|
9
|
+
* @readonly
|
|
10
|
+
* @enum {string}
|
|
11
|
+
*/
|
|
12
|
+
export const RollType: Readonly<{
|
|
13
|
+
Advantage: "advantage";
|
|
14
|
+
Disadvantage: "disadvantage";
|
|
15
|
+
}>;
|
|
16
|
+
/**
|
|
17
|
+
* Checks whether a given value is a valid `RollType`.
|
|
18
|
+
*
|
|
19
|
+
* @function isValidRollType
|
|
20
|
+
* @param {RollTypeValue | null | undefined} rollType
|
|
21
|
+
* @returns {boolean}
|
|
22
|
+
*/
|
|
23
|
+
export function isValidRollType(rollType: RollTypeValue | null | undefined): boolean;
|
|
24
|
+
//# sourceMappingURL=RollType.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RollType.d.ts","sourceRoot":"","sources":["../../src/entities/RollType.js"],"names":[],"mappings":"0BA2Ba,MAAM,OAAO,QAAQ;4BACrB,CAAA,OAAO,QAAQ,EAAC,MAAM,OAAO,QAAQ,CAAC;uBArBzC,MAAM;AANhB;;;;;;;GAOG;AACH;;;GAGG;AAEH;;;;;;GAMG;AACH,0CAHW,aAAa,GAAG,IAAI,GAAG,SAAS,GAC9B,OAAO,CAKnB"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @module @platonic-dice/core/src/entities/RollType
|
|
4
|
+
* @description
|
|
5
|
+
* Enum for roll modes (normal, advantage, disadvantage).
|
|
6
|
+
*
|
|
7
|
+
* @readonly
|
|
8
|
+
* @enum {string}
|
|
9
|
+
*/
|
|
10
|
+
const RollType = Object.freeze({
|
|
11
|
+
Advantage: "advantage",
|
|
12
|
+
Disadvantage: "disadvantage",
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Checks whether a given value is a valid `RollType`.
|
|
17
|
+
*
|
|
18
|
+
* @function isValidRollType
|
|
19
|
+
* @param {RollTypeValue | null | undefined} rollType
|
|
20
|
+
* @returns {boolean}
|
|
21
|
+
*/
|
|
22
|
+
function isValidRollType(rollType) {
|
|
23
|
+
if (!rollType) return false;
|
|
24
|
+
return Object.values(RollType).includes(rollType);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @typedef {keyof typeof RollType} RollTypeKey
|
|
29
|
+
* @typedef {typeof RollType[keyof typeof RollType]} RollTypeValue
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
module.exports = {
|
|
33
|
+
RollType,
|
|
34
|
+
isValidRollType,
|
|
35
|
+
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
export type BaseTestCondition = {
|
|
2
|
+
dieType: DieTypeValue;
|
|
3
|
+
};
|
|
4
|
+
export type TargetConditions = BaseTestCondition & {
|
|
5
|
+
target: number;
|
|
6
|
+
};
|
|
7
|
+
export type WithinConditions = BaseTestCondition & {
|
|
8
|
+
min: number;
|
|
9
|
+
max: number;
|
|
10
|
+
};
|
|
11
|
+
export type SpecificListConditions = BaseTestCondition & {
|
|
12
|
+
values: number[];
|
|
13
|
+
};
|
|
14
|
+
export type SkillConditions = BaseTestCondition & {
|
|
15
|
+
target: number;
|
|
16
|
+
critical_success?: number;
|
|
17
|
+
critical_failure?: number;
|
|
18
|
+
};
|
|
19
|
+
export type TestTypeValue = import("./TestType").TestTypeValue;
|
|
20
|
+
export type DieTypeValue = import("./DieType").DieTypeValue;
|
|
21
|
+
/**
|
|
22
|
+
* Represents any valid dice roll test condition.
|
|
23
|
+
*
|
|
24
|
+
* This is a union type of all the internal test condition shapes:
|
|
25
|
+
* - `TargetConditions` – single target value
|
|
26
|
+
* - `SkillConditions` – target with optional critical thresholds
|
|
27
|
+
* - `WithinConditions` – min/max range
|
|
28
|
+
* - `SpecificListConditions` – array of specific allowed values
|
|
29
|
+
*/
|
|
30
|
+
export type Conditions = TargetConditions | SkillConditions | WithinConditions | SpecificListConditions;
|
|
31
|
+
export type TestConditionsInstance = InstanceType<typeof TestConditions>;
|
|
32
|
+
/**
|
|
33
|
+
* @typedef {import("./TestType").TestTypeValue} TestTypeValue
|
|
34
|
+
* @typedef {import("./DieType").DieTypeValue} DieTypeValue
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* Represents a set of conditions for a dice roll test.
|
|
38
|
+
*/
|
|
39
|
+
export class TestConditions {
|
|
40
|
+
/**
|
|
41
|
+
* @param {TestTypeValue} testType - The test type.
|
|
42
|
+
* @param {Conditions} conditions - The test conditions object.
|
|
43
|
+
* @param {DieTypeValue} dieType - The die type to validate numeric ranges.
|
|
44
|
+
* @throws {TypeError|RangeError} If the test type or conditions are invalid.
|
|
45
|
+
*/
|
|
46
|
+
constructor(testType: TestTypeValue, conditions: Conditions, dieType: DieTypeValue);
|
|
47
|
+
/** @type {TestTypeValue} */
|
|
48
|
+
testType: TestTypeValue;
|
|
49
|
+
/** @type {Conditions} */
|
|
50
|
+
conditions: Conditions;
|
|
51
|
+
/** @type {DieTypeValue} */
|
|
52
|
+
dieType: DieTypeValue;
|
|
53
|
+
/**
|
|
54
|
+
* Validates that the test conditions still conforms to spec.
|
|
55
|
+
* (Useful if they are loaded dynamically or serialized.)
|
|
56
|
+
* @throws {TypeError} If the test conditions are invalid.
|
|
57
|
+
*/
|
|
58
|
+
validate(): void;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Master validation function for all test conditions.
|
|
62
|
+
*
|
|
63
|
+
* @function areValidTestConditions
|
|
64
|
+
* @param {Conditions} c
|
|
65
|
+
* @param {TestTypeValue} testType
|
|
66
|
+
* @returns {boolean}
|
|
67
|
+
*/
|
|
68
|
+
export function areValidTestConditions(c: Conditions, testType: TestTypeValue): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Normalises any input into a {@link TestConditions} instance.
|
|
71
|
+
* Supports both pre-existing instances and plain objects.
|
|
72
|
+
* Automatically validates all conditions for the specified die type.
|
|
73
|
+
*
|
|
74
|
+
* @function normaliseTestConditions
|
|
75
|
+
* @param {TestConditions | { testType: TestTypeValue, [key: string]: any }} tc
|
|
76
|
+
* A {@link TestConditions} instance or plain object with `testType` and other fields.
|
|
77
|
+
* @param {DieTypeValue} dieType
|
|
78
|
+
* The die type (e.g., `'d6'`, `'d20'`) used for validation.
|
|
79
|
+
* @returns {TestConditions}
|
|
80
|
+
* A validated {@link TestConditions} instance.
|
|
81
|
+
* @throws {TypeError}
|
|
82
|
+
* If the input is neither a TestConditions instance nor a plain object.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* // Passing a plain object
|
|
86
|
+
* const conditions = normaliseTestConditions({ testType: 'atLeast', target: 4 }, 'd6');
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* // Passing an existing TestConditions instance
|
|
90
|
+
* const existing = new TestConditions('exact', { target: 3 }, 'd6');
|
|
91
|
+
* const conditions2 = normaliseTestConditions(existing, 'd6');
|
|
92
|
+
*/
|
|
93
|
+
export function normaliseTestConditions(tc: TestConditions | {
|
|
94
|
+
testType: TestTypeValue;
|
|
95
|
+
[key: string]: any;
|
|
96
|
+
}, dieType: DieTypeValue): TestConditions;
|
|
97
|
+
//# sourceMappingURL=TestConditions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TestConditions.d.ts","sourceRoot":"","sources":["../../src/entities/TestConditions.js"],"names":[],"mappings":";aAiMc,YAAY;;+BAKb,iBAAiB,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE;+BAEtC,iBAAiB,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE;qCAEhD,iBAAiB,GAAG;IAAE,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE;8BAExC,iBAAiB,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE;4BA5L5F,OAAO,YAAY,EAAE,aAAa;2BAClC,OAAO,WAAW,EAAE,YAAY;;;;;;;;;;yBA4JhC,gBAAgB,GAAG,eAAe,GAAG,gBAAgB,GAAG,sBAAsB;qCAI9E,YAAY,CAAC,OAAO,cAAc,CAAC;AAlKhD;;;GAGG;AAEH;;GAEG;AACH;IACE;;;;;OAKG;IACH,sBALW,aAAa,cACb,UAAU,WACV,YAAY,EAgDtB;IANC,4BAA4B;IAC5B,UADW,aAAa,CACA;IACxB,yBAAyB;IACzB,YADW,UAAU,CACO;IAC5B,2BAA2B;IAC3B,SADW,YAAY,CACD;IAGxB;;;;OAIG;IACH,iBASC;CACF;AAED;;;;;;;GAOG;AACH,0CAJW,UAAU,YACV,aAAa,GACX,OAAO,CAwBnB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,4CAlBW,cAAc,GAAG;IAAE,QAAQ,EAAE,aAAa,CAAC;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CAAE,WAEhE,YAAY,GAEV,cAAc,CA0B1B"}
|