@iamjameslennon/ddb-mcp 2.8.0 → 2.9.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/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +19 -4
- package/dist/browser.js.map +1 -1
- package/dist/index.js +10 -18
- package/dist/index.js.map +1 -1
- package/dist/tools/campaign.js +1 -1
- package/dist/tools/campaign.js.map +1 -1
- package/dist/tools/character/ac.d.ts +20 -0
- package/dist/tools/character/ac.d.ts.map +1 -0
- package/dist/tools/character/ac.js +83 -0
- package/dist/tools/character/ac.js.map +1 -0
- package/dist/tools/character/actions.d.ts +22 -0
- package/dist/tools/character/actions.d.ts.map +1 -0
- package/dist/tools/character/actions.js +110 -0
- package/dist/tools/character/actions.js.map +1 -0
- package/dist/tools/character/core.d.ts +15 -0
- package/dist/tools/character/core.d.ts.map +1 -0
- package/dist/tools/character/core.js +129 -0
- package/dist/tools/character/core.js.map +1 -0
- package/dist/tools/character/defenses.d.ts +16 -0
- package/dist/tools/character/defenses.d.ts.map +1 -0
- package/dist/tools/character/defenses.js +27 -0
- package/dist/tools/character/defenses.js.map +1 -0
- package/dist/tools/character/definition.d.ts +14 -0
- package/dist/tools/character/definition.d.ts.map +1 -0
- package/dist/tools/character/definition.js +205 -0
- package/dist/tools/character/definition.js.map +1 -0
- package/dist/tools/character/features.d.ts +26 -0
- package/dist/tools/character/features.d.ts.map +1 -0
- package/dist/tools/character/features.js +107 -0
- package/dist/tools/character/features.js.map +1 -0
- package/dist/tools/character/helpers.d.ts +57 -0
- package/dist/tools/character/helpers.d.ts.map +1 -0
- package/dist/tools/character/helpers.js +80 -0
- package/dist/tools/character/helpers.js.map +1 -0
- package/dist/tools/character/identity.d.ts +19 -0
- package/dist/tools/character/identity.d.ts.map +1 -0
- package/dist/tools/character/identity.js +84 -0
- package/dist/tools/character/identity.js.map +1 -0
- package/dist/tools/character/inventory.d.ts +21 -0
- package/dist/tools/character/inventory.d.ts.map +1 -0
- package/dist/tools/character/inventory.js +61 -0
- package/dist/tools/character/inventory.js.map +1 -0
- package/dist/tools/character/notes.d.ts +20 -0
- package/dist/tools/character/notes.d.ts.map +1 -0
- package/dist/tools/character/notes.js +63 -0
- package/dist/tools/character/notes.js.map +1 -0
- package/dist/tools/character/parse.d.ts +13 -0
- package/dist/tools/character/parse.d.ts.map +1 -0
- package/dist/tools/character/parse.js +82 -0
- package/dist/tools/character/parse.js.map +1 -0
- package/dist/tools/character/spells.d.ts +26 -0
- package/dist/tools/character/spells.d.ts.map +1 -0
- package/dist/tools/character/spells.js +237 -0
- package/dist/tools/character/spells.js.map +1 -0
- package/dist/tools/character/stats.d.ts +33 -0
- package/dist/tools/character/stats.d.ts.map +1 -0
- package/dist/tools/character/stats.js +372 -0
- package/dist/tools/character/stats.js.map +1 -0
- package/dist/tools/character/templates.d.ts +22 -0
- package/dist/tools/character/templates.d.ts.map +1 -0
- package/dist/tools/character/templates.js +61 -0
- package/dist/tools/character/templates.js.map +1 -0
- package/dist/tools/character/types.d.ts +133 -0
- package/dist/tools/character/types.d.ts.map +1 -0
- package/dist/tools/character/types.js +12 -0
- package/dist/tools/character/types.js.map +1 -0
- package/dist/tools/character/vitals.d.ts +34 -0
- package/dist/tools/character/vitals.d.ts.map +1 -0
- package/dist/tools/character/vitals.js +198 -0
- package/dist/tools/character/vitals.js.map +1 -0
- package/dist/tools/character/weapons.d.ts +13 -0
- package/dist/tools/character/weapons.d.ts.map +1 -0
- package/dist/tools/character/weapons.js +79 -0
- package/dist/tools/character/weapons.js.map +1 -0
- package/dist/tools/character.d.ts +13 -3
- package/dist/tools/character.d.ts.map +1 -1
- package/dist/tools/character.js +18 -1181
- package/dist/tools/character.js.map +1 -1
- package/dist/tools/library.d.ts +32 -4
- package/dist/tools/library.d.ts.map +1 -1
- package/dist/tools/library.js +181 -86
- package/dist/tools/library.js.map +1 -1
- package/dist/tools/search.d.ts +1 -2
- package/dist/tools/search.d.ts.map +1 -1
- package/dist/tools/search.js +36 -59
- package/dist/tools/search.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stats domain — saving throws, skills, senses, proficiencies. Owns the
|
|
3
|
+
* ability-score display line and the stats block.
|
|
4
|
+
*
|
|
5
|
+
* Phase 5 of the character.ts refactor — see docs/character-refactor.md.
|
|
6
|
+
*
|
|
7
|
+
* Output strings are pre-formatted (e.g. `savingThrows = ["STR +5*", ...]`)
|
|
8
|
+
* to preserve byte-for-byte parity with the original inline code. A future
|
|
9
|
+
* pass could lift these into structured data, but that's a separate concern.
|
|
10
|
+
*/
|
|
11
|
+
import { arr, capitalize, hasRemarkableAthlete, isBonusMod, isExpertiseMod, isHalfProficiencyMod, isLanguageMod, isProficiencyMod, isSenseMod, isSetBaseMod, isSetMod, num, obj, signed, statKeys, statNames, str } from "./helpers.js";
|
|
12
|
+
// 18 standard skills, with the index of the ability stat they key off of.
|
|
13
|
+
const SKILLS = [
|
|
14
|
+
["Acrobatics", 1], ["Animal Handling", 4], ["Arcana", 3], ["Athletics", 0],
|
|
15
|
+
["Deception", 5], ["History", 3], ["Insight", 4], ["Intimidation", 5],
|
|
16
|
+
["Investigation", 3], ["Medicine", 4], ["Nature", 3], ["Perception", 4],
|
|
17
|
+
["Performance", 5], ["Persuasion", 5], ["Religion", 3], ["Sleight of Hand", 1],
|
|
18
|
+
["Stealth", 1], ["Survival", 4],
|
|
19
|
+
];
|
|
20
|
+
const SENSE_ID_NAMES = {
|
|
21
|
+
1: "Blindsight", 2: "Darkvision", 3: "Tremorsense", 4: "Truesight",
|
|
22
|
+
};
|
|
23
|
+
const SENSE_SLUGS = new Set(["darkvision", "blindsight", "tremorsense", "truesight"]);
|
|
24
|
+
// ── Ability score display ────────────────────────────────────────────────────
|
|
25
|
+
function buildAbilityScoreDisplay(statTotals, statMods) {
|
|
26
|
+
return statNames.map((n, i) => `${n} ${statTotals[i]} (${signed(statMods[i])})`);
|
|
27
|
+
}
|
|
28
|
+
// ── Saving throws ────────────────────────────────────────────────────────────
|
|
29
|
+
function buildSavingThrows(allMods, statMods, profBonus) {
|
|
30
|
+
const saveProfSubTypes = new Set(allMods.filter(isProficiencyMod)
|
|
31
|
+
.filter(m => m.subType.includes("saving-throws"))
|
|
32
|
+
.map(m => m.subType));
|
|
33
|
+
// Global save bonus — `bonus subType:"saving-throws"` applies to every save.
|
|
34
|
+
// Sources: Stone of Good Luck (Luckstone), Cloak of Protection, Paladin's
|
|
35
|
+
// Aura of Protection (via grantedModifiers on the aura feature), etc.
|
|
36
|
+
const globalSaveBonus = allMods
|
|
37
|
+
.filter(m => isBonusMod(m) && m.subType === "saving-throws")
|
|
38
|
+
.reduce((s, m) => s + num(m.value ?? m.fixedValue), 0);
|
|
39
|
+
return statKeys.map((key, i) => {
|
|
40
|
+
const isProficient = saveProfSubTypes.has(`${key}-saving-throws`);
|
|
41
|
+
const total = statMods[i] + (isProficient ? profBonus : 0) + globalSaveBonus;
|
|
42
|
+
return `${statNames[i]} ${signed(total)}${isProficient ? "*" : ""}`;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
// STR/DEX/CON stat indices — the abilities that Remarkable Athlete covers.
|
|
46
|
+
const RA_STAT_INDICES = new Set([0, 1, 2]);
|
|
47
|
+
function computeSkillBonuses(allMods, statMods, profBonus, classes) {
|
|
48
|
+
const skillProfSubTypes = new Set(allMods.filter(isProficiencyMod).map(m => m.subType));
|
|
49
|
+
const skillExpertiseSubTypes = new Set(allMods.filter(isExpertiseMod).map(m => m.subType));
|
|
50
|
+
// half-proficiency (e.g. Bard's Jack of All Trades applies to all ability
|
|
51
|
+
// checks; floor(prof/2)).
|
|
52
|
+
const hasJackOfAllTrades = allMods.some(m => isHalfProficiencyMod(m) && m.subType === "ability-checks");
|
|
53
|
+
const skillHalfProfSubTypes = new Set(allMods.filter(isHalfProficiencyMod)
|
|
54
|
+
.filter(m => m.subType !== "ability-checks")
|
|
55
|
+
.map(m => m.subType));
|
|
56
|
+
// Remarkable Athlete (Champion 7+) adds ceil(prof/2) to non-proficient
|
|
57
|
+
// STR/DEX/CON ability checks. DDB does NOT emit a half-proficiency
|
|
58
|
+
// modifier for RA, so we must detect it by class + level.
|
|
59
|
+
const ra = hasRemarkableAthlete(classes);
|
|
60
|
+
// Global ability-check bonus — `bonus subType:"ability-checks"` applies to
|
|
61
|
+
// every skill (and therefore every passive). Sources: Stone of Good Luck
|
|
62
|
+
// (Luckstone), Headband of Intellect (no — that's a set on INT score), etc.
|
|
63
|
+
const globalAbilityCheckBonus = allMods
|
|
64
|
+
.filter(m => isBonusMod(m) && m.subType === "ability-checks")
|
|
65
|
+
.reduce((s, m) => s + num(m.value ?? m.fixedValue), 0);
|
|
66
|
+
return SKILLS.map(([skillName, statIdx]) => {
|
|
67
|
+
const slug = skillName.toLowerCase().replace(/ /g, "-").replace(/'/g, "");
|
|
68
|
+
const isProficient = skillProfSubTypes.has(slug);
|
|
69
|
+
const isExpertise = skillExpertiseSubTypes.has(slug);
|
|
70
|
+
const isJoatHalf = !isProficient && (skillHalfProfSubTypes.has(slug) || hasJackOfAllTrades);
|
|
71
|
+
const isRaHalf = !isProficient && ra && RA_STAT_INDICES.has(statIdx);
|
|
72
|
+
let bonus = statMods[statIdx];
|
|
73
|
+
if (isExpertise)
|
|
74
|
+
bonus += profBonus * 2;
|
|
75
|
+
else if (isProficient)
|
|
76
|
+
bonus += profBonus;
|
|
77
|
+
// RA takes precedence over JoAT on STR/DEX/CON because it rounds up
|
|
78
|
+
// (a strict superset of JoAT's floor at odd proficiency bonuses).
|
|
79
|
+
else if (isRaHalf)
|
|
80
|
+
bonus += Math.ceil(profBonus / 2);
|
|
81
|
+
else if (isJoatHalf)
|
|
82
|
+
bonus += Math.floor(profBonus / 2);
|
|
83
|
+
// Flat bonus modifiers on the skill subType (e.g. Divine Order: Scholar adds WIS to Arcana/Religion).
|
|
84
|
+
// When value/fixedValue are null, statId identifies which ability modifier to add instead.
|
|
85
|
+
const flatBonus = allMods
|
|
86
|
+
.filter(m => isBonusMod(m) && m.subType === slug)
|
|
87
|
+
.reduce((s, m) => {
|
|
88
|
+
if (m.value != null)
|
|
89
|
+
return s + num(m.value);
|
|
90
|
+
if (m.fixedValue != null)
|
|
91
|
+
return s + num(m.fixedValue);
|
|
92
|
+
const sid = num(m.statId);
|
|
93
|
+
return s + (sid > 0 ? statMods[sid - 1] : 0);
|
|
94
|
+
}, 0);
|
|
95
|
+
bonus += flatBonus + globalAbilityCheckBonus;
|
|
96
|
+
return { bonus, isProficient, isExpertise };
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
function buildSkillLines(skillBonuses) {
|
|
100
|
+
return SKILLS.map(([skillName, statIdx], i) => {
|
|
101
|
+
const { bonus, isProficient, isExpertise } = skillBonuses[i];
|
|
102
|
+
const marker = isExpertise ? " **" : isProficient ? " *" : "";
|
|
103
|
+
const statLabel = statNames[statIdx];
|
|
104
|
+
return ` ${(skillName + ` (${statLabel})`).padEnd(22)} ${signed(bonus)}${marker}`;
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
// ── Senses ──────────────────────────────────────────────────────────────────
|
|
108
|
+
// Senses use a two-pass model so Gloom Stalker's Umbral Sight extends race
|
|
109
|
+
// darkvision rather than being clamped to the higher of the two:
|
|
110
|
+
//
|
|
111
|
+
// Pass 1 (base-establishing): record `set`/`set-base` modifiers,
|
|
112
|
+
// `customSenses`, and racial-trait sense notes. These define the
|
|
113
|
+
// character's baseline for each sense.
|
|
114
|
+
// Pass 2 (`type:"sense"` modifiers): for each, decide:
|
|
115
|
+
// • no baseline yet → this sense becomes the base (2024-style: race
|
|
116
|
+
// emits `type:"sense"` directly without a paired set-base)
|
|
117
|
+
// • baseline from a DIFFERENT component → additive (Umbral Sight pattern:
|
|
118
|
+
// race has set-base 60 from one component, class emits sense 30 from
|
|
119
|
+
// another → 60 + 30 = 90)
|
|
120
|
+
// • baseline from the SAME component → take the max (Umbral Sight in
|
|
121
|
+
// isolation emits both set-base 30 and sense 30 from the same
|
|
122
|
+
// component as dual encodings — must not double-count to 60)
|
|
123
|
+
function computeSenses(char, allMods, skillBonuses) {
|
|
124
|
+
const perceptionIdx = SKILLS.findIndex(([n]) => n === "Perception");
|
|
125
|
+
const investigationIdx = SKILLS.findIndex(([n]) => n === "Investigation");
|
|
126
|
+
const insightIdx = SKILLS.findIndex(([n]) => n === "Insight");
|
|
127
|
+
const passivePerception = 10 + skillBonuses[perceptionIdx].bonus;
|
|
128
|
+
const passiveInvestigation = 10 + skillBonuses[investigationIdx].bonus;
|
|
129
|
+
const passiveInsight = 10 + skillBonuses[insightIdx].bonus;
|
|
130
|
+
const senseBase = new Map();
|
|
131
|
+
const senseAdditions = new Map();
|
|
132
|
+
const recordBase = (name, val, cid) => {
|
|
133
|
+
if (val <= 0)
|
|
134
|
+
return;
|
|
135
|
+
const existing = senseBase.get(name);
|
|
136
|
+
if (!existing || val > existing.value)
|
|
137
|
+
senseBase.set(name, { value: val, componentId: cid });
|
|
138
|
+
};
|
|
139
|
+
// Pass 1: collect baseline-establishing sources.
|
|
140
|
+
for (const m of allMods) {
|
|
141
|
+
if ((isSetMod(m) || isSetBaseMod(m)) && SENSE_SLUGS.has(m.subType)) {
|
|
142
|
+
recordBase(capitalize(m.subType), num(m.value), num(m.componentId));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
for (const cs of arr(char.customSenses)) {
|
|
146
|
+
const name = SENSE_ID_NAMES[num(cs.senseId)];
|
|
147
|
+
if (name)
|
|
148
|
+
recordBase(name, num(cs.value), 0);
|
|
149
|
+
}
|
|
150
|
+
for (const trait of arr(obj(char.race).racialTraits)) {
|
|
151
|
+
for (const sense of arr(obj(trait.definition).senses)) {
|
|
152
|
+
const name = SENSE_ID_NAMES[num(sense.senseId)];
|
|
153
|
+
const match = str(sense.notes).match(/\d+/);
|
|
154
|
+
if (name && match)
|
|
155
|
+
recordBase(name, parseInt(match[0], 10), 0);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// Pass 2: process `type:"sense"` modifiers — additive if from a different
|
|
159
|
+
// component than the current baseline (Umbral Sight extending race darkvision).
|
|
160
|
+
for (const m of allMods) {
|
|
161
|
+
if (!isSenseMod(m))
|
|
162
|
+
continue;
|
|
163
|
+
const name = capitalize(m.subType);
|
|
164
|
+
const val = num(m.value);
|
|
165
|
+
if (val <= 0)
|
|
166
|
+
continue;
|
|
167
|
+
const cid = num(m.componentId);
|
|
168
|
+
const baseEntry = senseBase.get(name);
|
|
169
|
+
if (!baseEntry) {
|
|
170
|
+
recordBase(name, val, cid);
|
|
171
|
+
}
|
|
172
|
+
else if (baseEntry.componentId !== cid) {
|
|
173
|
+
senseAdditions.set(name, (senseAdditions.get(name) ?? 0) + val);
|
|
174
|
+
}
|
|
175
|
+
else if (val > baseEntry.value) {
|
|
176
|
+
// Same component — dual encoding of one effective value; keep the higher.
|
|
177
|
+
senseBase.set(name, { value: val, componentId: cid });
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
const special = [];
|
|
181
|
+
for (const [name, { value }] of senseBase) {
|
|
182
|
+
special.push(`${name} ${value + (senseAdditions.get(name) ?? 0)} ft.`);
|
|
183
|
+
}
|
|
184
|
+
return { passivePerception, passiveInvestigation, passiveInsight, special };
|
|
185
|
+
}
|
|
186
|
+
// ── Proficiencies ───────────────────────────────────────────────────────────
|
|
187
|
+
// customProficiencies type IDs (DDB's encoding):
|
|
188
|
+
// 1 = Skill, 2 = Tool, 3 = Language
|
|
189
|
+
// Languages added by the player directly (not via a race/background/feat
|
|
190
|
+
// modifier) live here. Confirmed by inspecting Astarion (107164636) whose
|
|
191
|
+
// "Orc" language is stored as {name:"Orc", type:3, proficiencyLevel:3}.
|
|
192
|
+
const CUSTOM_PROF_TYPE_LANGUAGE = 3;
|
|
193
|
+
// characterValues entries with typeId 35 are language grants that point
|
|
194
|
+
// into the rule-data language table via `valueId` (a stringified integer).
|
|
195
|
+
// DDB's React app resolves the ID → name client-side from a /rule-data
|
|
196
|
+
// fetch. Confirmed via Playwright network trace against the three "missing
|
|
197
|
+
// languages" characters in BUG #7. We mirror every officially-sourced
|
|
198
|
+
// language (those with `rpgSourceId != null` in /character/v5/rule-data)
|
|
199
|
+
// here so the parser doesn't need a network call. Creature-language IDs
|
|
200
|
+
// (Worg, Sahuagin, Giant Eagle, etc — `rpgSourceId: null`) are excluded
|
|
201
|
+
// since they aren't typically player-selectable. Unknown IDs fall back
|
|
202
|
+
// to a placeholder so homebrew never silently disappears.
|
|
203
|
+
//
|
|
204
|
+
// Refresh procedure: re-run `scripts/dump-sourced-languages.mts` and
|
|
205
|
+
// paste the output here, keeping the ID-sorted order.
|
|
206
|
+
const CHAR_VALUE_TYPE_LANGUAGE = 35;
|
|
207
|
+
const LANGUAGE_NAMES_BY_ID = {
|
|
208
|
+
// PHB (rpgSourceId 198) — core 2014 + 2024 standard languages
|
|
209
|
+
1: "Common", 2: "Dwarvish", 3: "Elvish", 4: "Giant", 5: "Gnomish",
|
|
210
|
+
6: "Goblin", 7: "Halfling", 8: "Orc", 9: "Abyssal", 10: "Celestial",
|
|
211
|
+
11: "Draconic", 12: "Deep Speech", 13: "Infernal", 14: "Primordial",
|
|
212
|
+
15: "Sylvan", 16: "Undercommon",
|
|
213
|
+
// Telepathy (unsourced in rule-data but commonly granted by monster races / mind flayers)
|
|
214
|
+
18: "Telepathy",
|
|
215
|
+
// PHB elementals + Druidic + Thieves' Cant
|
|
216
|
+
19: "Aquan", 20: "Auran", 21: "Ignan", 22: "Terran", 23: "Druidic",
|
|
217
|
+
46: "Thieves' Cant",
|
|
218
|
+
// Journeys through the Radiant Citadel (rpgSourceId 87)
|
|
219
|
+
74: "Citlanés", 75: "Djaynaian", 76: "Godstongue", 77: "Halri",
|
|
220
|
+
78: "Maynah", 79: "N'warian", 80: "Quirapu", 81: "Sensan",
|
|
221
|
+
82: "Shankhi", 83: "Tletlahtolli", 84: "Xingyu", 85: "Zabaani",
|
|
222
|
+
// Spelljammer: Adventures in Space (rpgSourceId 90)
|
|
223
|
+
86: "Dohwar", 87: "Hadozee", 88: "Aartuk",
|
|
224
|
+
// Dragonlance: Shadow of the Dragon Queen (rpgSourceId 95)
|
|
225
|
+
89: "Abanasinian", 90: "Ergot", 91: "Istarian", 92: "Kenderspeak",
|
|
226
|
+
93: "Kharolian", 94: "Khur", 95: "Kothian", 96: "Nerakese",
|
|
227
|
+
97: "Nordmaarian", 98: "Ogre", 99: "Solamnic",
|
|
228
|
+
// Planescape: Adventures in the Multiverse (rpgSourceId 114)
|
|
229
|
+
101: "Demodand",
|
|
230
|
+
// Humblewood (rpgSourceId 133)
|
|
231
|
+
102: "Birdfolk", 103: "Cervan", 105: "Hedge", 106: "Jerbeen",
|
|
232
|
+
107: "Mapach", 108: "Vulpin",
|
|
233
|
+
// Tome of Beasts / Kobold Press (rpgSourceId 139)
|
|
234
|
+
110: "Derro", 111: "Eonic", 113: "Lemurfolk", 114: "Loxodan",
|
|
235
|
+
115: "Millitaur", 117: "Tosculi", 118: "Trollkin", 120: "Void Speech",
|
|
236
|
+
// Tales of the Valiant (rpgSourceId 142)
|
|
237
|
+
121: "Angulotl", 122: "Kuran'zoi",
|
|
238
|
+
// Tome of Beasts 2 / Midgard (rpgSourceId 151)
|
|
239
|
+
109: "Darakhul", 112: "Erina", 116: "Ravenfolk", 119: "Umbral",
|
|
240
|
+
124: "Huginn's Speech", 125: "Northern Tongue",
|
|
241
|
+
// Ankeshel (rpgSourceId 152)
|
|
242
|
+
126: "Ankeshelian",
|
|
243
|
+
// Birdfolk feather speech (rpgSourceId 39)
|
|
244
|
+
104: "Feather Speech",
|
|
245
|
+
// Old Gods of Appalachia / Drakkenheim (rpgSourceId 137)
|
|
246
|
+
123: "Gibberling",
|
|
247
|
+
// 2024 additions (rpgSourceId 198)
|
|
248
|
+
127: "Common Sign Language",
|
|
249
|
+
// Adventures in Middle-earth (rpgSourceId 154)
|
|
250
|
+
129: "Black Speech", 130: "Dalish", 131: "Khuzdul", 132: "Orkish",
|
|
251
|
+
133: "Sindarin", 134: "Warg-speech", 135: "Westron", 136: "Dunlendish",
|
|
252
|
+
// Setting expansion (rpgSourceId 156) — secondary Thieves' Cant entry
|
|
253
|
+
137: "Thieves' Cant",
|
|
254
|
+
// Quests from the Infinite Staircase / Critical Role / etc (rpgSourceId 158, 160, 162)
|
|
255
|
+
138: "Capran", 139: "Eluran", 140: "Tilia",
|
|
256
|
+
141: "Swallybog",
|
|
257
|
+
142: "Dara", 143: "Howler", 144: "Naku Naku", 145: "Torum",
|
|
258
|
+
// Pathfinder for Dummies / Paizo-style (rpgSourceId 202)
|
|
259
|
+
146: "Aklo", 147: "Caligni", 148: "Daemonic", 149: "Necril",
|
|
260
|
+
150: "Varisian",
|
|
261
|
+
// Forgotten Realms regional (rpgSourceId 205)
|
|
262
|
+
151: "Aglarondan", 152: "Alzhedo", 153: "Chessentan", 154: "Chondathan",
|
|
263
|
+
155: "Damaran", 156: "Iluskan", 157: "Lantanese", 158: "Midani",
|
|
264
|
+
159: "Mulhorandi", 160: "Rashemi", 161: "Reghedjic", 162: "Sespech",
|
|
265
|
+
163: "Turmic", 164: "Untheric",
|
|
266
|
+
// Misc later additions (rpgSourceId 224, 225, 230)
|
|
267
|
+
165: "Skin Cant", 166: "Communication Spores", 167: "Archosauric",
|
|
268
|
+
};
|
|
269
|
+
function computeProficiencies(char, allMods) {
|
|
270
|
+
const armorProfMap = {
|
|
271
|
+
"light-armor": "Light Armor", "medium-armor": "Medium Armor",
|
|
272
|
+
"heavy-armor": "Heavy Armor", "shields": "Shields",
|
|
273
|
+
};
|
|
274
|
+
const weaponProfMap = {
|
|
275
|
+
"simple-weapons": "Simple Weapons", "martial-weapons": "Martial Weapons",
|
|
276
|
+
};
|
|
277
|
+
// Placeholder subType values that are unresolved character-builder selections — discard them.
|
|
278
|
+
const isProfPlaceholder = (sub) => sub.toLowerCase().startsWith("choose") || sub.toLowerCase() === "self";
|
|
279
|
+
// Specific weapon type slugs — route to Weapons, not Tools.
|
|
280
|
+
const isWeaponSlug = (sub) => /sword|axe|bow|crossbow|dagger|dart|sling|blowgun|staff|spear|club|mace|hammer|flail|lance|pike|rapier|scimitar|sickle|whip|maul|halberd|glaive|javelin|trident|handaxe|net|morningstar/.test(sub);
|
|
281
|
+
const armor = [];
|
|
282
|
+
const weapons = [];
|
|
283
|
+
const tools = [];
|
|
284
|
+
const languages = [];
|
|
285
|
+
for (const m of allMods) {
|
|
286
|
+
const sub = str(m.subType);
|
|
287
|
+
if (isProfPlaceholder(sub))
|
|
288
|
+
continue;
|
|
289
|
+
if (isProficiencyMod(m)) {
|
|
290
|
+
if (armorProfMap[sub])
|
|
291
|
+
armor.push(armorProfMap[sub]);
|
|
292
|
+
else if (weaponProfMap[sub])
|
|
293
|
+
weapons.push(weaponProfMap[sub]);
|
|
294
|
+
else if (sub.includes("saving-throws") || sub.includes("-skill") ||
|
|
295
|
+
SKILLS.some(([n]) => n.toLowerCase().replace(/ /g, "-").replace(/'/g, "") === sub)) {
|
|
296
|
+
// skill/save prof — handled elsewhere
|
|
297
|
+
}
|
|
298
|
+
else if (!sub.includes("-score") && sub.length > 0 &&
|
|
299
|
+
!statKeys.some(k => sub.startsWith(k))) {
|
|
300
|
+
if (isWeaponSlug(sub)) {
|
|
301
|
+
weapons.push(capitalize(sub.replace(/-/g, " ")));
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
tools.push(capitalize(sub.replace(/-/g, " ")));
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
else if (isLanguageMod(m)) {
|
|
309
|
+
languages.push(capitalize(sub.replace(/-/g, " ")));
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
// Player-added languages via DDB's "Custom Proficiency" UI live here.
|
|
313
|
+
for (const cp of arr(char.customProficiencies)) {
|
|
314
|
+
if (num(cp.type) !== CUSTOM_PROF_TYPE_LANGUAGE)
|
|
315
|
+
continue;
|
|
316
|
+
const name = str(cp.name).trim();
|
|
317
|
+
if (name)
|
|
318
|
+
languages.push(name);
|
|
319
|
+
}
|
|
320
|
+
// Language grants stored in char.characterValues (typeId 35). valueId is
|
|
321
|
+
// a stringified integer pointing into the rule-data language table.
|
|
322
|
+
for (const cv of arr(char.characterValues)) {
|
|
323
|
+
if (num(cv.typeId) !== CHAR_VALUE_TYPE_LANGUAGE)
|
|
324
|
+
continue;
|
|
325
|
+
if (num(cv.value) <= 0)
|
|
326
|
+
continue;
|
|
327
|
+
const id = parseInt(str(cv.valueId), 10);
|
|
328
|
+
if (!Number.isFinite(id) || id <= 0)
|
|
329
|
+
continue;
|
|
330
|
+
languages.push(LANGUAGE_NAMES_BY_ID[id] ?? `Language #${id}`);
|
|
331
|
+
}
|
|
332
|
+
return { armor, weapons, tools, languages };
|
|
333
|
+
}
|
|
334
|
+
// ── Public API ──────────────────────────────────────────────────────────────
|
|
335
|
+
export function computeStats(core) {
|
|
336
|
+
const { char, allMods, statMods, statTotals, profBonus, classes } = core;
|
|
337
|
+
const skillBonuses = computeSkillBonuses(allMods, statMods, profBonus, classes);
|
|
338
|
+
return {
|
|
339
|
+
abilityScoreDisplay: buildAbilityScoreDisplay(statTotals, statMods),
|
|
340
|
+
savingThrows: buildSavingThrows(allMods, statMods, profBonus),
|
|
341
|
+
skillLines: buildSkillLines(skillBonuses),
|
|
342
|
+
senses: computeSenses(char, allMods, skillBonuses),
|
|
343
|
+
proficiencies: computeProficiencies(char, allMods),
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
export function formatStatsBlock(s) {
|
|
347
|
+
const { proficiencies: p } = s;
|
|
348
|
+
return [
|
|
349
|
+
`ABILITY SCORES`,
|
|
350
|
+
` ${s.abilityScoreDisplay.join(" ")}`,
|
|
351
|
+
``,
|
|
352
|
+
`SAVING THROWS`,
|
|
353
|
+
` ${s.savingThrows.join(" ")}`,
|
|
354
|
+
` (* proficient)`,
|
|
355
|
+
``,
|
|
356
|
+
`SKILLS`,
|
|
357
|
+
...s.skillLines,
|
|
358
|
+
` (* proficient, ** expertise)`,
|
|
359
|
+
``,
|
|
360
|
+
`SENSES`,
|
|
361
|
+
` Passive Perception: ${s.senses.passivePerception} Passive Investigation: ${s.senses.passiveInvestigation} Passive Insight: ${s.senses.passiveInsight}`,
|
|
362
|
+
...(s.senses.special.length ? [` ${s.senses.special.join(", ")}`] : []),
|
|
363
|
+
``,
|
|
364
|
+
`PROFICIENCIES & TRAINING`,
|
|
365
|
+
` Armor: ${p.armor.length ? [...new Set(p.armor)].join(", ") : "None"}`,
|
|
366
|
+
` Weapons: ${p.weapons.length ? [...new Set(p.weapons)].join(", ") : "None"}`,
|
|
367
|
+
` Tools: ${p.tools.length ? [...new Set(p.tools)].join(", ") : "None"}`,
|
|
368
|
+
` Languages: ${p.languages.length ? [...new Set(p.languages)].join(", ") : "None"}`,
|
|
369
|
+
``,
|
|
370
|
+
];
|
|
371
|
+
}
|
|
372
|
+
//# sourceMappingURL=stats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stats.js","sourceRoot":"","sources":["../../../src/tools/character/stats.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template resolver for class/feat description strings.
|
|
3
|
+
*
|
|
4
|
+
* Phase 1 of the character.ts refactor — see docs/character-refactor.md.
|
|
5
|
+
*
|
|
6
|
+
* D&D Beyond descriptions embed templates like `{{proficiency}}`,
|
|
7
|
+
* `{{characterlevel*2#signed}}`, `{{level/3}}`. This factory takes the
|
|
8
|
+
* character's profBonus and totalLevel and returns a resolver that
|
|
9
|
+
* substitutes them.
|
|
10
|
+
*
|
|
11
|
+
* Supported variables:
|
|
12
|
+
* proficiency — the character's proficiency bonus
|
|
13
|
+
* level — total character level
|
|
14
|
+
* characterlevel — alias of `level`
|
|
15
|
+
* classlevel — `classLevel` arg, falls back to `totalLevel`
|
|
16
|
+
*
|
|
17
|
+
* Supported operators: `*`, `+`, `-`, `/` (integer divide).
|
|
18
|
+
* Supported modifiers (after `#`): `signed`, `unsigned`.
|
|
19
|
+
*/
|
|
20
|
+
export type TemplateResolver = (text: string, classLevel?: number) => string;
|
|
21
|
+
export declare function makeResolveTemplates(profBonus: number, totalLevel: number): TemplateResolver;
|
|
22
|
+
//# sourceMappingURL=templates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../../src/tools/character/templates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,MAAM,gBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;AAE7E,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,gBAAgB,CAgC5F"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template resolver for class/feat description strings.
|
|
3
|
+
*
|
|
4
|
+
* Phase 1 of the character.ts refactor — see docs/character-refactor.md.
|
|
5
|
+
*
|
|
6
|
+
* D&D Beyond descriptions embed templates like `{{proficiency}}`,
|
|
7
|
+
* `{{characterlevel*2#signed}}`, `{{level/3}}`. This factory takes the
|
|
8
|
+
* character's profBonus and totalLevel and returns a resolver that
|
|
9
|
+
* substitutes them.
|
|
10
|
+
*
|
|
11
|
+
* Supported variables:
|
|
12
|
+
* proficiency — the character's proficiency bonus
|
|
13
|
+
* level — total character level
|
|
14
|
+
* characterlevel — alias of `level`
|
|
15
|
+
* classlevel — `classLevel` arg, falls back to `totalLevel`
|
|
16
|
+
*
|
|
17
|
+
* Supported operators: `*`, `+`, `-`, `/` (integer divide).
|
|
18
|
+
* Supported modifiers (after `#`): `signed`, `unsigned`.
|
|
19
|
+
*/
|
|
20
|
+
export function makeResolveTemplates(profBonus, totalLevel) {
|
|
21
|
+
return (text, classLevel) => {
|
|
22
|
+
const vars = {
|
|
23
|
+
proficiency: profBonus,
|
|
24
|
+
level: totalLevel,
|
|
25
|
+
characterlevel: totalLevel,
|
|
26
|
+
classlevel: classLevel ?? totalLevel,
|
|
27
|
+
};
|
|
28
|
+
return text.replace(/\{\{([^}]+)\}\}/g, (_match, expr) => {
|
|
29
|
+
const [rawExpr, modifier] = expr.split("#");
|
|
30
|
+
const opMatch = rawExpr.match(/^(\w+)\s*([*+\-/])\s*(\d+(?:\.\d+)?)$/);
|
|
31
|
+
let value = null;
|
|
32
|
+
if (opMatch) {
|
|
33
|
+
const [, varName, op, numStr] = opMatch;
|
|
34
|
+
const base = vars[varName] ?? null;
|
|
35
|
+
const n = parseFloat(numStr);
|
|
36
|
+
if (base !== null) {
|
|
37
|
+
if (op === "*")
|
|
38
|
+
value = base * n;
|
|
39
|
+
else if (op === "+")
|
|
40
|
+
value = base + n;
|
|
41
|
+
else if (op === "-")
|
|
42
|
+
value = base - n;
|
|
43
|
+
else if (op === "/")
|
|
44
|
+
value = Math.floor(base / n);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
else if (vars[rawExpr] !== undefined) {
|
|
48
|
+
value = vars[rawExpr];
|
|
49
|
+
}
|
|
50
|
+
if (value === null)
|
|
51
|
+
return "?";
|
|
52
|
+
const rounded = Math.floor(value);
|
|
53
|
+
if (modifier === "signed")
|
|
54
|
+
return rounded >= 0 ? `+${rounded}` : `${rounded}`;
|
|
55
|
+
if (modifier === "unsigned")
|
|
56
|
+
return String(Math.max(0, rounded));
|
|
57
|
+
return String(rounded);
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=templates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../../../src/tools/character/templates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,MAAM,UAAU,oBAAoB,CAAC,SAAiB,EAAE,UAAkB;IACxE,OAAO,CAAC,IAAY,EAAE,UAAmB,EAAU,EAAE;QACnD,MAAM,IAAI,GAA2B;YACnC,WAAW,EAAE,SAAS;YACtB,KAAK,EAAE,UAAU;YACjB,cAAc,EAAE,UAAU;YAC1B,UAAU,EAAE,UAAU,IAAI,UAAU;SACrC,CAAC;QACF,OAAO,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,CAAC,MAAM,EAAE,IAAY,EAAE,EAAE;YAC/D,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAiC,CAAC;YAC5E,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAC;YACvE,IAAI,KAAK,GAAkB,IAAI,CAAC;YAChC,IAAI,OAAO,EAAE,CAAC;gBACZ,MAAM,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC;gBACxC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;gBACnC,MAAM,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;gBAC7B,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;oBAClB,IAAI,EAAE,KAAK,GAAG;wBAAE,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC;yBAC5B,IAAI,EAAE,KAAK,GAAG;wBAAE,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC;yBACjC,IAAI,EAAE,KAAK,GAAG;wBAAE,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC;yBACjC,IAAI,EAAE,KAAK,GAAG;wBAAE,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;gBACpD,CAAC;YACH,CAAC;iBAAM,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,SAAS,EAAE,CAAC;gBACvC,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;YACxB,CAAC;YACD,IAAI,KAAK,KAAK,IAAI;gBAAE,OAAO,GAAG,CAAC;YAC/B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAClC,IAAI,QAAQ,KAAK,QAAQ;gBAAE,OAAO,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,EAAE,CAAC,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC;YAC9E,IAAI,QAAQ,KAAK,UAAU;gBAAE,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;YACjE,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for the character module.
|
|
3
|
+
*
|
|
4
|
+
* Phase 1 of the character.ts refactor — see docs/character-refactor.md.
|
|
5
|
+
*
|
|
6
|
+
* `Mod`, `ClassEntry`, `InventoryItem`, `CharData` are all currently aliased
|
|
7
|
+
* to `Record<string, unknown>`. They will be tightened to proper shapes
|
|
8
|
+
* post-refactor (see follow-up A in the refactor doc) — the aliases exist so
|
|
9
|
+
* future tightening is a one-place change instead of a sweep.
|
|
10
|
+
*/
|
|
11
|
+
export type CharData = Record<string, unknown>;
|
|
12
|
+
export type Mod = Record<string, unknown>;
|
|
13
|
+
export type ClassEntry = Record<string, unknown>;
|
|
14
|
+
export type InventoryItem = Record<string, unknown>;
|
|
15
|
+
/** `type:"proficiency"` — skill/save/tool/weapon/armor/language proficiency grant. */
|
|
16
|
+
export interface ProficiencyMod {
|
|
17
|
+
type: "proficiency";
|
|
18
|
+
subType: string;
|
|
19
|
+
}
|
|
20
|
+
/** `type:"expertise"` — doubles proficiency bonus on the given skill/tool. */
|
|
21
|
+
export interface ExpertiseMod {
|
|
22
|
+
type: "expertise";
|
|
23
|
+
subType: string;
|
|
24
|
+
}
|
|
25
|
+
/** `type:"half-proficiency"` — half-prof (Jack of All Trades family). */
|
|
26
|
+
export interface HalfProficiencyMod {
|
|
27
|
+
type: "half-proficiency";
|
|
28
|
+
subType: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* `type:"bonus"` — additive numeric modifier. Most varied mod type in DDB:
|
|
32
|
+
* AC, skill bonuses, ability-score buffs, HP-per-level, initiative, speed,
|
|
33
|
+
* hit/damage. The value is in `value` OR `fixedValue` (use `??` —
|
|
34
|
+
* "whichever is present"), or computed from `statId` (e.g. Gloom Stalker
|
|
35
|
+
* Dread Ambusher adds WIS mod to initiative). `bonusTypes` indicates a
|
|
36
|
+
* dynamic value source (e.g. `[1]` = use the proficiency bonus).
|
|
37
|
+
* `isGranted: false` on a race mod means the player declined the ASI
|
|
38
|
+
* (2024 floating-ASI rule).
|
|
39
|
+
*/
|
|
40
|
+
export interface BonusMod {
|
|
41
|
+
type: "bonus";
|
|
42
|
+
subType: string;
|
|
43
|
+
value?: number | null;
|
|
44
|
+
fixedValue?: number | null;
|
|
45
|
+
statId?: number | null;
|
|
46
|
+
isGranted?: boolean;
|
|
47
|
+
bonusTypes?: number[];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* `type:"set"` — replaces the base value (e.g. Belt of Giant Strength sets STR
|
|
51
|
+
* to 23, Boots of Striding and Springing set walking speed to 40, Draconic
|
|
52
|
+
* Resilience sets unarmored AC base via `unarmored-armor-class`).
|
|
53
|
+
* `componentId` discriminates dual-encoded set+sense pairs from same-source
|
|
54
|
+
* extensions in the sense calculation.
|
|
55
|
+
*/
|
|
56
|
+
export interface SetMod {
|
|
57
|
+
type: "set";
|
|
58
|
+
subType: string;
|
|
59
|
+
value?: number | null;
|
|
60
|
+
fixedValue?: number | null;
|
|
61
|
+
statId?: number | null;
|
|
62
|
+
componentId?: number;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* `type:"set-base"` — baseline-establishing set, used by senses (e.g. race
|
|
66
|
+
* darkvision baseline 60 ft.). Functionally identical to `set` for the
|
|
67
|
+
* baseline pass; the distinct type exists because DDB sometimes emits both.
|
|
68
|
+
*/
|
|
69
|
+
export interface SetBaseMod {
|
|
70
|
+
type: "set-base";
|
|
71
|
+
subType: string;
|
|
72
|
+
value?: number | null;
|
|
73
|
+
fixedValue?: number | null;
|
|
74
|
+
componentId?: number;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* `type:"sense"` — grants or extends a sense (Darkvision, Blindsight, etc.).
|
|
78
|
+
* Pass 2 of the sense calculation uses `componentId` to decide whether the mod
|
|
79
|
+
* is a dual encoding of the baseline (take max) or an extension from a
|
|
80
|
+
* different source (additive — Gloom Stalker Umbral Sight +30 ft on race
|
|
81
|
+
* darkvision 60 ft).
|
|
82
|
+
*/
|
|
83
|
+
export interface SenseMod {
|
|
84
|
+
type: "sense";
|
|
85
|
+
subType: string;
|
|
86
|
+
value?: number | null;
|
|
87
|
+
componentId?: number;
|
|
88
|
+
}
|
|
89
|
+
/** `type:"resistance"` — damage resistance grant (e.g. "fire", "cold"). */
|
|
90
|
+
export interface ResistanceMod {
|
|
91
|
+
type: "resistance";
|
|
92
|
+
subType: string;
|
|
93
|
+
}
|
|
94
|
+
/** `type:"immunity"` — damage immunity grant (e.g. "poison", "psychic"). */
|
|
95
|
+
export interface ImmunityMod {
|
|
96
|
+
type: "immunity";
|
|
97
|
+
subType: string;
|
|
98
|
+
}
|
|
99
|
+
/** `type:"vulnerability"` — damage vulnerability grant. */
|
|
100
|
+
export interface VulnerabilityMod {
|
|
101
|
+
type: "vulnerability";
|
|
102
|
+
subType: string;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* `type:"language"` — language proficiency grant. The standard race/class/feat
|
|
106
|
+
* source for languages. The other two storage mechanisms (custom proficiencies
|
|
107
|
+
* with `type: 3`, and `characterValues` entries with `typeId: 35`) live
|
|
108
|
+
* outside `modifiers` and don't use this interface.
|
|
109
|
+
*/
|
|
110
|
+
export interface LanguageMod {
|
|
111
|
+
type: "language";
|
|
112
|
+
subType: string;
|
|
113
|
+
}
|
|
114
|
+
export type ParseSection = "summary" | "combat" | "spells" | "inventory" | "features" | "concentration" | "notes" | "full";
|
|
115
|
+
/**
|
|
116
|
+
* The shared context object passed to every per-domain computer module.
|
|
117
|
+
*
|
|
118
|
+
* Computed once by `computeCoreStats(char)` (Phase 2) and threaded through
|
|
119
|
+
* vitals/stats/spells/etc. Avoids each module re-deriving statMods,
|
|
120
|
+
* profBonus, allMods, etc.
|
|
121
|
+
*/
|
|
122
|
+
export interface CoreStats {
|
|
123
|
+
readonly char: CharData;
|
|
124
|
+
readonly allMods: readonly Mod[];
|
|
125
|
+
readonly statTotals: readonly number[];
|
|
126
|
+
readonly statMods: readonly number[];
|
|
127
|
+
readonly profBonus: number;
|
|
128
|
+
readonly totalLevel: number;
|
|
129
|
+
readonly classes: readonly ClassEntry[];
|
|
130
|
+
readonly inventory: readonly InventoryItem[];
|
|
131
|
+
readonly resolveTemplates: (text: string, classLevel?: number) => string;
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/tools/character/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for the character module.
|
|
3
|
+
*
|
|
4
|
+
* Phase 1 of the character.ts refactor — see docs/character-refactor.md.
|
|
5
|
+
*
|
|
6
|
+
* `Mod`, `ClassEntry`, `InventoryItem`, `CharData` are all currently aliased
|
|
7
|
+
* to `Record<string, unknown>`. They will be tightened to proper shapes
|
|
8
|
+
* post-refactor (see follow-up A in the refactor doc) — the aliases exist so
|
|
9
|
+
* future tightening is a one-place change instead of a sweep.
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=types.js.map
|