@mythxengine/engine 0.1.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 (82) hide show
  1. package/LICENSE +21 -0
  2. package/dist/__tests__/advantage.test.d.ts +5 -0
  3. package/dist/__tests__/advantage.test.d.ts.map +1 -0
  4. package/dist/__tests__/advantage.test.js +126 -0
  5. package/dist/__tests__/advantage.test.js.map +1 -0
  6. package/dist/__tests__/dice.test.d.ts +2 -0
  7. package/dist/__tests__/dice.test.d.ts.map +1 -0
  8. package/dist/__tests__/dice.test.js +150 -0
  9. package/dist/__tests__/dice.test.js.map +1 -0
  10. package/dist/__tests__/resolution.test.d.ts +5 -0
  11. package/dist/__tests__/resolution.test.d.ts.map +1 -0
  12. package/dist/__tests__/resolution.test.js +362 -0
  13. package/dist/__tests__/resolution.test.js.map +1 -0
  14. package/dist/__tests__/rng.test.d.ts +2 -0
  15. package/dist/__tests__/rng.test.d.ts.map +1 -0
  16. package/dist/__tests__/rng.test.js +93 -0
  17. package/dist/__tests__/rng.test.js.map +1 -0
  18. package/dist/dice/advantage.d.ts +45 -0
  19. package/dist/dice/advantage.d.ts.map +1 -0
  20. package/dist/dice/advantage.js +118 -0
  21. package/dist/dice/advantage.js.map +1 -0
  22. package/dist/dice/index.d.ts +7 -0
  23. package/dist/dice/index.d.ts.map +1 -0
  24. package/dist/dice/index.js +7 -0
  25. package/dist/dice/index.js.map +1 -0
  26. package/dist/dice/parser.d.ts +21 -0
  27. package/dist/dice/parser.d.ts.map +1 -0
  28. package/dist/dice/parser.js +61 -0
  29. package/dist/dice/parser.js.map +1 -0
  30. package/dist/dice/roller.d.ts +23 -0
  31. package/dist/dice/roller.d.ts.map +1 -0
  32. package/dist/dice/roller.js +62 -0
  33. package/dist/dice/roller.js.map +1 -0
  34. package/dist/index.d.ts +10 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +14 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/resolution/combat.d.ts +39 -0
  39. package/dist/resolution/combat.d.ts.map +1 -0
  40. package/dist/resolution/combat.js +214 -0
  41. package/dist/resolution/combat.js.map +1 -0
  42. package/dist/resolution/damage.d.ts +27 -0
  43. package/dist/resolution/damage.d.ts.map +1 -0
  44. package/dist/resolution/damage.js +44 -0
  45. package/dist/resolution/damage.js.map +1 -0
  46. package/dist/resolution/index.d.ts +8 -0
  47. package/dist/resolution/index.d.ts.map +1 -0
  48. package/dist/resolution/index.js +8 -0
  49. package/dist/resolution/index.js.map +1 -0
  50. package/dist/resolution/initiative.d.ts +19 -0
  51. package/dist/resolution/initiative.d.ts.map +1 -0
  52. package/dist/resolution/initiative.js +55 -0
  53. package/dist/resolution/initiative.js.map +1 -0
  54. package/dist/resolution/test.d.ts +34 -0
  55. package/dist/resolution/test.d.ts.map +1 -0
  56. package/dist/resolution/test.js +143 -0
  57. package/dist/resolution/test.js.map +1 -0
  58. package/dist/rng/index.d.ts +6 -0
  59. package/dist/rng/index.d.ts.map +1 -0
  60. package/dist/rng/index.js +6 -0
  61. package/dist/rng/index.js.map +1 -0
  62. package/dist/rng/mulberry32.d.ts +13 -0
  63. package/dist/rng/mulberry32.d.ts.map +1 -0
  64. package/dist/rng/mulberry32.js +23 -0
  65. package/dist/rng/mulberry32.js.map +1 -0
  66. package/dist/rng/rng.d.ts +26 -0
  67. package/dist/rng/rng.d.ts.map +1 -0
  68. package/dist/rng/rng.js +51 -0
  69. package/dist/rng/rng.js.map +1 -0
  70. package/dist/rules/context.d.ts +86 -0
  71. package/dist/rules/context.d.ts.map +1 -0
  72. package/dist/rules/context.js +137 -0
  73. package/dist/rules/context.js.map +1 -0
  74. package/dist/rules/custom-test.d.ts +33 -0
  75. package/dist/rules/custom-test.d.ts.map +1 -0
  76. package/dist/rules/custom-test.js +164 -0
  77. package/dist/rules/custom-test.js.map +1 -0
  78. package/dist/rules/index.d.ts +6 -0
  79. package/dist/rules/index.d.ts.map +1 -0
  80. package/dist/rules/index.js +6 -0
  81. package/dist/rules/index.js.map +1 -0
  82. package/package.json +47 -0
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Dice exports
3
+ */
4
+ export { parseDice, isValidDiceExpression } from "./parser.js";
5
+ export { rollDice, rollDie, rollNd } from "./roller.js";
6
+ export { calculateNetAdvantage, rollD20WithAdvantage, rollWithAdvantage, } from "./advantage.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/dice/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Dice exports
3
+ */
4
+ export { parseDice, isValidDiceExpression } from "./parser.js";
5
+ export { rollDice, rollDie, rollNd } from "./roller.js";
6
+ export { calculateNetAdvantage, rollD20WithAdvantage, rollWithAdvantage, } from "./advantage.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/dice/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Dice expression parser
3
+ *
4
+ * Supports formats:
5
+ * - "d20" -> 1d20+0
6
+ * - "2d6" -> 2d6+0
7
+ * - "1d8+3" -> 1d8+3
8
+ * - "d6-1" -> 1d6-1
9
+ * - "d20+STR" -> 1d20+0 with ability: "STR"
10
+ */
11
+ import type { ParsedDice } from "@mythxengine/types";
12
+ /**
13
+ * Parse a dice expression string
14
+ * @throws Error if expression is invalid
15
+ */
16
+ export declare function parseDice(expression: string): ParsedDice;
17
+ /**
18
+ * Check if a string is a valid dice expression
19
+ */
20
+ export declare function isValidDiceExpression(expression: string): boolean;
21
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/dice/parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAe,MAAM,oBAAoB,CAAC;AAMlE;;;GAGG;AACH,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,CAsCxD;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAOjE"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Dice expression parser
3
+ *
4
+ * Supports formats:
5
+ * - "d20" -> 1d20+0
6
+ * - "2d6" -> 2d6+0
7
+ * - "1d8+3" -> 1d8+3
8
+ * - "d6-1" -> 1d6-1
9
+ * - "d20+STR" -> 1d20+0 with ability: "STR"
10
+ */
11
+ const DICE_REGEX = /^(\d*)d(\d+)(?:([+-])(\d+|STR|AGI|WIT|CON))?$/i;
12
+ const ABILITY_NAMES = new Set(["STR", "AGI", "WIT", "CON"]);
13
+ /**
14
+ * Parse a dice expression string
15
+ * @throws Error if expression is invalid
16
+ */
17
+ export function parseDice(expression) {
18
+ const trimmed = expression.trim();
19
+ const match = trimmed.match(DICE_REGEX);
20
+ if (!match) {
21
+ throw new Error(`Invalid dice expression: ${expression}`);
22
+ }
23
+ const [, countStr, sidesStr, sign, modifierStr] = match;
24
+ const count = countStr ? parseInt(countStr, 10) : 1;
25
+ const sides = parseInt(sidesStr, 10);
26
+ if (count < 1 || count > 100) {
27
+ throw new Error(`Invalid dice count: ${count}`);
28
+ }
29
+ if (sides < 1 || sides > 100) {
30
+ throw new Error(`Invalid dice sides: ${sides}`);
31
+ }
32
+ // Parse modifier
33
+ let modifier = 0;
34
+ let ability;
35
+ if (modifierStr) {
36
+ const upperMod = modifierStr.toUpperCase();
37
+ if (ABILITY_NAMES.has(upperMod)) {
38
+ ability = upperMod;
39
+ }
40
+ else {
41
+ modifier = parseInt(modifierStr, 10);
42
+ if (sign === "-") {
43
+ modifier = -modifier;
44
+ }
45
+ }
46
+ }
47
+ return { count, sides, modifier, ability };
48
+ }
49
+ /**
50
+ * Check if a string is a valid dice expression
51
+ */
52
+ export function isValidDiceExpression(expression) {
53
+ try {
54
+ parseDice(expression);
55
+ return true;
56
+ }
57
+ catch {
58
+ return false;
59
+ }
60
+ }
61
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/dice/parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,MAAM,UAAU,GAAG,gDAAgD,CAAC;AAEpE,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;AAE5D;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,UAAkB;IAC1C,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC;IAClC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAExC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,4BAA4B,UAAU,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,MAAM,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,CAAC,GAAG,KAAK,CAAC;IAExD,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACpD,MAAM,KAAK,GAAG,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAErC,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,GAAG,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,uBAAuB,KAAK,EAAE,CAAC,CAAC;IAClD,CAAC;IAED,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,GAAG,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,uBAAuB,KAAK,EAAE,CAAC,CAAC;IAClD,CAAC;IAED,iBAAiB;IACjB,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,OAAgC,CAAC;IAErC,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,QAAQ,GAAG,WAAW,CAAC,WAAW,EAAE,CAAC;QAC3C,IAAI,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YAChC,OAAO,GAAG,QAAuB,CAAC;QACpC,CAAC;aAAM,CAAC;YACN,QAAQ,GAAG,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;YACrC,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;gBACjB,QAAQ,GAAG,CAAC,QAAQ,CAAC;YACvB,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;AAC7C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,UAAkB;IACtD,IAAI,CAAC;QACH,SAAS,CAAC,UAAU,CAAC,CAAC;QACtB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Dice roller
3
+ */
4
+ import type { DiceResult, Abilities } from "@mythxengine/types";
5
+ import type { RNG } from "../rng/rng.js";
6
+ /**
7
+ * Roll a dice expression
8
+ *
9
+ * @param expression - Dice expression (e.g., "2d6+3")
10
+ * @param rng - Random number generator
11
+ * @param abilities - Optional abilities for ability modifiers
12
+ * @returns Dice result with all roll details
13
+ */
14
+ export declare function rollDice(expression: string, rng: RNG, abilities?: Abilities): DiceResult;
15
+ /**
16
+ * Roll a simple die (e.g., d20, d6)
17
+ */
18
+ export declare function rollDie(sides: number, rng: RNG): number;
19
+ /**
20
+ * Roll multiple dice and sum
21
+ */
22
+ export declare function rollNd(count: number, sides: number, rng: RNG): number;
23
+ //# sourceMappingURL=roller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roller.d.ts","sourceRoot":"","sources":["../../src/dice/roller.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAGzC;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CACtB,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,GAAG,EACR,SAAS,CAAC,EAAE,SAAS,GACpB,UAAU,CAoCZ;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,MAAM,CAEvD;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,MAAM,CAMrE"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Dice roller
3
+ */
4
+ import { parseDice } from "./parser.js";
5
+ /**
6
+ * Roll a dice expression
7
+ *
8
+ * @param expression - Dice expression (e.g., "2d6+3")
9
+ * @param rng - Random number generator
10
+ * @param abilities - Optional abilities for ability modifiers
11
+ * @returns Dice result with all roll details
12
+ */
13
+ export function rollDice(expression, rng, abilities) {
14
+ const parsed = parseDice(expression);
15
+ // Roll each die
16
+ const rolls = [];
17
+ for (let i = 0; i < parsed.count; i++) {
18
+ rolls.push(rng.nextInt(1, parsed.sides));
19
+ }
20
+ // Calculate natural total (sum of dice)
21
+ const natural = rolls.reduce((sum, roll) => sum + roll, 0);
22
+ // Calculate modifier (including ability if specified)
23
+ let modifier = parsed.modifier;
24
+ if (parsed.ability && abilities) {
25
+ modifier += abilities[parsed.ability];
26
+ }
27
+ // Calculate total
28
+ const total = natural + modifier;
29
+ // Check for critical (only on d20)
30
+ let critical;
31
+ if (parsed.count === 1 && parsed.sides === 20) {
32
+ if (natural === 20)
33
+ critical = "success";
34
+ if (natural === 1)
35
+ critical = "failure";
36
+ }
37
+ return {
38
+ expression,
39
+ rolls,
40
+ modifier,
41
+ total,
42
+ natural,
43
+ critical,
44
+ };
45
+ }
46
+ /**
47
+ * Roll a simple die (e.g., d20, d6)
48
+ */
49
+ export function rollDie(sides, rng) {
50
+ return rng.nextInt(1, sides);
51
+ }
52
+ /**
53
+ * Roll multiple dice and sum
54
+ */
55
+ export function rollNd(count, sides, rng) {
56
+ let total = 0;
57
+ for (let i = 0; i < count; i++) {
58
+ total += rng.nextInt(1, sides);
59
+ }
60
+ return total;
61
+ }
62
+ //# sourceMappingURL=roller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roller.js","sourceRoot":"","sources":["../../src/dice/roller.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC;;;;;;;GAOG;AACH,MAAM,UAAU,QAAQ,CACtB,UAAkB,EAClB,GAAQ,EACR,SAAqB;IAErB,MAAM,MAAM,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC;IAErC,gBAAgB;IAChB,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAC3C,CAAC;IAED,wCAAwC;IACxC,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC;IAE3D,sDAAsD;IACtD,IAAI,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;IAC/B,IAAI,MAAM,CAAC,OAAO,IAAI,SAAS,EAAE,CAAC;QAChC,QAAQ,IAAI,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACxC,CAAC;IAED,kBAAkB;IAClB,MAAM,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;IAEjC,mCAAmC;IACnC,IAAI,QAA2C,CAAC;IAChD,IAAI,MAAM,CAAC,KAAK,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,KAAK,EAAE,EAAE,CAAC;QAC9C,IAAI,OAAO,KAAK,EAAE;YAAE,QAAQ,GAAG,SAAS,CAAC;QACzC,IAAI,OAAO,KAAK,CAAC;YAAE,QAAQ,GAAG,SAAS,CAAC;IAC1C,CAAC;IAED,OAAO;QACL,UAAU;QACV,KAAK;QACL,QAAQ;QACR,KAAK;QACL,OAAO;QACP,QAAQ;KACT,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,OAAO,CAAC,KAAa,EAAE,GAAQ;IAC7C,OAAO,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AAC/B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,MAAM,CAAC,KAAa,EAAE,KAAa,EAAE,GAAQ;IAC3D,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/B,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACjC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @mythxengine/engine
3
+ *
4
+ * Pure game engine with deterministic mechanics
5
+ */
6
+ export * from "./rng/index.js";
7
+ export * from "./dice/index.js";
8
+ export * from "./resolution/index.js";
9
+ export * from "./rules/index.js";
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,cAAc,gBAAgB,CAAC;AAG/B,cAAc,iBAAiB,CAAC;AAGhC,cAAc,uBAAuB,CAAC;AAGtC,cAAc,kBAAkB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @mythxengine/engine
3
+ *
4
+ * Pure game engine with deterministic mechanics
5
+ */
6
+ // RNG
7
+ export * from "./rng/index.js";
8
+ // Dice
9
+ export * from "./dice/index.js";
10
+ // Resolution
11
+ export * from "./resolution/index.js";
12
+ // Rules
13
+ export * from "./rules/index.js";
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM;AACN,cAAc,gBAAgB,CAAC;AAE/B,OAAO;AACP,cAAc,iBAAiB,CAAC;AAEhC,aAAa;AACb,cAAc,uBAAuB,CAAC;AAEtC,QAAQ;AACR,cAAc,kBAAkB,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Combat resolution
3
+ */
4
+ import type { Character, Enemy, Weapon, Modifier, AttackResult } from "@mythxengine/types";
5
+ import type { RNG } from "../rng/rng.js";
6
+ import type { RulesContext } from "../rules/context.js";
7
+ export interface AttackOptions {
8
+ attacker: Character | Enemy;
9
+ defender: Character | Enemy;
10
+ weapon: Weapon;
11
+ modifiers?: Modifier[];
12
+ rng: RNG;
13
+ /** Explicit sources of advantage (e.g., "flanking", "high ground") */
14
+ advantageSources?: string[];
15
+ /** Explicit sources of disadvantage (e.g., "darkness", "cover") */
16
+ disadvantageSources?: string[];
17
+ /** Damage type for resistance/vulnerability (default: "physical") */
18
+ damageType?: string;
19
+ /** Rules context (uses defaults if not provided) */
20
+ rules?: RulesContext;
21
+ }
22
+ /**
23
+ * Resolve an attack
24
+ *
25
+ * Attack roll: d20 + ability + skill vs 10 + defender AGI
26
+ * On hit: roll damage - armor
27
+ *
28
+ * Supports advantage/disadvantage:
29
+ * - Explicit sources passed via advantageSources/disadvantageSources
30
+ * - Conditions with GRANT_ADVANTAGE/GRANT_DISADVANTAGE effects
31
+ * - Advantage + disadvantage cancel to normal roll
32
+ *
33
+ * Supports resistance/vulnerability:
34
+ * - Defender conditions with RESISTANCE/VULNERABILITY effects
35
+ * - Resistance halves damage, vulnerability doubles it
36
+ * - Resistance + vulnerability cancel out
37
+ */
38
+ export declare function resolveAttack(options: AttackOptions): AttackResult;
39
+ //# sourceMappingURL=combat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"combat.d.ts","sourceRoot":"","sources":["../../src/resolution/combat.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,SAAS,EACT,KAAK,EACL,MAAM,EACN,QAAQ,EACR,YAAY,EAGb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAGzC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAUxD,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,SAAS,GAAG,KAAK,CAAC;IAC5B,QAAQ,EAAE,SAAS,GAAG,KAAK,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;IACvB,GAAG,EAAE,GAAG,CAAC;IACT,sEAAsE;IACtE,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,mEAAmE;IACnE,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oDAAoD;IACpD,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AA2ID;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,YAAY,CA6GlE"}
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Combat resolution
3
+ */
4
+ import { rollWithAdvantage, calculateNetAdvantage } from "../dice/advantage.js";
5
+ import { calculateDamage } from "./damage.js";
6
+ import { getDefaultRulesContext, checkCriticalSuccess, checkCriticalFailure, calculateDefenseTarget, getResistanceMultiplier, getVulnerabilityMultiplier, } from "../rules/context.js";
7
+ /**
8
+ * Check if combatant is a Character (not an Enemy)
9
+ */
10
+ function isCharacter(combatant) {
11
+ return "archetypeId" in combatant;
12
+ }
13
+ /**
14
+ * Get skill bonus for combat
15
+ */
16
+ function getCombatSkillBonus(combatant, skillName) {
17
+ if (!skillName)
18
+ return 0;
19
+ if (!isCharacter(combatant))
20
+ return 0; // Enemies don't have skills
21
+ const skill = combatant.skills.find((s) => s.name.toLowerCase() === skillName.toLowerCase());
22
+ return skill?.bonus ?? 0;
23
+ }
24
+ /**
25
+ * Get condition modifiers for attack
26
+ */
27
+ function getAttackConditionMod(combatant) {
28
+ let total = 0;
29
+ for (const condition of combatant.conditions) {
30
+ for (const effect of condition.effects) {
31
+ if (effect.type === "MODIFY_SKILL" && effect.skillId === "combat") {
32
+ total += effect.amount;
33
+ }
34
+ }
35
+ }
36
+ return total;
37
+ }
38
+ /**
39
+ * Get advantage/disadvantage sources from attacker conditions
40
+ */
41
+ function getAttackAdvantage(attacker) {
42
+ const advantageSources = [];
43
+ const disadvantageSources = [];
44
+ for (const condition of attacker.conditions) {
45
+ for (const effect of condition.effects) {
46
+ if (effect.type === "GRANT_ADVANTAGE") {
47
+ if (effect.scope === "all" || effect.scope === "attacks") {
48
+ advantageSources.push(`condition:${condition.name}`);
49
+ }
50
+ }
51
+ else if (effect.type === "GRANT_DISADVANTAGE") {
52
+ if (effect.scope === "all" || effect.scope === "attacks") {
53
+ disadvantageSources.push(`condition:${condition.name}`);
54
+ }
55
+ }
56
+ }
57
+ }
58
+ return { advantageSources, disadvantageSources };
59
+ }
60
+ /**
61
+ * Calculate damage multiplier from defender's resistance/vulnerability
62
+ *
63
+ * @param defender - The defender
64
+ * @param damageType - The damage type being dealt
65
+ * @param rules - Rules context for default multipliers
66
+ * @returns Object with multiplier and reason
67
+ */
68
+ function getDamageMultiplier(defender, damageType, rules) {
69
+ let hasResistance = false;
70
+ let hasVulnerability = false;
71
+ // Use rules context for default multipliers
72
+ let resistanceMultiplier = getResistanceMultiplier(rules);
73
+ let vulnerabilityMultiplier = getVulnerabilityMultiplier(rules);
74
+ for (const condition of defender.conditions) {
75
+ for (const effect of condition.effects) {
76
+ if (effect.type === "RESISTANCE" &&
77
+ effect.damageType.toLowerCase() === damageType.toLowerCase()) {
78
+ hasResistance = true;
79
+ if (effect.multiplier !== undefined) {
80
+ resistanceMultiplier = effect.multiplier;
81
+ }
82
+ }
83
+ else if (effect.type === "VULNERABILITY" &&
84
+ effect.damageType.toLowerCase() === damageType.toLowerCase()) {
85
+ hasVulnerability = true;
86
+ if (effect.multiplier !== undefined) {
87
+ vulnerabilityMultiplier = effect.multiplier;
88
+ }
89
+ }
90
+ }
91
+ }
92
+ // If both, they cancel out (like advantage/disadvantage)
93
+ if (hasResistance && hasVulnerability) {
94
+ return { multiplier: 1 };
95
+ }
96
+ if (hasResistance) {
97
+ return { multiplier: resistanceMultiplier, reason: "resistance" };
98
+ }
99
+ if (hasVulnerability) {
100
+ return { multiplier: vulnerabilityMultiplier, reason: "vulnerability" };
101
+ }
102
+ return { multiplier: 1 };
103
+ }
104
+ /**
105
+ * Get armor value for a defender
106
+ */
107
+ function getArmorValue(defender) {
108
+ if ("armor" in defender && typeof defender.armor === "number") {
109
+ return defender.armor;
110
+ }
111
+ // Parse armor from equipment description if character
112
+ const armorStr = defender.equipment?.armor;
113
+ if (!armorStr)
114
+ return 0;
115
+ // Try to extract number from description like "Light armor (+1 defense)"
116
+ const match = armorStr.match(/\+(\d+)/);
117
+ return match ? parseInt(match[1], 10) : 0;
118
+ }
119
+ /**
120
+ * Resolve an attack
121
+ *
122
+ * Attack roll: d20 + ability + skill vs 10 + defender AGI
123
+ * On hit: roll damage - armor
124
+ *
125
+ * Supports advantage/disadvantage:
126
+ * - Explicit sources passed via advantageSources/disadvantageSources
127
+ * - Conditions with GRANT_ADVANTAGE/GRANT_DISADVANTAGE effects
128
+ * - Advantage + disadvantage cancel to normal roll
129
+ *
130
+ * Supports resistance/vulnerability:
131
+ * - Defender conditions with RESISTANCE/VULNERABILITY effects
132
+ * - Resistance halves damage, vulnerability doubles it
133
+ * - Resistance + vulnerability cancel out
134
+ */
135
+ export function resolveAttack(options) {
136
+ const { attacker, defender, weapon, modifiers = [], rng, advantageSources = [], disadvantageSources = [], damageType = "physical", rules = getDefaultRulesContext(), } = options;
137
+ // Determine attack ability
138
+ const attackAbility = weapon.ability ?? "STR";
139
+ const attackMod = attacker.abilities[attackAbility];
140
+ // Get combat skill bonus
141
+ const skillBonus = getCombatSkillBonus(attacker, weapon.skill);
142
+ // Condition modifiers
143
+ const conditionMods = getAttackConditionMod(attacker);
144
+ // Other modifiers
145
+ const otherMods = modifiers.reduce((sum, m) => sum + m.amount, 0);
146
+ // Total attack bonus
147
+ const totalAttackMod = attackMod + skillBonus + conditionMods + otherMods;
148
+ // Defense target from rules context (default: 10 + defender AGI)
149
+ const defenseTarget = calculateDefenseTarget(rules, defender.abilities);
150
+ // Get advantage/disadvantage from attacker conditions
151
+ const conditionAdvantage = getAttackAdvantage(attacker);
152
+ // Combine explicit and condition-based advantage sources
153
+ const allAdvantageSources = [
154
+ ...advantageSources,
155
+ ...conditionAdvantage.advantageSources,
156
+ ];
157
+ const allDisadvantageSources = [
158
+ ...disadvantageSources,
159
+ ...conditionAdvantage.disadvantageSources,
160
+ ];
161
+ // Calculate net advantage state
162
+ const advantageState = calculateNetAdvantage(allAdvantageSources, allDisadvantageSources);
163
+ // Roll attack with advantage/disadvantage
164
+ const attackRoll = rollWithAdvantage("d20", rng, advantageState);
165
+ const attackTotal = attackRoll.natural + totalAttackMod;
166
+ // Check for critical using rules context
167
+ const criticalHit = checkCriticalSuccess(rules, attackRoll.natural);
168
+ const criticalMiss = checkCriticalFailure(rules, attackRoll.natural);
169
+ // Determine hit (respects autoSuccess/autoFailure from rules)
170
+ const { criticals } = rules.rules.mechanics;
171
+ const autoHit = criticalHit && criticals.autoSuccess;
172
+ const autoMiss = criticalMiss && criticals.autoFailure;
173
+ const hit = autoHit || (!autoMiss && attackTotal >= defenseTarget);
174
+ if (!hit) {
175
+ return {
176
+ hit: false,
177
+ roll: { ...attackRoll, total: attackTotal },
178
+ critical: criticalMiss ? "miss" : undefined,
179
+ advantageState,
180
+ };
181
+ }
182
+ // Calculate base damage using rules context for damage config
183
+ const armor = getArmorValue(defender);
184
+ const damageResult = calculateDamage(weapon, attackMod, armor, rng, rules);
185
+ // Apply critical damage multiplier from rules context
186
+ const critMultiplier = criticalHit ? criticals.damageMultiplier : 1;
187
+ let finalDamage = Math.floor(damageResult.damage * critMultiplier);
188
+ // Apply resistance/vulnerability using rules context for default multipliers
189
+ const damageMultiplierInfo = getDamageMultiplier(defender, damageType, rules);
190
+ const originalDamage = finalDamage;
191
+ finalDamage = Math.floor(finalDamage * damageMultiplierInfo.multiplier);
192
+ // Build damage modification info if applicable
193
+ let damageModification;
194
+ if (damageMultiplierInfo.reason) {
195
+ damageModification = {
196
+ originalDamage,
197
+ finalDamage,
198
+ reason: damageMultiplierInfo.reason,
199
+ damageType,
200
+ };
201
+ }
202
+ // Calculate remaining HP
203
+ const defenderHpRemaining = defender.hp.current - finalDamage;
204
+ return {
205
+ hit: true,
206
+ roll: { ...attackRoll, total: attackTotal },
207
+ damage: finalDamage,
208
+ critical: criticalHit ? "hit" : undefined,
209
+ defenderHpRemaining,
210
+ advantageState,
211
+ damageModification,
212
+ };
213
+ }
214
+ //# sourceMappingURL=combat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"combat.js","sourceRoot":"","sources":["../../src/resolution/combat.ts"],"names":[],"mappings":"AAAA;;GAEG;AAYH,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAChF,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,oBAAoB,EACpB,sBAAsB,EACtB,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAC;AAkB7B;;GAEG;AACH,SAAS,WAAW,CAAC,SAA4B;IAC/C,OAAO,aAAa,IAAI,SAAS,CAAC;AACpC,CAAC;AAED;;GAEG;AACH,SAAS,mBAAmB,CAC1B,SAA4B,EAC5B,SAAkB;IAElB,IAAI,CAAC,SAAS;QAAE,OAAO,CAAC,CAAC;IACzB,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC;QAAE,OAAO,CAAC,CAAC,CAAC,4BAA4B;IAEnE,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,CAAC,IAAI,CACjC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,SAAS,CAAC,WAAW,EAAE,CACxD,CAAC;IACF,OAAO,KAAK,EAAE,KAAK,IAAI,CAAC,CAAC;AAC3B,CAAC;AAED;;GAEG;AACH,SAAS,qBAAqB,CAAC,SAA4B;IACzD,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,SAAS,IAAI,SAAS,CAAC,UAAU,EAAE,CAAC;QAC7C,KAAK,MAAM,MAAM,IAAI,SAAS,CAAC,OAAO,EAAE,CAAC;YACvC,IAAI,MAAM,CAAC,IAAI,KAAK,cAAc,IAAI,MAAM,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;gBAClE,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC;YACzB,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,SAAS,kBAAkB,CACzB,QAA2B;IAE3B,MAAM,gBAAgB,GAAa,EAAE,CAAC;IACtC,MAAM,mBAAmB,GAAa,EAAE,CAAC;IAEzC,KAAK,MAAM,SAAS,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC;QAC5C,KAAK,MAAM,MAAM,IAAI,SAAS,CAAC,OAAmB,EAAE,CAAC;YACnD,IAAI,MAAM,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;gBACtC,IAAI,MAAM,CAAC,KAAK,KAAK,KAAK,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;oBACzD,gBAAgB,CAAC,IAAI,CAAC,aAAa,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC;gBACvD,CAAC;YACH,CAAC;iBAAM,IAAI,MAAM,CAAC,IAAI,KAAK,oBAAoB,EAAE,CAAC;gBAChD,IAAI,MAAM,CAAC,KAAK,KAAK,KAAK,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;oBACzD,mBAAmB,CAAC,IAAI,CAAC,aAAa,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC;gBAC1D,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,CAAC;AACnD,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,mBAAmB,CAC1B,QAA2B,EAC3B,UAAkB,EAClB,KAAmB;IAEnB,IAAI,aAAa,GAAG,KAAK,CAAC;IAC1B,IAAI,gBAAgB,GAAG,KAAK,CAAC;IAC7B,4CAA4C;IAC5C,IAAI,oBAAoB,GAAG,uBAAuB,CAAC,KAAK,CAAC,CAAC;IAC1D,IAAI,uBAAuB,GAAG,0BAA0B,CAAC,KAAK,CAAC,CAAC;IAEhE,KAAK,MAAM,SAAS,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC;QAC5C,KAAK,MAAM,MAAM,IAAI,SAAS,CAAC,OAAmB,EAAE,CAAC;YACnD,IACE,MAAM,CAAC,IAAI,KAAK,YAAY;gBAC5B,MAAM,CAAC,UAAU,CAAC,WAAW,EAAE,KAAK,UAAU,CAAC,WAAW,EAAE,EAC5D,CAAC;gBACD,aAAa,GAAG,IAAI,CAAC;gBACrB,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;oBACpC,oBAAoB,GAAG,MAAM,CAAC,UAAU,CAAC;gBAC3C,CAAC;YACH,CAAC;iBAAM,IACL,MAAM,CAAC,IAAI,KAAK,eAAe;gBAC/B,MAAM,CAAC,UAAU,CAAC,WAAW,EAAE,KAAK,UAAU,CAAC,WAAW,EAAE,EAC5D,CAAC;gBACD,gBAAgB,GAAG,IAAI,CAAC;gBACxB,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;oBACpC,uBAAuB,GAAG,MAAM,CAAC,UAAU,CAAC;gBAC9C,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,yDAAyD;IACzD,IAAI,aAAa,IAAI,gBAAgB,EAAE,CAAC;QACtC,OAAO,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC;IAC3B,CAAC;IAED,IAAI,aAAa,EAAE,CAAC;QAClB,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;IACpE,CAAC;IAED,IAAI,gBAAgB,EAAE,CAAC;QACrB,OAAO,EAAE,UAAU,EAAE,uBAAuB,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1E,CAAC;IAED,OAAO,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC;AAC3B,CAAC;AAED;;GAEG;AACH,SAAS,aAAa,CAAC,QAA2B;IAChD,IAAI,OAAO,IAAI,QAAQ,IAAI,OAAO,QAAQ,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9D,OAAO,QAAQ,CAAC,KAAK,CAAC;IACxB,CAAC;IACD,sDAAsD;IACtD,MAAM,QAAQ,GAAI,QAAsB,CAAC,SAAS,EAAE,KAAK,CAAC;IAC1D,IAAI,CAAC,QAAQ;QAAE,OAAO,CAAC,CAAC;IAExB,yEAAyE;IACzE,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACxC,OAAO,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,aAAa,CAAC,OAAsB;IAClD,MAAM,EACJ,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,SAAS,GAAG,EAAE,EACd,GAAG,EACH,gBAAgB,GAAG,EAAE,EACrB,mBAAmB,GAAG,EAAE,EACxB,UAAU,GAAG,UAAU,EACvB,KAAK,GAAG,sBAAsB,EAAE,GACjC,GAAG,OAAO,CAAC;IAEZ,2BAA2B;IAC3B,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,IAAI,KAAK,CAAC;IAC9C,MAAM,SAAS,GAAG,QAAQ,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;IAEpD,yBAAyB;IACzB,MAAM,UAAU,GAAG,mBAAmB,CAAC,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IAE/D,sBAAsB;IACtB,MAAM,aAAa,GAAG,qBAAqB,CAAC,QAAQ,CAAC,CAAC;IAEtD,kBAAkB;IAClB,MAAM,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAElE,qBAAqB;IACrB,MAAM,cAAc,GAAG,SAAS,GAAG,UAAU,GAAG,aAAa,GAAG,SAAS,CAAC;IAE1E,iEAAiE;IACjE,MAAM,aAAa,GAAG,sBAAsB,CAAC,KAAK,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;IAExE,sDAAsD;IACtD,MAAM,kBAAkB,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAExD,yDAAyD;IACzD,MAAM,mBAAmB,GAAG;QAC1B,GAAG,gBAAgB;QACnB,GAAG,kBAAkB,CAAC,gBAAgB;KACvC,CAAC;IACF,MAAM,sBAAsB,GAAG;QAC7B,GAAG,mBAAmB;QACtB,GAAG,kBAAkB,CAAC,mBAAmB;KAC1C,CAAC;IAEF,gCAAgC;IAChC,MAAM,cAAc,GAAG,qBAAqB,CAC1C,mBAAmB,EACnB,sBAAsB,CACvB,CAAC;IAEF,0CAA0C;IAC1C,MAAM,UAAU,GAAG,iBAAiB,CAAC,KAAK,EAAE,GAAG,EAAE,cAAc,CAAC,CAAC;IACjE,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,GAAG,cAAc,CAAC;IAExD,yCAAyC;IACzC,MAAM,WAAW,GAAG,oBAAoB,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IACpE,MAAM,YAAY,GAAG,oBAAoB,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IAErE,8DAA8D;IAC9D,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC;IAC5C,MAAM,OAAO,GAAG,WAAW,IAAI,SAAS,CAAC,WAAW,CAAC;IACrD,MAAM,QAAQ,GAAG,YAAY,IAAI,SAAS,CAAC,WAAW,CAAC;IACvD,MAAM,GAAG,GAAG,OAAO,IAAI,CAAC,CAAC,QAAQ,IAAI,WAAW,IAAI,aAAa,CAAC,CAAC;IAEnE,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO;YACL,GAAG,EAAE,KAAK;YACV,IAAI,EAAE,EAAE,GAAG,UAAU,EAAE,KAAK,EAAE,WAAW,EAAE;YAC3C,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;YAC3C,cAAc;SACf,CAAC;IACJ,CAAC;IAED,8DAA8D;IAC9D,MAAM,KAAK,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IACtC,MAAM,YAAY,GAAG,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;IAC3E,sDAAsD;IACtD,MAAM,cAAc,GAAG,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;IACpE,IAAI,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,GAAG,cAAc,CAAC,CAAC;IAEnE,6EAA6E;IAC7E,MAAM,oBAAoB,GAAG,mBAAmB,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,CAAC,CAAC;IAC9E,MAAM,cAAc,GAAG,WAAW,CAAC;IACnC,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,oBAAoB,CAAC,UAAU,CAAC,CAAC;IAExE,+CAA+C;IAC/C,IAAI,kBAAkD,CAAC;IACvD,IAAI,oBAAoB,CAAC,MAAM,EAAE,CAAC;QAChC,kBAAkB,GAAG;YACnB,cAAc;YACd,WAAW;YACX,MAAM,EAAE,oBAAoB,CAAC,MAAM;YACnC,UAAU;SACX,CAAC;IACJ,CAAC;IAED,yBAAyB;IACzB,MAAM,mBAAmB,GAAG,QAAQ,CAAC,EAAE,CAAC,OAAO,GAAG,WAAW,CAAC;IAE9D,OAAO;QACL,GAAG,EAAE,IAAI;QACT,IAAI,EAAE,EAAE,GAAG,UAAU,EAAE,KAAK,EAAE,WAAW,EAAE;QAC3C,MAAM,EAAE,WAAW;QACnB,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;QACzC,mBAAmB;QACnB,cAAc;QACd,kBAAkB;KACnB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Damage calculation
3
+ */
4
+ import type { Weapon, DiceResult } from "@mythxengine/types";
5
+ import type { RNG } from "../rng/rng.js";
6
+ import type { RulesContext } from "../rules/context.js";
7
+ export interface DamageResult {
8
+ damage: number;
9
+ roll: DiceResult;
10
+ rawDamage: number;
11
+ armorReduction: number;
12
+ }
13
+ /**
14
+ * Calculate damage from an attack
15
+ *
16
+ * Default: Damage = weapon die + ability mod - armor (minimum 0)
17
+ * Respects rules context for addAbility, subtractArmor, minimumDamage
18
+ */
19
+ export declare function calculateDamage(weapon: Weapon, abilityMod: number, armor: number, rng: RNG, rules?: RulesContext): DamageResult;
20
+ /**
21
+ * Calculate healing amount
22
+ */
23
+ export declare function calculateHealing(expression: string, rng: RNG, maxHp: number, currentHp: number): {
24
+ healing: number;
25
+ roll: DiceResult;
26
+ };
27
+ //# sourceMappingURL=damage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"damage.d.ts","sourceRoot":"","sources":["../../src/resolution/damage.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAEzC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGxD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,UAAU,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,GAAG,EACR,KAAK,GAAE,YAAuC,GAC7C,YAAY,CA2Bd;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAChB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAMvC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Damage calculation
3
+ */
4
+ import { rollDice } from "../dice/roller.js";
5
+ import { getDefaultRulesContext, getDamageConfig } from "../rules/context.js";
6
+ /**
7
+ * Calculate damage from an attack
8
+ *
9
+ * Default: Damage = weapon die + ability mod - armor (minimum 0)
10
+ * Respects rules context for addAbility, subtractArmor, minimumDamage
11
+ */
12
+ export function calculateDamage(weapon, abilityMod, armor, rng, rules = getDefaultRulesContext()) {
13
+ const damageConfig = getDamageConfig(rules);
14
+ // Roll weapon damage
15
+ const roll = rollDice(weapon.damage, rng);
16
+ // Raw damage = roll + ability mod (if configured)
17
+ const abilityBonus = damageConfig.addAbility ? abilityMod : 0;
18
+ const rawDamage = roll.total + abilityBonus;
19
+ // Apply armor reduction (if configured)
20
+ let damage = rawDamage;
21
+ let armorReduction = 0;
22
+ if (damageConfig.subtractArmor) {
23
+ armorReduction = Math.min(armor, rawDamage);
24
+ damage = rawDamage - armorReduction;
25
+ }
26
+ // Apply minimum damage
27
+ damage = Math.max(damageConfig.minimumDamage, damage);
28
+ return {
29
+ damage,
30
+ roll,
31
+ rawDamage,
32
+ armorReduction,
33
+ };
34
+ }
35
+ /**
36
+ * Calculate healing amount
37
+ */
38
+ export function calculateHealing(expression, rng, maxHp, currentHp) {
39
+ const roll = rollDice(expression, rng);
40
+ const potential = roll.total;
41
+ const healing = Math.min(potential, maxHp - currentHp);
42
+ return { healing, roll };
43
+ }
44
+ //# sourceMappingURL=damage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"damage.js","sourceRoot":"","sources":["../../src/resolution/damage.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE7C,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAS9E;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAC7B,MAAc,EACd,UAAkB,EAClB,KAAa,EACb,GAAQ,EACR,QAAsB,sBAAsB,EAAE;IAE9C,MAAM,YAAY,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;IAE5C,qBAAqB;IACrB,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAE1C,kDAAkD;IAClD,MAAM,YAAY,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9D,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,GAAG,YAAY,CAAC;IAE5C,wCAAwC;IACxC,IAAI,MAAM,GAAG,SAAS,CAAC;IACvB,IAAI,cAAc,GAAG,CAAC,CAAC;IACvB,IAAI,YAAY,CAAC,aAAa,EAAE,CAAC;QAC/B,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;QAC5C,MAAM,GAAG,SAAS,GAAG,cAAc,CAAC;IACtC,CAAC;IAED,uBAAuB;IACvB,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;IAEtD,OAAO;QACL,MAAM;QACN,IAAI;QACJ,SAAS;QACT,cAAc;KACf,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAC9B,UAAkB,EAClB,GAAQ,EACR,KAAa,EACb,SAAiB;IAEjB,MAAM,IAAI,GAAG,QAAQ,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IACvC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC;IAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,GAAG,SAAS,CAAC,CAAC;IAEvD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Resolution exports
3
+ */
4
+ export { resolveTest, type TestOptions } from "./test.js";
5
+ export { resolveAttack, type AttackOptions } from "./combat.js";
6
+ export { calculateDamage, calculateHealing, type DamageResult } from "./damage.js";
7
+ export { rollInitiative, rollInitiativeDetailed } from "./initiative.js";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/resolution/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AACnF,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Resolution exports
3
+ */
4
+ export { resolveTest } from "./test.js";
5
+ export { resolveAttack } from "./combat.js";
6
+ export { calculateDamage, calculateHealing } from "./damage.js";
7
+ export { rollInitiative, rollInitiativeDetailed } from "./initiative.js";
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/resolution/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAoB,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAsB,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAqB,MAAM,aAAa,CAAC;AACnF,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Initiative resolution
3
+ */
4
+ import type { Character, Enemy, InitiativeResult } from "@mythxengine/types";
5
+ import type { RNG } from "../rng/rng.js";
6
+ type Combatant = Character | Enemy;
7
+ /**
8
+ * Roll initiative for all combatants
9
+ *
10
+ * Initiative = d20 + AGI
11
+ * Returns array of IDs in order (highest first)
12
+ */
13
+ export declare function rollInitiative(combatants: readonly Combatant[], rng: RNG): string[];
14
+ /**
15
+ * Get initiative results with full details
16
+ */
17
+ export declare function rollInitiativeDetailed(combatants: readonly Combatant[], rng: RNG): InitiativeResult[];
18
+ export {};
19
+ //# sourceMappingURL=initiative.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"initiative.d.ts","sourceRoot":"","sources":["../../src/resolution/initiative.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC7E,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAGzC,KAAK,SAAS,GAAG,SAAS,GAAG,KAAK,CAAC;AAEnC;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,UAAU,EAAE,SAAS,SAAS,EAAE,EAChC,GAAG,EAAE,GAAG,GACP,MAAM,EAAE,CA6BV;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,SAAS,SAAS,EAAE,EAChC,GAAG,EAAE,GAAG,GACP,gBAAgB,EAAE,CAmBpB"}