@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,55 @@
1
+ /**
2
+ * Initiative resolution
3
+ */
4
+ import { rollDice } from "../dice/roller.js";
5
+ /**
6
+ * Roll initiative for all combatants
7
+ *
8
+ * Initiative = d20 + AGI
9
+ * Returns array of IDs in order (highest first)
10
+ */
11
+ export function rollInitiative(combatants, rng) {
12
+ const results = [];
13
+ for (const combatant of combatants) {
14
+ const roll = rollDice("d20", rng);
15
+ const agiMod = combatant.abilities.AGI;
16
+ const total = roll.total + agiMod;
17
+ results.push({
18
+ characterId: combatant.id,
19
+ roll,
20
+ total,
21
+ });
22
+ }
23
+ // Sort by total (descending), then by AGI (descending) for ties
24
+ results.sort((a, b) => {
25
+ if (b.total !== a.total)
26
+ return b.total - a.total;
27
+ // Find combatants for tie-breaking by AGI
28
+ const combatantA = combatants.find((c) => c.id === a.characterId);
29
+ const combatantB = combatants.find((c) => c.id === b.characterId);
30
+ const agiA = combatantA?.abilities.AGI ?? 0;
31
+ const agiB = combatantB?.abilities.AGI ?? 0;
32
+ return agiB - agiA;
33
+ });
34
+ return results.map((r) => r.characterId);
35
+ }
36
+ /**
37
+ * Get initiative results with full details
38
+ */
39
+ export function rollInitiativeDetailed(combatants, rng) {
40
+ const results = [];
41
+ for (const combatant of combatants) {
42
+ const roll = rollDice("d20", rng);
43
+ const agiMod = combatant.abilities.AGI;
44
+ const total = roll.total + agiMod;
45
+ results.push({
46
+ characterId: combatant.id,
47
+ roll,
48
+ total,
49
+ });
50
+ }
51
+ // Sort by total (descending)
52
+ results.sort((a, b) => b.total - a.total);
53
+ return results;
54
+ }
55
+ //# sourceMappingURL=initiative.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"initiative.js","sourceRoot":"","sources":["../../src/resolution/initiative.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAI7C;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAC5B,UAAgC,EAChC,GAAQ;IAER,MAAM,OAAO,GAAuB,EAAE,CAAC;IAEvC,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAClC,MAAM,MAAM,GAAG,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC;QACvC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC;QAElC,OAAO,CAAC,IAAI,CAAC;YACX,WAAW,EAAE,SAAS,CAAC,EAAE;YACzB,IAAI;YACJ,KAAK;SACN,CAAC,CAAC;IACL,CAAC;IAED,gEAAgE;IAChE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACpB,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK;YAAE,OAAO,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;QAElD,0CAA0C;QAC1C,MAAM,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,WAAW,CAAC,CAAC;QAClE,MAAM,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,WAAW,CAAC,CAAC;QAClE,MAAM,IAAI,GAAG,UAAU,EAAE,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC;QAC5C,MAAM,IAAI,GAAG,UAAU,EAAE,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC;QAE5C,OAAO,IAAI,GAAG,IAAI,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;AAC3C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB,CACpC,UAAgC,EAChC,GAAQ;IAER,MAAM,OAAO,GAAuB,EAAE,CAAC;IAEvC,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAClC,MAAM,MAAM,GAAG,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC;QACvC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC;QAElC,OAAO,CAAC,IAAI,CAAC;YACX,WAAW,EAAE,SAAS,CAAC,EAAE;YACzB,IAAI;YACJ,KAAK;SACN,CAAC,CAAC;IACL,CAAC;IAED,6BAA6B;IAC7B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;IAE1C,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Skill/ability test resolution
3
+ */
4
+ import type { Character, AbilityName, Modifier, TestResult } from "@mythxengine/types";
5
+ import type { RNG } from "../rng/rng.js";
6
+ import type { RulesContext } from "../rules/context.js";
7
+ export interface TestOptions {
8
+ character: Character;
9
+ skill?: string;
10
+ ability?: AbilityName;
11
+ difficulty: number;
12
+ modifiers?: Modifier[];
13
+ rng: RNG;
14
+ /** Explicit sources of advantage (e.g., "flanking", "high ground") */
15
+ advantageSources?: string[];
16
+ /** Explicit sources of disadvantage (e.g., "darkness", "wounded") */
17
+ disadvantageSources?: string[];
18
+ /** Rules context (uses defaults if not provided) */
19
+ rules?: RulesContext;
20
+ }
21
+ /**
22
+ * Resolve a skill or ability test
23
+ *
24
+ * Roll d20 + ability + skill + modifiers vs difficulty
25
+ *
26
+ * Critical success/failure based on rules context (default: nat 20/1)
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
+ export declare function resolveTest(options: TestOptions): TestResult;
34
+ //# sourceMappingURL=test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../../src/resolution/test.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,SAAS,EACT,WAAW,EACX,QAAQ,EACR,UAAU,EAEX,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAEzC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAQxD,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;IACvB,GAAG,EAAE,GAAG,CAAC;IACT,sEAAsE;IACtE,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,qEAAqE;IACrE,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,oDAAoD;IACpD,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AA6FD;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,UAAU,CAiF5D"}
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Skill/ability test resolution
3
+ */
4
+ import { rollWithAdvantage, calculateNetAdvantage } from "../dice/advantage.js";
5
+ import { getDefaultRulesContext, checkCriticalSuccess, checkCriticalFailure, getTestDice, } from "../rules/context.js";
6
+ /**
7
+ * Get the ability associated with a skill
8
+ */
9
+ function getSkillAbility(character, skillName) {
10
+ const skill = character.skills.find((s) => s.name.toLowerCase() === skillName.toLowerCase());
11
+ return skill?.ability ?? "WIT";
12
+ }
13
+ /**
14
+ * Get the bonus from a skill
15
+ */
16
+ function getSkillBonus(character, skillName) {
17
+ const skill = character.skills.find((s) => s.name.toLowerCase() === skillName.toLowerCase());
18
+ return skill?.bonus ?? 0;
19
+ }
20
+ /**
21
+ * Get total condition modifiers for a character
22
+ */
23
+ function getConditionMod(character, ability) {
24
+ let total = 0;
25
+ for (const condition of character.conditions) {
26
+ for (const effect of condition.effects) {
27
+ if (effect.type === "MODIFY_ABILITY") {
28
+ if (!ability || effect.ability === ability) {
29
+ total += effect.amount;
30
+ }
31
+ }
32
+ }
33
+ }
34
+ return total;
35
+ }
36
+ /**
37
+ * Get advantage/disadvantage sources from character conditions
38
+ *
39
+ * @param character - The character to check
40
+ * @param scope - The scope to check ("skill_tests" or "all")
41
+ * @param skillName - Optional specific skill being tested
42
+ */
43
+ function getConditionAdvantage(character, scope, skillName) {
44
+ const advantageSources = [];
45
+ const disadvantageSources = [];
46
+ for (const condition of character.conditions) {
47
+ for (const effect of condition.effects) {
48
+ if (effect.type === "GRANT_ADVANTAGE") {
49
+ // Check if scope matches
50
+ if (effect.scope === "all" || effect.scope === scope) {
51
+ // Check if skill matches (if skills are specified)
52
+ if (!effect.skills ||
53
+ effect.skills.length === 0 ||
54
+ (skillName &&
55
+ effect.skills.some((s) => s.toLowerCase() === skillName.toLowerCase()))) {
56
+ advantageSources.push(`condition:${condition.name}`);
57
+ }
58
+ }
59
+ }
60
+ else if (effect.type === "GRANT_DISADVANTAGE") {
61
+ if (effect.scope === "all" || effect.scope === scope) {
62
+ if (!effect.skills ||
63
+ effect.skills.length === 0 ||
64
+ (skillName &&
65
+ effect.skills.some((s) => s.toLowerCase() === skillName.toLowerCase()))) {
66
+ disadvantageSources.push(`condition:${condition.name}`);
67
+ }
68
+ }
69
+ }
70
+ }
71
+ }
72
+ return { advantageSources, disadvantageSources };
73
+ }
74
+ /**
75
+ * Resolve a skill or ability test
76
+ *
77
+ * Roll d20 + ability + skill + modifiers vs difficulty
78
+ *
79
+ * Critical success/failure based on rules context (default: nat 20/1)
80
+ *
81
+ * Supports advantage/disadvantage:
82
+ * - Explicit sources passed via advantageSources/disadvantageSources
83
+ * - Conditions with GRANT_ADVANTAGE/GRANT_DISADVANTAGE effects
84
+ * - Advantage + disadvantage cancel to normal roll
85
+ */
86
+ export function resolveTest(options) {
87
+ const { character, skill, ability, difficulty, modifiers = [], rng, advantageSources = [], disadvantageSources = [], rules = getDefaultRulesContext(), } = options;
88
+ // Determine which ability to use
89
+ const abilityName = ability ?? (skill ? getSkillAbility(character, skill) : "WIT");
90
+ const abilityMod = character.abilities[abilityName];
91
+ // Get skill bonus if applicable
92
+ const skillBonus = skill ? getSkillBonus(character, skill) : 0;
93
+ // Sum other modifiers
94
+ const otherMods = modifiers.reduce((sum, m) => sum + m.amount, 0);
95
+ // Condition modifiers
96
+ const conditionMods = getConditionMod(character, abilityName);
97
+ // Total modifier
98
+ const totalMod = abilityMod + skillBonus + otherMods + conditionMods;
99
+ // Get advantage/disadvantage from conditions
100
+ const conditionAdvantage = getConditionAdvantage(character, "skill_tests", skill);
101
+ // Combine explicit and condition-based advantage sources
102
+ const allAdvantageSources = [
103
+ ...advantageSources,
104
+ ...conditionAdvantage.advantageSources,
105
+ ];
106
+ const allDisadvantageSources = [
107
+ ...disadvantageSources,
108
+ ...conditionAdvantage.disadvantageSources,
109
+ ];
110
+ // Calculate net advantage state
111
+ const advantageState = calculateNetAdvantage(allAdvantageSources, allDisadvantageSources);
112
+ // Roll with advantage/disadvantage (dice type from rules)
113
+ const dice = getTestDice(rules);
114
+ const roll = rollWithAdvantage(dice, rng, advantageState);
115
+ const total = roll.natural + totalMod;
116
+ // Check for criticals using rules context
117
+ const isCritSuccess = checkCriticalSuccess(rules, roll.natural);
118
+ const isCritFail = checkCriticalFailure(rules, roll.natural);
119
+ // Determine success using rules context
120
+ const { criticals } = rules.rules.mechanics;
121
+ const autoSucceeds = isCritSuccess && criticals.autoSuccess;
122
+ const autoFails = isCritFail && criticals.autoFailure;
123
+ const success = autoSucceeds || (!autoFails && total >= difficulty);
124
+ return {
125
+ skill: skill ?? "",
126
+ ability: abilityName,
127
+ abilityMod,
128
+ skillBonus,
129
+ otherMods: otherMods + conditionMods,
130
+ totalMod,
131
+ difficulty,
132
+ roll: {
133
+ ...roll,
134
+ total,
135
+ critical: isCritSuccess ? "success" : isCritFail ? "failure" : undefined,
136
+ },
137
+ success,
138
+ margin: total - difficulty,
139
+ critical: isCritSuccess || isCritFail,
140
+ advantageState,
141
+ };
142
+ }
143
+ //# sourceMappingURL=test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test.js","sourceRoot":"","sources":["../../src/resolution/test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * RNG exports
3
+ */
4
+ export { mulberry32 } from "./mulberry32.js";
5
+ export { createRNG, type RNG } from "./rng.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rng/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,KAAK,GAAG,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * RNG exports
3
+ */
4
+ export { mulberry32 } from "./mulberry32.js";
5
+ export { createRNG } from "./rng.js";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rng/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAY,MAAM,UAAU,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Mulberry32 PRNG algorithm
3
+ *
4
+ * A simple and fast 32-bit PRNG with good statistical properties.
5
+ * Produces deterministic sequences from a seed.
6
+ */
7
+ /**
8
+ * Create a Mulberry32 generator function
9
+ * @param seed - Initial seed value
10
+ * @returns Generator function that returns floats in [0, 1)
11
+ */
12
+ export declare function mulberry32(seed: number): () => number;
13
+ //# sourceMappingURL=mulberry32.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mulberry32.d.ts","sourceRoot":"","sources":["../../src/rng/mulberry32.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,MAAM,CAWrD"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Mulberry32 PRNG algorithm
3
+ *
4
+ * A simple and fast 32-bit PRNG with good statistical properties.
5
+ * Produces deterministic sequences from a seed.
6
+ */
7
+ /**
8
+ * Create a Mulberry32 generator function
9
+ * @param seed - Initial seed value
10
+ * @returns Generator function that returns floats in [0, 1)
11
+ */
12
+ export function mulberry32(seed) {
13
+ // Use a mutable closure for the state
14
+ let state = seed;
15
+ return function next() {
16
+ state += 0x6d2b79f5;
17
+ let t = state;
18
+ t = Math.imul(t ^ (t >>> 15), t | 1);
19
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
20
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
21
+ };
22
+ }
23
+ //# sourceMappingURL=mulberry32.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mulberry32.js","sourceRoot":"","sources":["../../src/rng/mulberry32.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,sCAAsC;IACtC,IAAI,KAAK,GAAG,IAAI,CAAC;IAEjB,OAAO,SAAS,IAAI;QAClB,KAAK,IAAI,UAAU,CAAC;QACpB,IAAI,CAAC,GAAG,KAAK,CAAC;QACd,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QACrC,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;QAC1C,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,UAAU,CAAC;IAC/C,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * High-level RNG interface
3
+ */
4
+ import type { RNGState } from "@mythxengine/types";
5
+ /**
6
+ * Random number generator interface
7
+ */
8
+ export interface RNG {
9
+ /** Get next float [0, 1) */
10
+ next(): number;
11
+ /** Get integer in range [min, max] inclusive */
12
+ nextInt(min: number, max: number): number;
13
+ /** Get boolean with probability */
14
+ nextBool(probability?: number): boolean;
15
+ /** Pick random element from array */
16
+ pick<T>(array: readonly T[]): T;
17
+ /** Shuffle array (returns new array) */
18
+ shuffle<T>(array: readonly T[]): T[];
19
+ /** Get current state for persistence */
20
+ getState(): RNGState;
21
+ }
22
+ /**
23
+ * Create an RNG instance from seed or state
24
+ */
25
+ export declare function createRNG(seedOrState: number | RNGState): RNG;
26
+ //# sourceMappingURL=rng.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rng.d.ts","sourceRoot":"","sources":["../../src/rng/rng.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAGnD;;GAEG;AACH,MAAM,WAAW,GAAG;IAClB,4BAA4B;IAC5B,IAAI,IAAI,MAAM,CAAC;IAEf,gDAAgD;IAChD,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IAE1C,mCAAmC;IACnC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAExC,qCAAqC;IACrC,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC,CAAC;IAEhC,wCAAwC;IACxC,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;IAErC,wCAAwC;IACxC,QAAQ,IAAI,QAAQ,CAAC;CACtB;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,QAAQ,GAAG,GAAG,CAmD7D"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * High-level RNG interface
3
+ */
4
+ import { mulberry32 } from "./mulberry32.js";
5
+ /**
6
+ * Create an RNG instance from seed or state
7
+ */
8
+ export function createRNG(seedOrState) {
9
+ const state = typeof seedOrState === "number"
10
+ ? { seed: seedOrState, cursor: 0 }
11
+ : { ...seedOrState };
12
+ // Create generator and advance to cursor position
13
+ const generator = mulberry32(state.seed);
14
+ // Advance to current cursor position
15
+ for (let i = 0; i < state.cursor; i++) {
16
+ generator();
17
+ }
18
+ return {
19
+ next() {
20
+ state.cursor++;
21
+ return generator();
22
+ },
23
+ nextInt(min, max) {
24
+ const range = max - min + 1;
25
+ return Math.floor(this.next() * range) + min;
26
+ },
27
+ nextBool(probability = 0.5) {
28
+ return this.next() < probability;
29
+ },
30
+ pick(array) {
31
+ if (array.length === 0) {
32
+ throw new Error("Cannot pick from empty array");
33
+ }
34
+ const index = this.nextInt(0, array.length - 1);
35
+ return array[index];
36
+ },
37
+ shuffle(array) {
38
+ const result = [...array];
39
+ // Fisher-Yates shuffle
40
+ for (let i = result.length - 1; i > 0; i--) {
41
+ const j = this.nextInt(0, i);
42
+ [result[i], result[j]] = [result[j], result[i]];
43
+ }
44
+ return result;
45
+ },
46
+ getState() {
47
+ return { ...state };
48
+ },
49
+ };
50
+ }
51
+ //# sourceMappingURL=rng.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rng.js","sourceRoot":"","sources":["../../src/rng/rng.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAyB7C;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,WAA8B;IACtD,MAAM,KAAK,GACT,OAAO,WAAW,KAAK,QAAQ;QAC7B,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,EAAE;QAClC,CAAC,CAAC,EAAE,GAAG,WAAW,EAAE,CAAC;IAEzB,kDAAkD;IAClD,MAAM,SAAS,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAEzC,qCAAqC;IACrC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,SAAS,EAAE,CAAC;IACd,CAAC;IAED,OAAO;QACL,IAAI;YACF,KAAK,CAAC,MAAM,EAAE,CAAC;YACf,OAAO,SAAS,EAAE,CAAC;QACrB,CAAC;QAED,OAAO,CAAC,GAAW,EAAE,GAAW;YAC9B,MAAM,KAAK,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC;YAC5B,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC;QAC/C,CAAC;QAED,QAAQ,CAAC,WAAW,GAAG,GAAG;YACxB,OAAO,IAAI,CAAC,IAAI,EAAE,GAAG,WAAW,CAAC;QACnC,CAAC;QAED,IAAI,CAAI,KAAmB;YACzB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACvB,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;YAClD,CAAC;YACD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YAChD,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;QAED,OAAO,CAAI,KAAmB;YAC5B,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC;YAC1B,uBAAuB;YACvB,KAAK,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC3C,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC7B,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YAClD,CAAC;YACD,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,QAAQ;YACN,OAAO,EAAE,GAAG,KAAK,EAAE,CAAC;QACtB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Rules Context
3
+ *
4
+ * Provides resolved rules to engine functions.
5
+ * When no rules context is provided, default rules are used.
6
+ */
7
+ import { type ResolvedRules, type ResolvedMechanics, type CriticalsConfig, type DamageConfig, type AbilityDefinition } from "@mythxengine/types";
8
+ /**
9
+ * Rules context for engine functions
10
+ */
11
+ export interface RulesContext {
12
+ /** Fully resolved rules */
13
+ rules: ResolvedRules;
14
+ }
15
+ /**
16
+ * Get or create default rules (cached)
17
+ */
18
+ export declare function getDefaultRulesContext(): RulesContext;
19
+ /**
20
+ * Reset the default rules cache.
21
+ * Intended for test isolation - call in beforeEach/afterEach to prevent
22
+ * test pollution from cached state.
23
+ */
24
+ export declare function resetDefaultRulesCache(): void;
25
+ /**
26
+ * Create a rules context from a world pack's rules config
27
+ * @throws Error if rulesConfig is invalid
28
+ */
29
+ export declare function createRulesContext(rulesConfig?: unknown): RulesContext;
30
+ /**
31
+ * Get the mechanics config from rules context
32
+ */
33
+ export declare function getMechanics(ctx: RulesContext): ResolvedMechanics;
34
+ /**
35
+ * Get the criticals config from rules context
36
+ */
37
+ export declare function getCriticals(ctx: RulesContext): CriticalsConfig;
38
+ /**
39
+ * Check if a natural roll is a critical success using rules context
40
+ */
41
+ export declare function checkCriticalSuccess(ctx: RulesContext, natural: number): boolean;
42
+ /**
43
+ * Check if a natural roll is a critical failure using rules context
44
+ */
45
+ export declare function checkCriticalFailure(ctx: RulesContext, natural: number): boolean;
46
+ /**
47
+ * Get ability definition by ID
48
+ */
49
+ export declare function getAbilityDefinition(ctx: RulesContext, abilityId: string): AbilityDefinition | undefined;
50
+ /**
51
+ * Check if an ability ID is valid in this rules context
52
+ */
53
+ export declare function isValidAbility(ctx: RulesContext, abilityId: string): boolean;
54
+ /**
55
+ * Get the defense ability (AGI by default, or from rules config)
56
+ */
57
+ export declare function getDefenseAbility(ctx: RulesContext): string;
58
+ /**
59
+ * Get the base defense value
60
+ */
61
+ export declare function getBaseDefense(ctx: RulesContext): number;
62
+ /**
63
+ * Calculate defense target for a defender
64
+ */
65
+ export declare function calculateDefenseTarget(ctx: RulesContext, defenderAbilities: Record<string, number>): number;
66
+ /**
67
+ * Get the damage config from rules context
68
+ */
69
+ export declare function getDamageConfig(ctx: RulesContext): DamageConfig;
70
+ /**
71
+ * Get resistance multiplier from rules
72
+ */
73
+ export declare function getResistanceMultiplier(ctx: RulesContext): number;
74
+ /**
75
+ * Get vulnerability multiplier from rules
76
+ */
77
+ export declare function getVulnerabilityMultiplier(ctx: RulesContext): number;
78
+ /**
79
+ * Check if this is a roll-under system
80
+ */
81
+ export declare function isRollUnder(ctx: RulesContext): boolean;
82
+ /**
83
+ * Get the dice to use for tests (d20 or d100 for roll-under)
84
+ */
85
+ export declare function getTestDice(ctx: RulesContext): string;
86
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/rules/context.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EAMvB,MAAM,oBAAoB,CAAC;AAE5B;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,2BAA2B;IAC3B,KAAK,EAAE,aAAa,CAAC;CACtB;AAKD;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,YAAY,CAKrD;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,CAAC,EAAE,OAAO,GAAG,YAAY,CAkBtE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,YAAY,GAAG,iBAAiB,CAEjE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,YAAY,GAAG,eAAe,CAE/D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAEhF;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAEhF;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,YAAY,EACjB,SAAS,EAAE,MAAM,GAChB,iBAAiB,GAAG,SAAS,CAE/B;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAE5E;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAE3D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAExD;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,YAAY,EACjB,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACxC,MAAM,CAKR;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,YAAY,GAAG,YAAY,CAE/D;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAEjE;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAEpE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAEtD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAKrD"}
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Rules Context
3
+ *
4
+ * Provides resolved rules to engine functions.
5
+ * When no rules context is provided, default rules are used.
6
+ */
7
+ import { resolveRules, validateRulesConfig, isCriticalSuccess, isCriticalFailure, } from "@mythxengine/types";
8
+ // Cached default rules (module-level singleton)
9
+ let defaultRulesCache = null;
10
+ /**
11
+ * Get or create default rules (cached)
12
+ */
13
+ export function getDefaultRulesContext() {
14
+ if (!defaultRulesCache) {
15
+ defaultRulesCache = resolveRules();
16
+ }
17
+ return { rules: defaultRulesCache };
18
+ }
19
+ /**
20
+ * Reset the default rules cache.
21
+ * Intended for test isolation - call in beforeEach/afterEach to prevent
22
+ * test pollution from cached state.
23
+ */
24
+ export function resetDefaultRulesCache() {
25
+ defaultRulesCache = null;
26
+ }
27
+ /**
28
+ * Create a rules context from a world pack's rules config
29
+ * @throws Error if rulesConfig is invalid
30
+ */
31
+ export function createRulesContext(rulesConfig) {
32
+ if (!rulesConfig) {
33
+ return getDefaultRulesContext();
34
+ }
35
+ // Basic structure check
36
+ if (typeof rulesConfig !== "object" || rulesConfig === null) {
37
+ throw new Error("rulesConfig must be an object");
38
+ }
39
+ // Validate the config
40
+ const config = rulesConfig;
41
+ const errors = validateRulesConfig(config);
42
+ if (errors.length > 0) {
43
+ throw new Error(`Invalid rules config: ${errors.join("; ")}`);
44
+ }
45
+ return { rules: resolveRules(config) };
46
+ }
47
+ /**
48
+ * Get the mechanics config from rules context
49
+ */
50
+ export function getMechanics(ctx) {
51
+ return ctx.rules.mechanics;
52
+ }
53
+ /**
54
+ * Get the criticals config from rules context
55
+ */
56
+ export function getCriticals(ctx) {
57
+ return ctx.rules.mechanics.criticals;
58
+ }
59
+ /**
60
+ * Check if a natural roll is a critical success using rules context
61
+ */
62
+ export function checkCriticalSuccess(ctx, natural) {
63
+ return isCriticalSuccess(natural, ctx.rules.mechanics.criticals);
64
+ }
65
+ /**
66
+ * Check if a natural roll is a critical failure using rules context
67
+ */
68
+ export function checkCriticalFailure(ctx, natural) {
69
+ return isCriticalFailure(natural, ctx.rules.mechanics.criticals);
70
+ }
71
+ /**
72
+ * Get ability definition by ID
73
+ */
74
+ export function getAbilityDefinition(ctx, abilityId) {
75
+ return ctx.rules.abilityMap.get(abilityId);
76
+ }
77
+ /**
78
+ * Check if an ability ID is valid in this rules context
79
+ */
80
+ export function isValidAbility(ctx, abilityId) {
81
+ return ctx.rules.abilityMap.has(abilityId);
82
+ }
83
+ /**
84
+ * Get the defense ability (AGI by default, or from rules config)
85
+ */
86
+ export function getDefenseAbility(ctx) {
87
+ return ctx.rules.mechanics.defense.ability ?? "AGI";
88
+ }
89
+ /**
90
+ * Get the base defense value
91
+ */
92
+ export function getBaseDefense(ctx) {
93
+ return ctx.rules.mechanics.defense.base;
94
+ }
95
+ /**
96
+ * Calculate defense target for a defender
97
+ */
98
+ export function calculateDefenseTarget(ctx, defenderAbilities) {
99
+ const { base, ability } = ctx.rules.mechanics.defense;
100
+ const abilityId = ability ?? "AGI";
101
+ const abilityMod = defenderAbilities[abilityId] ?? 0;
102
+ return base + abilityMod;
103
+ }
104
+ /**
105
+ * Get the damage config from rules context
106
+ */
107
+ export function getDamageConfig(ctx) {
108
+ return ctx.rules.mechanics.damage;
109
+ }
110
+ /**
111
+ * Get resistance multiplier from rules
112
+ */
113
+ export function getResistanceMultiplier(ctx) {
114
+ return ctx.rules.mechanics.resistanceMultiplier;
115
+ }
116
+ /**
117
+ * Get vulnerability multiplier from rules
118
+ */
119
+ export function getVulnerabilityMultiplier(ctx) {
120
+ return ctx.rules.mechanics.vulnerabilityMultiplier;
121
+ }
122
+ /**
123
+ * Check if this is a roll-under system
124
+ */
125
+ export function isRollUnder(ctx) {
126
+ return ctx.rules.mechanics.rollUnder?.enabled ?? false;
127
+ }
128
+ /**
129
+ * Get the dice to use for tests (d20 or d100 for roll-under)
130
+ */
131
+ export function getTestDice(ctx) {
132
+ if (ctx.rules.mechanics.rollUnder?.enabled) {
133
+ return ctx.rules.mechanics.rollUnder.dice;
134
+ }
135
+ return "d20";
136
+ }
137
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/rules/context.ts"],"names":[],"mappings":"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"}