@swrpg-online/dice 0.5.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.
@@ -0,0 +1,32 @@
1
+ import { DicePool } from './types';
2
+ /**
3
+ * Creates a basic skill check dice pool
4
+ * @param ability Number of ability (green) dice
5
+ * @param proficiency Number of proficiency (yellow) dice
6
+ * @returns DicePool configured for a basic skill check
7
+ */
8
+ export declare const createSkillCheck: (ability: number, proficiency: number) => DicePool;
9
+ /**
10
+ * Creates a combat check dice pool with optional boost die
11
+ * @param ability Number of ability (green) dice
12
+ * @param proficiency Number of proficiency (yellow) dice
13
+ * @param boost Number of boost (blue) dice
14
+ * @returns DicePool configured for a combat check
15
+ */
16
+ export declare const createCombatCheck: (ability: number, proficiency: number, boost?: number) => DicePool;
17
+ /**
18
+ * Creates an opposed check dice pool
19
+ * @param ability Number of ability (green) dice
20
+ * @param proficiency Number of proficiency (yellow) dice
21
+ * @param difficulty Number of difficulty (purple) dice
22
+ * @param challenge Number of challenge (red) dice
23
+ * @returns DicePool configured for an opposed check
24
+ */
25
+ export declare const createOpposedCheck: (ability: number, proficiency: number, difficulty: number, challenge?: number) => DicePool;
26
+ /**
27
+ * Creates a difficulty check dice pool
28
+ * @param difficulty Number of difficulty (purple) dice
29
+ * @param challenge Number of challenge (red) dice
30
+ * @returns DicePool configured for a pure difficulty check
31
+ */
32
+ export declare const createDifficultyPool: (difficulty: number, challenge?: number) => DicePool;
package/dist/pools.js ADDED
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createDifficultyPool = exports.createOpposedCheck = exports.createCombatCheck = exports.createSkillCheck = void 0;
4
+ /**
5
+ * Creates a basic skill check dice pool
6
+ * @param ability Number of ability (green) dice
7
+ * @param proficiency Number of proficiency (yellow) dice
8
+ * @returns DicePool configured for a basic skill check
9
+ */
10
+ const createSkillCheck = (ability, proficiency) => ({
11
+ abilityDice: Math.max(0, ability),
12
+ proficiencyDice: Math.max(0, proficiency)
13
+ });
14
+ exports.createSkillCheck = createSkillCheck;
15
+ /**
16
+ * Creates a combat check dice pool with optional boost die
17
+ * @param ability Number of ability (green) dice
18
+ * @param proficiency Number of proficiency (yellow) dice
19
+ * @param boost Number of boost (blue) dice
20
+ * @returns DicePool configured for a combat check
21
+ */
22
+ const createCombatCheck = (ability, proficiency, boost = 0) => ({
23
+ abilityDice: Math.max(0, ability),
24
+ proficiencyDice: Math.max(0, proficiency),
25
+ boostDice: Math.max(0, boost)
26
+ });
27
+ exports.createCombatCheck = createCombatCheck;
28
+ /**
29
+ * Creates an opposed check dice pool
30
+ * @param ability Number of ability (green) dice
31
+ * @param proficiency Number of proficiency (yellow) dice
32
+ * @param difficulty Number of difficulty (purple) dice
33
+ * @param challenge Number of challenge (red) dice
34
+ * @returns DicePool configured for an opposed check
35
+ */
36
+ const createOpposedCheck = (ability, proficiency, difficulty, challenge = 0) => ({
37
+ abilityDice: Math.max(0, ability),
38
+ proficiencyDice: Math.max(0, proficiency),
39
+ difficultyDice: Math.max(0, difficulty),
40
+ challengeDice: Math.max(0, challenge)
41
+ });
42
+ exports.createOpposedCheck = createOpposedCheck;
43
+ /**
44
+ * Creates a difficulty check dice pool
45
+ * @param difficulty Number of difficulty (purple) dice
46
+ * @param challenge Number of challenge (red) dice
47
+ * @returns DicePool configured for a pure difficulty check
48
+ */
49
+ const createDifficultyPool = (difficulty, challenge = 0) => ({
50
+ difficultyDice: Math.max(0, difficulty),
51
+ challengeDice: Math.max(0, challenge)
52
+ });
53
+ exports.createDifficultyPool = createDifficultyPool;
@@ -0,0 +1,26 @@
1
+ export type DicePool = {
2
+ boostDice?: number;
3
+ abilityDice?: number;
4
+ proficiencyDice?: number;
5
+ setBackDice?: number;
6
+ difficultyDice?: number;
7
+ challengeDice?: number;
8
+ };
9
+ export type DiceResult = {
10
+ successes: number;
11
+ failures: number;
12
+ advantages: number;
13
+ threats: number;
14
+ triumphs: number;
15
+ despair: number;
16
+ };
17
+ export type DieType = 'boost' | 'ability' | 'proficiency' | 'setback' | 'difficulty' | 'challenge';
18
+ export type DetailedDieResult = {
19
+ type: DieType;
20
+ roll: number;
21
+ result: DiceResult;
22
+ };
23
+ export type RollResult = {
24
+ results: DetailedDieResult[];
25
+ summary: DiceResult;
26
+ };
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/jest.config.js ADDED
@@ -0,0 +1,7 @@
1
+ module.exports = {
2
+ preset: 'ts-jest',
3
+ testEnvironment: 'node',
4
+ testMatch: ['**/tests/**/*.test.ts'],
5
+ collectCoverage: true,
6
+ collectCoverageFrom: ['src/**/*.ts']
7
+ };
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@swrpg-online/dice",
3
+ "version": "0.5.0",
4
+ "description": "A TypeScript library for simulating Star Wars RPG narrative dice rolls.",
5
+ "main": "jest.config.js",
6
+ "directories": {
7
+ "test": "tests"
8
+ },
9
+ "scripts": {
10
+ "test": "npx jest --verbose"
11
+ },
12
+ "keywords": [],
13
+ "author": "Chris Pan",
14
+ "license": "MIT",
15
+ "dependencies": {
16
+ "@types/jest": "^29.5.14",
17
+ "@types/node": "^22.10.0",
18
+ "jest": "^29.7.0",
19
+ "ts-jest": "^29.2.5",
20
+ "typescript": "^5.7.2"
21
+ },
22
+ "devDependencies": {},
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "https://github.com/swrpg-online/dice"
26
+ }
27
+ }
package/src/dice.js ADDED
@@ -0,0 +1,229 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.roll = void 0;
4
+ var rollDie = function (sides) { return Math.floor(Math.random() * sides) + 1; };
5
+ var boostDieResult = function (roll) {
6
+ switch (roll) {
7
+ case 3:
8
+ return { successes: 1, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
9
+ case 4:
10
+ return { successes: 1, failures: 0, advantages: 1, threats: 0, triumphs: 0, despair: 0 };
11
+ case 5:
12
+ return { successes: 0, failures: 0, advantages: 2, threats: 0, triumphs: 0, despair: 0 };
13
+ case 6:
14
+ return { successes: 0, failures: 0, advantages: 1, threats: 0, triumphs: 0, despair: 0 };
15
+ default:
16
+ return { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
17
+ }
18
+ };
19
+ var setBackDieResult = function (roll) {
20
+ switch (roll) {
21
+ case 3:
22
+ case 4:
23
+ return { successes: 0, failures: 1, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
24
+ case 5:
25
+ case 6:
26
+ return { successes: 0, failures: 0, advantages: 0, threats: 1, triumphs: 0, despair: 0 };
27
+ default:
28
+ return { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
29
+ }
30
+ };
31
+ var abilityDieResult = function (roll) {
32
+ switch (roll) {
33
+ case 2:
34
+ case 3:
35
+ return { successes: 1, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
36
+ case 4:
37
+ return { successes: 2, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
38
+ case 5:
39
+ case 6:
40
+ return { successes: 0, failures: 0, advantages: 1, threats: 0, triumphs: 0, despair: 0 };
41
+ case 7:
42
+ return { successes: 1, failures: 0, advantages: 1, threats: 0, triumphs: 0, despair: 0 };
43
+ case 8:
44
+ return { successes: 0, failures: 0, advantages: 2, threats: 0, triumphs: 0, despair: 0 };
45
+ default:
46
+ return { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
47
+ }
48
+ };
49
+ var difficultyDieResult = function (roll) {
50
+ switch (roll) {
51
+ case 2:
52
+ return { successes: 0, failures: 1, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
53
+ case 3:
54
+ return { successes: 0, failures: 2, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
55
+ case 4:
56
+ case 5:
57
+ case 6:
58
+ return { successes: 0, failures: 0, advantages: 0, threats: 1, triumphs: 0, despair: 0 };
59
+ case 7:
60
+ return { successes: 0, failures: 0, advantages: 0, threats: 2, triumphs: 0, despair: 0 };
61
+ case 8:
62
+ return { successes: 0, failures: 1, advantages: 0, threats: 1, triumphs: 0, despair: 0 };
63
+ default:
64
+ return { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
65
+ }
66
+ };
67
+ var proficiencyDieResult = function (roll) {
68
+ switch (roll) {
69
+ case 2:
70
+ case 3:
71
+ return { successes: 1, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
72
+ case 4:
73
+ case 5:
74
+ return { successes: 2, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
75
+ case 6:
76
+ return { successes: 0, failures: 0, advantages: 1, threats: 0, triumphs: 0, despair: 0 };
77
+ case 7:
78
+ case 8:
79
+ case 9:
80
+ return { successes: 1, failures: 0, advantages: 1, threats: 0, triumphs: 0, despair: 0 };
81
+ case 10:
82
+ case 11:
83
+ return { successes: 0, failures: 0, advantages: 2, threats: 0, triumphs: 0, despair: 0 };
84
+ case 12:
85
+ return { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 1, despair: 0 };
86
+ default:
87
+ return { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
88
+ }
89
+ };
90
+ var challengeDieResult = function (roll) {
91
+ switch (roll) {
92
+ case 2:
93
+ case 3:
94
+ return { successes: 0, failures: 1, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
95
+ case 4:
96
+ case 5:
97
+ return { successes: 0, failures: 2, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
98
+ case 6:
99
+ case 7:
100
+ return { successes: 0, failures: 0, advantages: 0, threats: 1, triumphs: 0, despair: 0 };
101
+ case 8:
102
+ case 9:
103
+ return { successes: 0, failures: 1, advantages: 0, threats: 1, triumphs: 0, despair: 0 };
104
+ case 10:
105
+ case 11:
106
+ return { successes: 0, failures: 0, advantages: 0, threats: 2, triumphs: 0, despair: 0 };
107
+ case 12:
108
+ return { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 1 };
109
+ default:
110
+ return { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
111
+ }
112
+ };
113
+ var sumResults = function (results) {
114
+ var sums = results.reduce(function (acc, curr) { return ({
115
+ successes: acc.successes + curr.successes,
116
+ failures: acc.failures + curr.failures,
117
+ advantages: acc.advantages + curr.advantages,
118
+ threats: acc.threats + curr.threats,
119
+ triumphs: acc.triumphs + curr.triumphs,
120
+ despair: acc.despair + curr.despair
121
+ }); }, {
122
+ successes: 0,
123
+ failures: 0,
124
+ advantages: 0,
125
+ threats: 0,
126
+ triumphs: 0,
127
+ despair: 0
128
+ });
129
+ // Calculate net successes/failures
130
+ var netSuccesses = 0;
131
+ var netFailures = 0;
132
+ if (sums.successes === sums.failures) {
133
+ netSuccesses = 0;
134
+ netFailures = 0;
135
+ }
136
+ else if (sums.successes > sums.failures) {
137
+ netSuccesses = sums.successes - sums.failures;
138
+ }
139
+ else {
140
+ netFailures = sums.failures - sums.successes;
141
+ }
142
+ return {
143
+ successes: netSuccesses,
144
+ failures: netFailures,
145
+ advantages: sums.advantages,
146
+ threats: sums.threats,
147
+ triumphs: sums.triumphs,
148
+ despair: sums.despair
149
+ };
150
+ };
151
+ var roll = function (pool) {
152
+ var _a, _b, _c, _d, _e, _f;
153
+ // Initialize all dice counts to 0 if undefined
154
+ var boostCount = (_a = pool.boostDice) !== null && _a !== void 0 ? _a : 0;
155
+ var abilityCount = (_b = pool.abilityDice) !== null && _b !== void 0 ? _b : 0;
156
+ var proficiencyCount = (_c = pool.proficiencyDice) !== null && _c !== void 0 ? _c : 0;
157
+ var setBackCount = (_d = pool.setBackDice) !== null && _d !== void 0 ? _d : 0;
158
+ var difficultyCount = (_e = pool.difficultyDice) !== null && _e !== void 0 ? _e : 0;
159
+ var challengeCount = (_f = pool.challengeDice) !== null && _f !== void 0 ? _f : 0;
160
+ // Ensure all dice counts are non-negative
161
+ var sanitizedPool = {
162
+ boostDice: Math.max(0, boostCount),
163
+ abilityDice: Math.max(0, abilityCount),
164
+ proficiencyDice: Math.max(0, proficiencyCount),
165
+ setBackDice: Math.max(0, setBackCount),
166
+ difficultyDice: Math.max(0, difficultyCount),
167
+ challengeDice: Math.max(0, challengeCount)
168
+ };
169
+ var detailedResults = [];
170
+ // Roll boost dice
171
+ for (var i = 0; i < sanitizedPool.boostDice; i++) {
172
+ var roll_1 = rollDie(6);
173
+ detailedResults.push({
174
+ type: 'boost',
175
+ roll: roll_1,
176
+ result: boostDieResult(roll_1)
177
+ });
178
+ }
179
+ // Roll ability dice
180
+ for (var i = 0; i < sanitizedPool.abilityDice; i++) {
181
+ var roll_2 = rollDie(8);
182
+ detailedResults.push({
183
+ type: 'ability',
184
+ roll: roll_2,
185
+ result: abilityDieResult(roll_2)
186
+ });
187
+ }
188
+ // Roll proficiency dice
189
+ for (var i = 0; i < sanitizedPool.proficiencyDice; i++) {
190
+ var roll_3 = rollDie(12);
191
+ detailedResults.push({
192
+ type: 'proficiency',
193
+ roll: roll_3,
194
+ result: proficiencyDieResult(roll_3)
195
+ });
196
+ }
197
+ // Roll setback dice
198
+ for (var i = 0; i < sanitizedPool.setBackDice; i++) {
199
+ var roll_4 = rollDie(6);
200
+ detailedResults.push({
201
+ type: 'setback',
202
+ roll: roll_4,
203
+ result: setBackDieResult(roll_4)
204
+ });
205
+ }
206
+ // Roll difficulty dice
207
+ for (var i = 0; i < sanitizedPool.difficultyDice; i++) {
208
+ var roll_5 = rollDie(8);
209
+ detailedResults.push({
210
+ type: 'difficulty',
211
+ roll: roll_5,
212
+ result: difficultyDieResult(roll_5)
213
+ });
214
+ }
215
+ // Roll challenge dice
216
+ for (var i = 0; i < sanitizedPool.challengeDice; i++) {
217
+ var roll_6 = rollDie(12);
218
+ detailedResults.push({
219
+ type: 'challenge',
220
+ roll: roll_6,
221
+ result: challengeDieResult(roll_6)
222
+ });
223
+ }
224
+ return {
225
+ results: detailedResults,
226
+ summary: sumResults(detailedResults.map(function (r) { return r.result; }))
227
+ };
228
+ };
229
+ exports.roll = roll;
package/src/dice.ts ADDED
@@ -0,0 +1,244 @@
1
+ import { DicePool, RollResult, DiceResult, DetailedDieResult } from './types';
2
+
3
+ const rollDie = (sides: number): number => Math.floor(Math.random() * sides) + 1;
4
+
5
+ const boostDieResult = (roll: number): DiceResult => {
6
+ switch (roll) {
7
+ case 3:
8
+ return { successes: 1, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
9
+ case 4:
10
+ return { successes: 1, failures: 0, advantages: 1, threats: 0, triumphs: 0, despair: 0 };
11
+ case 5:
12
+ return { successes: 0, failures: 0, advantages: 2, threats: 0, triumphs: 0, despair: 0 };
13
+ case 6:
14
+ return { successes: 0, failures: 0, advantages: 1, threats: 0, triumphs: 0, despair: 0 };
15
+ default:
16
+ return { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
17
+ }
18
+ };
19
+
20
+ const setBackDieResult = (roll: number): DiceResult => {
21
+ switch (roll) {
22
+ case 3:
23
+ case 4:
24
+ return { successes: 0, failures: 1, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
25
+ case 5:
26
+ case 6:
27
+ return { successes: 0, failures: 0, advantages: 0, threats: 1, triumphs: 0, despair: 0 };
28
+ default:
29
+ return { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
30
+ }
31
+ };
32
+
33
+ const abilityDieResult = (roll: number): DiceResult => {
34
+ switch (roll) {
35
+ case 2:
36
+ case 3:
37
+ return { successes: 1, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
38
+ case 4:
39
+ return { successes: 2, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
40
+ case 5:
41
+ case 6:
42
+ return { successes: 0, failures: 0, advantages: 1, threats: 0, triumphs: 0, despair: 0 };
43
+ case 7:
44
+ return { successes: 1, failures: 0, advantages: 1, threats: 0, triumphs: 0, despair: 0 };
45
+ case 8:
46
+ return { successes: 0, failures: 0, advantages: 2, threats: 0, triumphs: 0, despair: 0 };
47
+ default:
48
+ return { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
49
+ }
50
+ };
51
+
52
+ const difficultyDieResult = (roll: number): DiceResult => {
53
+ switch (roll) {
54
+ case 2:
55
+ return { successes: 0, failures: 1, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
56
+ case 3:
57
+ return { successes: 0, failures: 2, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
58
+ case 4:
59
+ case 5:
60
+ case 6:
61
+ return { successes: 0, failures: 0, advantages: 0, threats: 1, triumphs: 0, despair: 0 };
62
+ case 7:
63
+ return { successes: 0, failures: 0, advantages: 0, threats: 2, triumphs: 0, despair: 0 };
64
+ case 8:
65
+ return { successes: 0, failures: 1, advantages: 0, threats: 1, triumphs: 0, despair: 0 };
66
+ default:
67
+ return { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
68
+ }
69
+ };
70
+
71
+ const proficiencyDieResult = (roll: number): DiceResult => {
72
+ switch (roll) {
73
+ case 2:
74
+ case 3:
75
+ return { successes: 1, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
76
+ case 4:
77
+ case 5:
78
+ return { successes: 2, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
79
+ case 6:
80
+ return { successes: 0, failures: 0, advantages: 1, threats: 0, triumphs: 0, despair: 0 };
81
+ case 7:
82
+ case 8:
83
+ case 9:
84
+ return { successes: 1, failures: 0, advantages: 1, threats: 0, triumphs: 0, despair: 0 };
85
+ case 10:
86
+ case 11:
87
+ return { successes: 0, failures: 0, advantages: 2, threats: 0, triumphs: 0, despair: 0 };
88
+ case 12:
89
+ return { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 1, despair: 0 };
90
+ default:
91
+ return { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
92
+ }
93
+ };
94
+
95
+ const challengeDieResult = (roll: number): DiceResult => {
96
+ switch (roll) {
97
+ case 2:
98
+ case 3:
99
+ return { successes: 0, failures: 1, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
100
+ case 4:
101
+ case 5:
102
+ return { successes: 0, failures: 2, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
103
+ case 6:
104
+ case 7:
105
+ return { successes: 0, failures: 0, advantages: 0, threats: 1, triumphs: 0, despair: 0 };
106
+ case 8:
107
+ case 9:
108
+ return { successes: 0, failures: 1, advantages: 0, threats: 1, triumphs: 0, despair: 0 };
109
+ case 10:
110
+ case 11:
111
+ return { successes: 0, failures: 0, advantages: 0, threats: 2, triumphs: 0, despair: 0 };
112
+ case 12:
113
+ return { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 1 };
114
+ default:
115
+ return { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 };
116
+ }
117
+ };
118
+
119
+ const sumResults = (results: DiceResult[]): DiceResult => {
120
+ const sums = results.reduce((acc, curr) => ({
121
+ successes: acc.successes + curr.successes,
122
+ failures: acc.failures + curr.failures,
123
+ advantages: acc.advantages + curr.advantages,
124
+ threats: acc.threats + curr.threats,
125
+ triumphs: acc.triumphs + curr.triumphs,
126
+ despair: acc.despair + curr.despair
127
+ }), {
128
+ successes: 0,
129
+ failures: 0,
130
+ advantages: 0,
131
+ threats: 0,
132
+ triumphs: 0,
133
+ despair: 0
134
+ });
135
+
136
+ // Calculate net successes/failures
137
+ let netSuccesses = 0;
138
+ let netFailures = 0;
139
+
140
+ if (sums.successes === sums.failures) {
141
+ netSuccesses = 0;
142
+ netFailures = 0;
143
+ } else if (sums.successes > sums.failures) {
144
+ netSuccesses = sums.successes - sums.failures;
145
+ } else {
146
+ netFailures = sums.failures - sums.successes;
147
+ }
148
+
149
+ return {
150
+ successes: netSuccesses,
151
+ failures: netFailures,
152
+ advantages: sums.advantages,
153
+ threats: sums.threats,
154
+ triumphs: sums.triumphs,
155
+ despair: sums.despair
156
+ };
157
+ };
158
+
159
+ export const roll = (pool: DicePool): RollResult => {
160
+ // Initialize all dice counts to 0 if undefined
161
+ const boostCount = pool.boostDice ?? 0;
162
+ const abilityCount = pool.abilityDice ?? 0;
163
+ const proficiencyCount = pool.proficiencyDice ?? 0;
164
+ const setBackCount = pool.setBackDice ?? 0;
165
+ const difficultyCount = pool.difficultyDice ?? 0;
166
+ const challengeCount = pool.challengeDice ?? 0;
167
+
168
+ // Ensure all dice counts are non-negative
169
+ const sanitizedPool = {
170
+ boostDice: Math.max(0, boostCount),
171
+ abilityDice: Math.max(0, abilityCount),
172
+ proficiencyDice: Math.max(0, proficiencyCount),
173
+ setBackDice: Math.max(0, setBackCount),
174
+ difficultyDice: Math.max(0, difficultyCount),
175
+ challengeDice: Math.max(0, challengeCount)
176
+ };
177
+
178
+ const detailedResults: DetailedDieResult[] = [];
179
+
180
+ // Roll boost dice
181
+ for (let i = 0; i < sanitizedPool.boostDice; i++) {
182
+ const roll = rollDie(6);
183
+ detailedResults.push({
184
+ type: 'boost',
185
+ roll,
186
+ result: boostDieResult(roll)
187
+ });
188
+ }
189
+
190
+ // Roll ability dice
191
+ for (let i = 0; i < sanitizedPool.abilityDice; i++) {
192
+ const roll = rollDie(8);
193
+ detailedResults.push({
194
+ type: 'ability',
195
+ roll,
196
+ result: abilityDieResult(roll)
197
+ });
198
+ }
199
+
200
+ // Roll proficiency dice
201
+ for (let i = 0; i < sanitizedPool.proficiencyDice; i++) {
202
+ const roll = rollDie(12);
203
+ detailedResults.push({
204
+ type: 'proficiency',
205
+ roll,
206
+ result: proficiencyDieResult(roll)
207
+ });
208
+ }
209
+
210
+ // Roll setback dice
211
+ for (let i = 0; i < sanitizedPool.setBackDice; i++) {
212
+ const roll = rollDie(6);
213
+ detailedResults.push({
214
+ type: 'setback',
215
+ roll,
216
+ result: setBackDieResult(roll)
217
+ });
218
+ }
219
+
220
+ // Roll difficulty dice
221
+ for (let i = 0; i < sanitizedPool.difficultyDice; i++) {
222
+ const roll = rollDie(8);
223
+ detailedResults.push({
224
+ type: 'difficulty',
225
+ roll,
226
+ result: difficultyDieResult(roll)
227
+ });
228
+ }
229
+
230
+ // Roll challenge dice
231
+ for (let i = 0; i < sanitizedPool.challengeDice; i++) {
232
+ const roll = rollDie(12);
233
+ detailedResults.push({
234
+ type: 'challenge',
235
+ roll,
236
+ result: challengeDieResult(roll)
237
+ });
238
+ }
239
+
240
+ return {
241
+ results: detailedResults,
242
+ summary: sumResults(detailedResults.map(r => r.result))
243
+ };
244
+ };
package/src/index.ts ADDED
@@ -0,0 +1,8 @@
1
+ export { roll } from './dice';
2
+ export type { DicePool, RollResult } from './types';
3
+ export {
4
+ createSkillCheck,
5
+ createCombatCheck,
6
+ createOpposedCheck,
7
+ createDifficultyPool
8
+ } from './pools';
package/src/pools.ts ADDED
@@ -0,0 +1,56 @@
1
+ import { DicePool } from './types';
2
+
3
+ /**
4
+ * Creates a basic skill check dice pool
5
+ * @param ability Number of ability (green) dice
6
+ * @param proficiency Number of proficiency (yellow) dice
7
+ * @returns DicePool configured for a basic skill check
8
+ */
9
+ export const createSkillCheck = (ability: number, proficiency: number): DicePool => ({
10
+ abilityDice: Math.max(0, ability),
11
+ proficiencyDice: Math.max(0, proficiency)
12
+ });
13
+
14
+ /**
15
+ * Creates a combat check dice pool with optional boost die
16
+ * @param ability Number of ability (green) dice
17
+ * @param proficiency Number of proficiency (yellow) dice
18
+ * @param boost Number of boost (blue) dice
19
+ * @returns DicePool configured for a combat check
20
+ */
21
+ export const createCombatCheck = (ability: number, proficiency: number, boost: number = 0): DicePool => ({
22
+ abilityDice: Math.max(0, ability),
23
+ proficiencyDice: Math.max(0, proficiency),
24
+ boostDice: Math.max(0, boost)
25
+ });
26
+
27
+ /**
28
+ * Creates an opposed check dice pool
29
+ * @param ability Number of ability (green) dice
30
+ * @param proficiency Number of proficiency (yellow) dice
31
+ * @param difficulty Number of difficulty (purple) dice
32
+ * @param challenge Number of challenge (red) dice
33
+ * @returns DicePool configured for an opposed check
34
+ */
35
+ export const createOpposedCheck = (
36
+ ability: number,
37
+ proficiency: number,
38
+ difficulty: number,
39
+ challenge: number = 0
40
+ ): DicePool => ({
41
+ abilityDice: Math.max(0, ability),
42
+ proficiencyDice: Math.max(0, proficiency),
43
+ difficultyDice: Math.max(0, difficulty),
44
+ challengeDice: Math.max(0, challenge)
45
+ });
46
+
47
+ /**
48
+ * Creates a difficulty check dice pool
49
+ * @param difficulty Number of difficulty (purple) dice
50
+ * @param challenge Number of challenge (red) dice
51
+ * @returns DicePool configured for a pure difficulty check
52
+ */
53
+ export const createDifficultyPool = (difficulty: number, challenge: number = 0): DicePool => ({
54
+ difficultyDice: Math.max(0, difficulty),
55
+ challengeDice: Math.max(0, challenge)
56
+ });