@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.
- package/LICENSE +21 -0
- package/dist/__tests__/advantage.test.d.ts +5 -0
- package/dist/__tests__/advantage.test.d.ts.map +1 -0
- package/dist/__tests__/advantage.test.js +126 -0
- package/dist/__tests__/advantage.test.js.map +1 -0
- package/dist/__tests__/dice.test.d.ts +2 -0
- package/dist/__tests__/dice.test.d.ts.map +1 -0
- package/dist/__tests__/dice.test.js +150 -0
- package/dist/__tests__/dice.test.js.map +1 -0
- package/dist/__tests__/resolution.test.d.ts +5 -0
- package/dist/__tests__/resolution.test.d.ts.map +1 -0
- package/dist/__tests__/resolution.test.js +362 -0
- package/dist/__tests__/resolution.test.js.map +1 -0
- package/dist/__tests__/rng.test.d.ts +2 -0
- package/dist/__tests__/rng.test.d.ts.map +1 -0
- package/dist/__tests__/rng.test.js +93 -0
- package/dist/__tests__/rng.test.js.map +1 -0
- package/dist/dice/advantage.d.ts +45 -0
- package/dist/dice/advantage.d.ts.map +1 -0
- package/dist/dice/advantage.js +118 -0
- package/dist/dice/advantage.js.map +1 -0
- package/dist/dice/index.d.ts +7 -0
- package/dist/dice/index.d.ts.map +1 -0
- package/dist/dice/index.js +7 -0
- package/dist/dice/index.js.map +1 -0
- package/dist/dice/parser.d.ts +21 -0
- package/dist/dice/parser.d.ts.map +1 -0
- package/dist/dice/parser.js +61 -0
- package/dist/dice/parser.js.map +1 -0
- package/dist/dice/roller.d.ts +23 -0
- package/dist/dice/roller.d.ts.map +1 -0
- package/dist/dice/roller.js +62 -0
- package/dist/dice/roller.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/resolution/combat.d.ts +39 -0
- package/dist/resolution/combat.d.ts.map +1 -0
- package/dist/resolution/combat.js +214 -0
- package/dist/resolution/combat.js.map +1 -0
- package/dist/resolution/damage.d.ts +27 -0
- package/dist/resolution/damage.d.ts.map +1 -0
- package/dist/resolution/damage.js +44 -0
- package/dist/resolution/damage.js.map +1 -0
- package/dist/resolution/index.d.ts +8 -0
- package/dist/resolution/index.d.ts.map +1 -0
- package/dist/resolution/index.js +8 -0
- package/dist/resolution/index.js.map +1 -0
- package/dist/resolution/initiative.d.ts +19 -0
- package/dist/resolution/initiative.d.ts.map +1 -0
- package/dist/resolution/initiative.js +55 -0
- package/dist/resolution/initiative.js.map +1 -0
- package/dist/resolution/test.d.ts +34 -0
- package/dist/resolution/test.d.ts.map +1 -0
- package/dist/resolution/test.js +143 -0
- package/dist/resolution/test.js.map +1 -0
- package/dist/rng/index.d.ts +6 -0
- package/dist/rng/index.d.ts.map +1 -0
- package/dist/rng/index.js +6 -0
- package/dist/rng/index.js.map +1 -0
- package/dist/rng/mulberry32.d.ts +13 -0
- package/dist/rng/mulberry32.d.ts.map +1 -0
- package/dist/rng/mulberry32.js +23 -0
- package/dist/rng/mulberry32.js.map +1 -0
- package/dist/rng/rng.d.ts +26 -0
- package/dist/rng/rng.d.ts.map +1 -0
- package/dist/rng/rng.js +51 -0
- package/dist/rng/rng.js.map +1 -0
- package/dist/rules/context.d.ts +86 -0
- package/dist/rules/context.d.ts.map +1 -0
- package/dist/rules/context.js +137 -0
- package/dist/rules/context.js.map +1 -0
- package/dist/rules/custom-test.d.ts +33 -0
- package/dist/rules/custom-test.d.ts.map +1 -0
- package/dist/rules/custom-test.js +164 -0
- package/dist/rules/custom-test.js.map +1 -0
- package/dist/rules/index.d.ts +6 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +6 -0
- package/dist/rules/index.js.map +1 -0
- 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 @@
|
|
|
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 @@
|
|
|
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"}
|
package/dist/rng/rng.js
ADDED
|
@@ -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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"}
|