@platonic-dice/dice 2.0.3 → 2.1.1

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 CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Persistent dice objects with roll history and TypeScript support. This package builds on top of `@platonic-dice/core` and provides classes such as `Die` which maintain roll history, validators, and utilities for consuming applications.
4
4
 
5
+ Version 2.1.0 adds support for `rollModTest()` - combining modifiers with test evaluation in a single method call.
6
+
5
7
  ## Installation
6
8
 
7
9
  ```bash
@@ -18,7 +20,12 @@ const { DieType } = require("@platonic-dice/core");
18
20
 
19
21
  const d20 = new Die(DieType.D20);
20
22
  console.log(d20.roll());
21
- console.log(d20.history.getAll());
23
+ console.log(d20.history("normal"));
24
+
25
+ // New in 2.1.0: rollModTest combines modifier and test evaluation
26
+ const result = d20.rollModTest((n) => n + 5, { testType: "skill", target: 15 });
27
+ console.log(`Modified result: ${result}`);
28
+ console.log(d20.history("modifiedTest"));
22
29
  ```
23
30
 
24
31
  TypeScript:
@@ -29,6 +36,12 @@ import { DieType } from "@platonic-dice/core";
29
36
 
30
37
  const d20 = new Die(DieType.D20);
31
38
  console.log(d20.roll());
39
+
40
+ // Apply modifier and evaluate against test conditions
41
+ const result = d20.rollModTest((n) => n + 5, {
42
+ testType: "at_least",
43
+ target: 15,
44
+ });
32
45
  ```
33
46
 
34
47
  ## Build
@@ -1,4 +1,4 @@
1
- import { isDieRollRecord, isModifiedDieRollRecord, isTargetDieRollRecord, stripTimestamp, } from "../roll-record-validator.js";
1
+ import { isDieRollRecord, isModifiedDieRollRecord, isTargetDieRollRecord, isModifiedTestDieRollRecord, stripTimestamp, } from "../roll-record-validator.js";
2
2
  import { RollRecordStorage } from "./internal/index.js";
3
3
  /**
4
4
  * Default maximum number of roll records stored.
@@ -66,8 +66,9 @@ export class RollRecordManager {
66
66
  }
67
67
  if (!isDieRollRecord(record) &&
68
68
  !isModifiedDieRollRecord(record) &&
69
- !isTargetDieRollRecord(record)) {
70
- throw new TypeError("Record must be a valid DieRollRecord, ModifiedDieRollRecord, or TargetDieRollRecord");
69
+ !isTargetDieRollRecord(record) &&
70
+ !isModifiedTestDieRollRecord(record)) {
71
+ throw new TypeError("Record must be a valid DieRollRecord, ModifiedDieRollRecord, TestDieRollRecord, or ModifiedTestDieRollRecord");
71
72
  }
72
73
  this.storage.add(record);
73
74
  }
@@ -23,6 +23,15 @@ export function isTargetDieRollRecord(record) {
23
23
  typeof record.roll === "number" &&
24
24
  "outcome" in record &&
25
25
  Object.values(Outcome).includes(record.outcome) &&
26
+ record.timestamp instanceof Date &&
27
+ !("modified" in record));
28
+ }
29
+ export function isModifiedTestDieRollRecord(record) {
30
+ return (!!record &&
31
+ typeof record.roll === "number" &&
32
+ typeof record.modified === "number" &&
33
+ "outcome" in record &&
34
+ Object.values(Outcome).includes(record.outcome) &&
26
35
  record.timestamp instanceof Date);
27
36
  }
28
37
  /**
@@ -40,5 +49,6 @@ export default {
40
49
  isDieRollRecord,
41
50
  isModifiedDieRollRecord,
42
51
  isTargetDieRollRecord,
52
+ isModifiedTestDieRollRecord,
43
53
  stripTimestamp,
44
54
  };
@@ -1,6 +1,5 @@
1
- import core from "@platonic-dice/core";
2
- const { RollType, roll: coreRoll, rollMod: coreRollMod, rollTest: coreRollTest, } = core;
3
- import { isDieRollRecord, isModifiedDieRollRecord, isTargetDieRollRecord, } from "./internal/index.js";
1
+ import { RollType, roll as coreRoll, rollMod as coreRollMod, rollTest as coreRollTest, rollModTest as coreRollModTest, } from "@platonic-dice/core";
2
+ import { isDieRollRecord, isModifiedDieRollRecord, isTargetDieRollRecord, isModifiedTestDieRollRecord, } from "./internal/index.js";
4
3
  /**
5
4
  * Default implementation that delegates to `@platonic-dice/core` and uses
6
5
  * the system clock. This keeps the public API simple and avoids DI.
@@ -52,5 +51,20 @@ export class RollRecordFactory {
52
51
  }
53
52
  return record;
54
53
  }
54
+ createModifiedTestRoll(dieType, modifier, testConditions, rollType, options) {
55
+ // Delegate to core rollModTest which combines modifier and test logic
56
+ const { base, modified, outcome } = coreRollModTest(dieType, modifier, testConditions, rollType, options);
57
+ const ts = new Date();
58
+ const record = {
59
+ roll: base,
60
+ modified,
61
+ outcome,
62
+ timestamp: ts,
63
+ };
64
+ if (!isModifiedTestDieRollRecord(record)) {
65
+ throw new Error("Factory produced invalid ModifiedTestDieRollRecord");
66
+ }
67
+ return record;
68
+ }
55
69
  }
56
70
  export default RollRecordFactory;
package/dist/die.js CHANGED
@@ -164,6 +164,26 @@ export class Die {
164
164
  this.rolls.add(record);
165
165
  return record.roll;
166
166
  }
167
+ /**
168
+ * Perform a roll with a modifier and evaluate against test conditions.
169
+ * Combines rollMod and rollTest functionality.
170
+ *
171
+ * @param {RollModifierFunction | RollModifierInstance} modifier - Numeric or functional modifier applied to the base roll.
172
+ * @param {TestConditionsInstance | { testType: TestTypeValue; [k: string]: any }} testConditions - Test conditions (plain object or normalized `TestConditions` instance).
173
+ * @param {RollTypeValue} [rollType] - Optional roll mode (`RollType.Advantage` / `RollType.Disadvantage`).
174
+ * @param {{useNaturalCrits?: boolean}} [options] - Optional configuration for natural crits behavior.
175
+ * @returns {number} The modified numeric result.
176
+ * @throws {Error} If inputs are invalid (delegated to core).
177
+ * @example
178
+ * d20.rollModTest(n => n + 5, { testType: "at_least", target: 15 });
179
+ */
180
+ rollModTest(modifier, testConditions, rollType, options) {
181
+ const record = this.recordFactory.createModifiedTestRoll(this.typeValue, modifier, testConditions, rollType, options);
182
+ this.resultValue = record.modified;
183
+ this.rolls.setActiveKey(Die.MODIFIED_TEST_KEY);
184
+ this.rolls.add(record);
185
+ return record.modified;
186
+ }
167
187
  /**
168
188
  * Retrieve roll history for a given key.
169
189
  *
@@ -174,7 +194,7 @@ export class Die {
174
194
  /**
175
195
  * Retrieve roll history for a given key.
176
196
  *
177
- * @param {string} [key=Die.NORMAL_KEY] - One of "normal" | "modifier" | "test".
197
+ * @param {string} [key=Die.NORMAL_KEY] - One of "normal" | "modifier" | "test" | "modifiedTest".
178
198
  * @param {boolean} [verbose=false] - Include timestamps when true.
179
199
  * @returns {Array} Array of roll records (timestamps included when verbose).
180
200
  */
@@ -225,3 +245,4 @@ export class Die {
225
245
  Die.NORMAL_KEY = "normal";
226
246
  Die.MODIFIER_KEY = "modifier";
227
247
  Die.TEST_KEY = "test";
248
+ Die.MODIFIED_TEST_KEY = "modifiedTest";
package/dist/index.js CHANGED
@@ -8,4 +8,4 @@ export { Die } from "./die.js";
8
8
  // Re-export core types and values that consumers will need
9
9
  // Note: Import from default due to CommonJS/ESM interop
10
10
  import core from "@platonic-dice/core";
11
- export const { DieType, RollType } = core;
11
+ export const { DieType, RollType, rollModTest } = core;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platonic-dice/dice",
3
- "version": "2.0.3",
3
+ "version": "2.1.1",
4
4
  "description": "Persistent dice objects with roll history and TypeScript support.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -39,7 +39,7 @@
39
39
  "test:watch": "vitest"
40
40
  },
41
41
  "dependencies": {
42
- "@platonic-dice/core": "^2.0.3"
42
+ "@platonic-dice/core": "^2.1.2"
43
43
  },
44
44
  "devDependencies": {
45
45
  "tsc-alias": "^1.8.16",