@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.
- package/LICENSE +21 -0
- package/README.md +43 -0
- package/coverage/clover.xml +113 -0
- package/coverage/coverage-final.json +4 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/dice.ts.html +817 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +146 -0
- package/coverage/lcov-report/index.ts.html +109 -0
- package/coverage/lcov-report/pools.ts.html +253 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +196 -0
- package/coverage/lcov.info +246 -0
- package/dist/dice.d.ts +2 -0
- package/dist/dice.js +229 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +10 -0
- package/dist/pools.d.ts +32 -0
- package/dist/pools.js +53 -0
- package/dist/types.d.ts +26 -0
- package/dist/types.js +2 -0
- package/jest.config.js +7 -0
- package/package.json +27 -0
- package/src/dice.js +229 -0
- package/src/dice.ts +244 -0
- package/src/index.ts +8 -0
- package/src/pools.ts +56 -0
- package/src/types.js +2 -0
- package/src/types.ts +30 -0
- package/tests/dice.test.ts +351 -0
- package/tests/pools.test.ts +86 -0
- package/tsconfig.json +14 -0
package/src/types.js
ADDED
package/src/types.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export type DicePool = {
|
|
2
|
+
boostDice?: number;
|
|
3
|
+
abilityDice?: number;
|
|
4
|
+
proficiencyDice?: number;
|
|
5
|
+
setBackDice?: number;
|
|
6
|
+
difficultyDice?: number;
|
|
7
|
+
challengeDice?: number;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type DiceResult = {
|
|
11
|
+
successes: number;
|
|
12
|
+
failures: number;
|
|
13
|
+
advantages: number;
|
|
14
|
+
threats: number;
|
|
15
|
+
triumphs: number;
|
|
16
|
+
despair: number;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type DieType = 'boost' | 'ability' | 'proficiency' | 'setback' | 'difficulty' | 'challenge';
|
|
20
|
+
|
|
21
|
+
export type DetailedDieResult = {
|
|
22
|
+
type: DieType;
|
|
23
|
+
roll: number;
|
|
24
|
+
result: DiceResult;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export type RollResult = {
|
|
28
|
+
results: DetailedDieResult[];
|
|
29
|
+
summary: DiceResult;
|
|
30
|
+
};
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
import { roll } from '../src/dice';
|
|
2
|
+
import { DicePool, RollResult } from '../src/types';
|
|
3
|
+
|
|
4
|
+
// Mock Math.random for deterministic tests
|
|
5
|
+
const mockMathRandom = (value: number) => {
|
|
6
|
+
const originalRandom = Math.random;
|
|
7
|
+
Math.random = jest.fn().mockReturnValue(value);
|
|
8
|
+
return () => {
|
|
9
|
+
Math.random = originalRandom;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
describe('SWRPG Dice Rolling', () => {
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
jest.restoreAllMocks();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe('Detailed Roll Breakdown', () => {
|
|
19
|
+
test('returns detailed results for each die', () => {
|
|
20
|
+
const cleanup = mockMathRandom(0.5); // mid-range roll
|
|
21
|
+
const pool: DicePool = {
|
|
22
|
+
boostDice: 1,
|
|
23
|
+
abilityDice: 1
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const result = roll(pool);
|
|
27
|
+
|
|
28
|
+
expect(result).toHaveProperty('results');
|
|
29
|
+
expect(result).toHaveProperty('summary');
|
|
30
|
+
expect(Array.isArray(result.results)).toBe(true);
|
|
31
|
+
expect(result.results).toHaveLength(2);
|
|
32
|
+
|
|
33
|
+
// Check structure of detailed results
|
|
34
|
+
result.results.forEach(dieResult => {
|
|
35
|
+
expect(dieResult).toHaveProperty('type');
|
|
36
|
+
expect(dieResult).toHaveProperty('roll');
|
|
37
|
+
expect(dieResult).toHaveProperty('result');
|
|
38
|
+
expect(['boost', 'ability']).toContain(dieResult.type);
|
|
39
|
+
expect(typeof dieResult.roll).toBe('number');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
cleanup();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('properly identifies die types', () => {
|
|
46
|
+
const cleanup = mockMathRandom(0.5);
|
|
47
|
+
const pool: DicePool = {
|
|
48
|
+
boostDice: 1,
|
|
49
|
+
abilityDice: 1,
|
|
50
|
+
proficiencyDice: 1,
|
|
51
|
+
setBackDice: 1,
|
|
52
|
+
difficultyDice: 1,
|
|
53
|
+
challengeDice: 1
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const result = roll(pool);
|
|
57
|
+
const dieTypes = result.results.map(r => r.type);
|
|
58
|
+
|
|
59
|
+
expect(dieTypes).toContain('boost');
|
|
60
|
+
expect(dieTypes).toContain('ability');
|
|
61
|
+
expect(dieTypes).toContain('proficiency');
|
|
62
|
+
expect(dieTypes).toContain('setback');
|
|
63
|
+
expect(dieTypes).toContain('difficulty');
|
|
64
|
+
expect(dieTypes).toContain('challenge');
|
|
65
|
+
|
|
66
|
+
cleanup();
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('Individual Die Results', () => {
|
|
71
|
+
describe('Boost Die (d6)', () => {
|
|
72
|
+
test.each([
|
|
73
|
+
[1, { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
|
|
74
|
+
[2, { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
|
|
75
|
+
[3, { successes: 1, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
|
|
76
|
+
[4, { successes: 1, failures: 0, advantages: 1, threats: 0, triumphs: 0, despair: 0 }],
|
|
77
|
+
[5, { successes: 0, failures: 0, advantages: 2, threats: 0, triumphs: 0, despair: 0 }],
|
|
78
|
+
[6, { successes: 0, failures: 0, advantages: 1, threats: 0, triumphs: 0, despair: 0 }]
|
|
79
|
+
])('face value %i should return correct results', (value, expected) => {
|
|
80
|
+
const cleanup = mockMathRandom((value - 1) / 6);
|
|
81
|
+
const pool: DicePool = { boostDice: 1 };
|
|
82
|
+
expect(roll(pool).summary).toEqual(expected);
|
|
83
|
+
cleanup();
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('Ability Die (d8)', () => {
|
|
88
|
+
test.each([
|
|
89
|
+
[1, { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
|
|
90
|
+
[2, { successes: 1, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
|
|
91
|
+
[3, { successes: 1, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
|
|
92
|
+
[4, { successes: 2, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
|
|
93
|
+
[5, { successes: 0, failures: 0, advantages: 1, threats: 0, triumphs: 0, despair: 0 }],
|
|
94
|
+
[6, { successes: 0, failures: 0, advantages: 1, threats: 0, triumphs: 0, despair: 0 }],
|
|
95
|
+
[7, { successes: 1, failures: 0, advantages: 1, threats: 0, triumphs: 0, despair: 0 }],
|
|
96
|
+
[8, { successes: 0, failures: 0, advantages: 2, threats: 0, triumphs: 0, despair: 0 }]
|
|
97
|
+
])('face value %i should return correct results', (value, expected) => {
|
|
98
|
+
const cleanup = mockMathRandom((value - 1) / 8);
|
|
99
|
+
const pool: DicePool = { abilityDice: 1 };
|
|
100
|
+
expect(roll(pool).summary).toEqual(expected);
|
|
101
|
+
cleanup();
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('Proficiency Die (d12)', () => {
|
|
106
|
+
test.each([
|
|
107
|
+
[1, { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
|
|
108
|
+
[2, { successes: 1, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
|
|
109
|
+
[3, { successes: 1, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
|
|
110
|
+
[4, { successes: 2, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
|
|
111
|
+
[5, { successes: 2, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
|
|
112
|
+
[6, { successes: 0, failures: 0, advantages: 1, threats: 0, triumphs: 0, despair: 0 }],
|
|
113
|
+
[7, { successes: 1, failures: 0, advantages: 1, threats: 0, triumphs: 0, despair: 0 }],
|
|
114
|
+
[8, { successes: 1, failures: 0, advantages: 1, threats: 0, triumphs: 0, despair: 0 }],
|
|
115
|
+
[9, { successes: 1, failures: 0, advantages: 1, threats: 0, triumphs: 0, despair: 0 }],
|
|
116
|
+
[10, { successes: 0, failures: 0, advantages: 2, threats: 0, triumphs: 0, despair: 0 }],
|
|
117
|
+
[11, { successes: 0, failures: 0, advantages: 2, threats: 0, triumphs: 0, despair: 0 }],
|
|
118
|
+
[12, { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 1, despair: 0 }]
|
|
119
|
+
])('face value %i should return correct results', (value, expected) => {
|
|
120
|
+
const cleanup = mockMathRandom((value - 1) / 12);
|
|
121
|
+
const pool: DicePool = { proficiencyDice: 1 };
|
|
122
|
+
expect(roll(pool).summary).toEqual(expected);
|
|
123
|
+
cleanup();
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('Setback Die (d6)', () => {
|
|
128
|
+
test.each([
|
|
129
|
+
[1, { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
|
|
130
|
+
[2, { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
|
|
131
|
+
[3, { successes: 0, failures: 1, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
|
|
132
|
+
[4, { successes: 0, failures: 1, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
|
|
133
|
+
[5, { successes: 0, failures: 0, advantages: 0, threats: 1, triumphs: 0, despair: 0 }],
|
|
134
|
+
[6, { successes: 0, failures: 0, advantages: 0, threats: 1, triumphs: 0, despair: 0 }]
|
|
135
|
+
])('face value %i should return correct results', (value, expected) => {
|
|
136
|
+
const cleanup = mockMathRandom((value - 1) / 6);
|
|
137
|
+
const pool: DicePool = { setBackDice: 1 };
|
|
138
|
+
expect(roll(pool).summary).toEqual(expected);
|
|
139
|
+
cleanup();
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe('Difficulty Die (d8)', () => {
|
|
144
|
+
test.each([
|
|
145
|
+
[1, { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
|
|
146
|
+
[2, { successes: 0, failures: 1, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
|
|
147
|
+
[3, { successes: 0, failures: 2, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
|
|
148
|
+
[4, { successes: 0, failures: 0, advantages: 0, threats: 1, triumphs: 0, despair: 0 }],
|
|
149
|
+
[5, { successes: 0, failures: 0, advantages: 0, threats: 1, triumphs: 0, despair: 0 }],
|
|
150
|
+
[6, { successes: 0, failures: 0, advantages: 0, threats: 1, triumphs: 0, despair: 0 }],
|
|
151
|
+
[7, { successes: 0, failures: 0, advantages: 0, threats: 2, triumphs: 0, despair: 0 }],
|
|
152
|
+
[8, { successes: 0, failures: 1, advantages: 0, threats: 1, triumphs: 0, despair: 0 }]
|
|
153
|
+
])('face value %i should return correct results', (value, expected) => {
|
|
154
|
+
const cleanup = mockMathRandom((value - 1) / 8);
|
|
155
|
+
const pool: DicePool = { difficultyDice: 1 };
|
|
156
|
+
expect(roll(pool).summary).toEqual(expected);
|
|
157
|
+
cleanup();
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
describe('Challenge Die (d12)', () => {
|
|
162
|
+
test.each([
|
|
163
|
+
[1, { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
|
|
164
|
+
[2, { successes: 0, failures: 1, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
|
|
165
|
+
[3, { successes: 0, failures: 1, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
|
|
166
|
+
[4, { successes: 0, failures: 2, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
|
|
167
|
+
[5, { successes: 0, failures: 2, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
|
|
168
|
+
[6, { successes: 0, failures: 0, advantages: 0, threats: 1, triumphs: 0, despair: 0 }],
|
|
169
|
+
[7, { successes: 0, failures: 0, advantages: 0, threats: 1, triumphs: 0, despair: 0 }],
|
|
170
|
+
[8, { successes: 0, failures: 1, advantages: 0, threats: 1, triumphs: 0, despair: 0 }],
|
|
171
|
+
[9, { successes: 0, failures: 1, advantages: 0, threats: 1, triumphs: 0, despair: 0 }],
|
|
172
|
+
[10, { successes: 0, failures: 0, advantages: 0, threats: 2, triumphs: 0, despair: 0 }],
|
|
173
|
+
[11, { successes: 0, failures: 0, advantages: 0, threats: 2, triumphs: 0, despair: 0 }],
|
|
174
|
+
[12, { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 1 }]
|
|
175
|
+
])('face value %i should return correct results', (value, expected) => {
|
|
176
|
+
const cleanup = mockMathRandom((value - 1) / 12);
|
|
177
|
+
const pool: DicePool = { challengeDice: 1 };
|
|
178
|
+
expect(roll(pool).summary).toEqual(expected);
|
|
179
|
+
cleanup();
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
describe('Edge Cases', () => {
|
|
185
|
+
test('negative numbers default to 0', () => {
|
|
186
|
+
const pool: DicePool = {
|
|
187
|
+
boostDice: -1,
|
|
188
|
+
abilityDice: -2,
|
|
189
|
+
proficiencyDice: -1,
|
|
190
|
+
setBackDice: -3,
|
|
191
|
+
difficultyDice: -2,
|
|
192
|
+
challengeDice: -1
|
|
193
|
+
};
|
|
194
|
+
const expected = {
|
|
195
|
+
successes: 0,
|
|
196
|
+
failures: 0,
|
|
197
|
+
advantages: 0,
|
|
198
|
+
threats: 0,
|
|
199
|
+
triumphs: 0,
|
|
200
|
+
despair: 0
|
|
201
|
+
};
|
|
202
|
+
expect(roll(pool).summary).toEqual(expected);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test('undefined values default to 0', () => {
|
|
206
|
+
const pool: DicePool = {
|
|
207
|
+
boostDice: undefined,
|
|
208
|
+
abilityDice: undefined,
|
|
209
|
+
proficiencyDice: undefined,
|
|
210
|
+
setBackDice: undefined,
|
|
211
|
+
difficultyDice: undefined,
|
|
212
|
+
challengeDice: undefined
|
|
213
|
+
};
|
|
214
|
+
const expected = {
|
|
215
|
+
successes: 0,
|
|
216
|
+
failures: 0,
|
|
217
|
+
advantages: 0,
|
|
218
|
+
threats: 0,
|
|
219
|
+
triumphs: 0,
|
|
220
|
+
despair: 0
|
|
221
|
+
};
|
|
222
|
+
expect(roll(pool).summary).toEqual(expected);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test('empty pool returns zero results', () => {
|
|
226
|
+
const pool: DicePool = {};
|
|
227
|
+
const expected = {
|
|
228
|
+
successes: 0,
|
|
229
|
+
failures: 0,
|
|
230
|
+
advantages: 0,
|
|
231
|
+
threats: 0,
|
|
232
|
+
triumphs: 0,
|
|
233
|
+
despair: 0
|
|
234
|
+
};
|
|
235
|
+
expect(roll(pool).summary).toEqual(expected);
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
describe('Result Accumulation', () => {
|
|
240
|
+
test('successes and failures cancel out', () => {
|
|
241
|
+
// Mock to generate one success and one failure
|
|
242
|
+
const cleanup1 = mockMathRandom(1/8); // ability die success (face value 2)
|
|
243
|
+
const cleanup2 = mockMathRandom(1/8); // difficulty die failure (face value 2)
|
|
244
|
+
|
|
245
|
+
const pool: DicePool = {
|
|
246
|
+
abilityDice: 1,
|
|
247
|
+
difficultyDice: 1
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const result = roll(pool);
|
|
251
|
+
expect(result.summary.successes).toBe(0);
|
|
252
|
+
expect(result.summary.failures).toBe(0);
|
|
253
|
+
|
|
254
|
+
cleanup1();
|
|
255
|
+
cleanup2();
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test('advantages and threats accumulate independently', () => {
|
|
259
|
+
// Mock to generate advantages and threats
|
|
260
|
+
const cleanup1 = mockMathRandom(4/6); // boost die success + advantage
|
|
261
|
+
const cleanup2 = mockMathRandom(6/8); // difficulty die threat
|
|
262
|
+
|
|
263
|
+
const pool: DicePool = {
|
|
264
|
+
boostDice: 1,
|
|
265
|
+
difficultyDice: 1
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
const result = roll(pool);
|
|
269
|
+
expect(result.summary.advantages).toBeGreaterThan(0);
|
|
270
|
+
expect(result.summary.threats).toBeGreaterThan(0);
|
|
271
|
+
|
|
272
|
+
cleanup1();
|
|
273
|
+
cleanup2();
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
test('triumphs and despair count independently', () => {
|
|
277
|
+
// Mock to generate one triumph and one despair
|
|
278
|
+
const cleanup1 = mockMathRandom(11/12); // proficiency die triumph
|
|
279
|
+
const cleanup2 = mockMathRandom(11/12); // challenge die despair
|
|
280
|
+
|
|
281
|
+
const pool: DicePool = {
|
|
282
|
+
proficiencyDice: 1,
|
|
283
|
+
challengeDice: 1
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
const result = roll(pool);
|
|
287
|
+
expect(result.summary.triumphs).toBe(1);
|
|
288
|
+
expect(result.summary.despair).toBe(1);
|
|
289
|
+
|
|
290
|
+
cleanup1();
|
|
291
|
+
cleanup2();
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
describe('Complex Combinations', () => {
|
|
296
|
+
test('realistic skill check - standard difficulty', () => {
|
|
297
|
+
const pool: DicePool = {
|
|
298
|
+
abilityDice: 2,
|
|
299
|
+
proficiencyDice: 1,
|
|
300
|
+
difficultyDice: 2
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
const result = roll(pool);
|
|
304
|
+
|
|
305
|
+
// Verify result structure
|
|
306
|
+
expect(result.summary).toHaveProperty('successes');
|
|
307
|
+
expect(result.summary).toHaveProperty('failures');
|
|
308
|
+
expect(result.summary).toHaveProperty('advantages');
|
|
309
|
+
expect(result.summary).toHaveProperty('threats');
|
|
310
|
+
expect(result.summary).toHaveProperty('triumphs');
|
|
311
|
+
expect(result.summary).toHaveProperty('despair');
|
|
312
|
+
|
|
313
|
+
// Verify ranges
|
|
314
|
+
expect(result.summary.successes).toBeGreaterThanOrEqual(0);
|
|
315
|
+
expect(result.summary.failures).toBeGreaterThanOrEqual(0);
|
|
316
|
+
expect(result.summary.advantages).toBeGreaterThanOrEqual(0);
|
|
317
|
+
expect(result.summary.threats).toBeGreaterThanOrEqual(0);
|
|
318
|
+
expect(result.summary.triumphs).toBeGreaterThanOrEqual(0);
|
|
319
|
+
expect(result.summary.despair).toBeGreaterThanOrEqual(0);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
test('opposed check - combat scenario', () => {
|
|
323
|
+
const pool: DicePool = {
|
|
324
|
+
abilityDice: 1,
|
|
325
|
+
proficiencyDice: 1,
|
|
326
|
+
boostDice: 1,
|
|
327
|
+
difficultyDice: 1,
|
|
328
|
+
challengeDice: 1,
|
|
329
|
+
setBackDice: 1
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
const result = roll(pool);
|
|
333
|
+
|
|
334
|
+
// Verify ranges
|
|
335
|
+
expect(result.summary.successes).toBeGreaterThanOrEqual(0);
|
|
336
|
+
expect(result.summary.failures).toBeGreaterThanOrEqual(0);
|
|
337
|
+
expect(result.summary.advantages).toBeGreaterThanOrEqual(0);
|
|
338
|
+
expect(result.summary.threats).toBeGreaterThanOrEqual(0);
|
|
339
|
+
expect(result.summary.triumphs).toBeGreaterThanOrEqual(0);
|
|
340
|
+
expect(result.summary.despair).toBeGreaterThanOrEqual(0);
|
|
341
|
+
|
|
342
|
+
// Check detailed results
|
|
343
|
+
expect(result.results).toHaveLength(6); // One result per die
|
|
344
|
+
result.results.forEach(dieResult => {
|
|
345
|
+
expect(dieResult).toHaveProperty('type');
|
|
346
|
+
expect(dieResult).toHaveProperty('roll');
|
|
347
|
+
expect(dieResult).toHaveProperty('result');
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
});
|
|
351
|
+
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createSkillCheck,
|
|
3
|
+
createCombatCheck,
|
|
4
|
+
createOpposedCheck,
|
|
5
|
+
createDifficultyPool
|
|
6
|
+
} from '../src/pools';
|
|
7
|
+
|
|
8
|
+
describe('Dice Pool Convenience Methods', () => {
|
|
9
|
+
describe('createSkillCheck', () => {
|
|
10
|
+
test('creates basic skill check pool', () => {
|
|
11
|
+
const pool = createSkillCheck(2, 1);
|
|
12
|
+
expect(pool).toEqual({
|
|
13
|
+
abilityDice: 2,
|
|
14
|
+
proficiencyDice: 1
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('creates pool with zero dice', () => {
|
|
19
|
+
const pool = createSkillCheck(0, 0);
|
|
20
|
+
expect(pool).toEqual({
|
|
21
|
+
abilityDice: 0,
|
|
22
|
+
proficiencyDice: 0
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe('createCombatCheck', () => {
|
|
28
|
+
test('creates combat check pool with boost', () => {
|
|
29
|
+
const pool = createCombatCheck(2, 1, 1);
|
|
30
|
+
expect(pool).toEqual({
|
|
31
|
+
abilityDice: 2,
|
|
32
|
+
proficiencyDice: 1,
|
|
33
|
+
boostDice: 1
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('creates combat check pool without boost', () => {
|
|
38
|
+
const pool = createCombatCheck(2, 1);
|
|
39
|
+
expect(pool).toEqual({
|
|
40
|
+
abilityDice: 2,
|
|
41
|
+
proficiencyDice: 1,
|
|
42
|
+
boostDice: 0
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe('createOpposedCheck', () => {
|
|
48
|
+
test('creates opposed check pool with challenge', () => {
|
|
49
|
+
const pool = createOpposedCheck(2, 1, 2, 1);
|
|
50
|
+
expect(pool).toEqual({
|
|
51
|
+
abilityDice: 2,
|
|
52
|
+
proficiencyDice: 1,
|
|
53
|
+
difficultyDice: 2,
|
|
54
|
+
challengeDice: 1
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('creates opposed check pool without challenge', () => {
|
|
59
|
+
const pool = createOpposedCheck(2, 1, 2);
|
|
60
|
+
expect(pool).toEqual({
|
|
61
|
+
abilityDice: 2,
|
|
62
|
+
proficiencyDice: 1,
|
|
63
|
+
difficultyDice: 2,
|
|
64
|
+
challengeDice: 0
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe('createDifficultyPool', () => {
|
|
70
|
+
test('creates difficulty pool with challenge', () => {
|
|
71
|
+
const pool = createDifficultyPool(2, 1);
|
|
72
|
+
expect(pool).toEqual({
|
|
73
|
+
difficultyDice: 2,
|
|
74
|
+
challengeDice: 1
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('creates difficulty pool without challenge', () => {
|
|
79
|
+
const pool = createDifficultyPool(2);
|
|
80
|
+
expect(pool).toEqual({
|
|
81
|
+
difficultyDice: 2,
|
|
82
|
+
challengeDice: 0
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "es2018",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"declaration": true,
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"strict": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"forceConsistentCasingInFileNames": true
|
|
11
|
+
},
|
|
12
|
+
"include": ["src/**/*"],
|
|
13
|
+
"exclude": ["node_modules", "**/*.test.ts"]
|
|
14
|
+
}
|