@archivist-gg/dnd5e 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 (322) hide show
  1. package/LICENSE +21 -0
  2. package/LICENSES/SRD.md +37 -0
  3. package/README.md +65 -0
  4. package/dist/armor/armor.codec.d.ts +10 -0
  5. package/dist/armor/armor.codec.js +8 -0
  6. package/dist/armor/armor.entity-type.d.ts +2 -0
  7. package/dist/armor/armor.parser.d.ts +3 -0
  8. package/dist/armor/armor.parser.js +7 -0
  9. package/dist/armor/armor.schema.d.ts +25 -0
  10. package/dist/armor/armor.schema.js +6 -0
  11. package/dist/armor/armor.types.d.ts +26 -0
  12. package/dist/armor/armor.types.js +0 -0
  13. package/dist/background/background.codec.d.ts +9 -0
  14. package/dist/background/background.codec.js +11 -0
  15. package/dist/background/background.entity-type.d.ts +2 -0
  16. package/dist/background/background.parser.d.ts +3 -0
  17. package/dist/background/background.parser.js +10 -0
  18. package/dist/background/background.schema.d.ts +74 -0
  19. package/dist/background/background.schema.js +9 -0
  20. package/dist/background/background.types.d.ts +52 -0
  21. package/dist/background/background.types.js +0 -0
  22. package/dist/chunk-24GFQU7T.js +18 -0
  23. package/dist/chunk-3DZ74AKK.js +28 -0
  24. package/dist/chunk-3TM34RSJ.js +23 -0
  25. package/dist/chunk-3TRGRSCH.js +8 -0
  26. package/dist/chunk-4C2KOKLF.js +185 -0
  27. package/dist/chunk-4F6AIZ36.js +19 -0
  28. package/dist/chunk-4SSDYJS2.js +86 -0
  29. package/dist/chunk-4TIJKN6O.js +37 -0
  30. package/dist/chunk-537BHVSH.js +18 -0
  31. package/dist/chunk-5INR4NSL.js +18 -0
  32. package/dist/chunk-5XUXXAQJ.js +30 -0
  33. package/dist/chunk-656QCX4U.js +22 -0
  34. package/dist/chunk-66XXW2Z2.js +133 -0
  35. package/dist/chunk-6NK2MUD3.js +114 -0
  36. package/dist/chunk-6PXE7T7C.js +79 -0
  37. package/dist/chunk-7P65Q426.js +158 -0
  38. package/dist/chunk-A7T5L7SS.js +83 -0
  39. package/dist/chunk-AFKTJXRA.js +39 -0
  40. package/dist/chunk-B7UXKNLD.js +46 -0
  41. package/dist/chunk-BAXWWGFJ.js +45 -0
  42. package/dist/chunk-BH6AMZVY.js +25 -0
  43. package/dist/chunk-CDW7R2QT.js +62 -0
  44. package/dist/chunk-DABBCYTT.js +68 -0
  45. package/dist/chunk-DC74TZNA.js +46 -0
  46. package/dist/chunk-DNAJRMNN.js +68 -0
  47. package/dist/chunk-DRNZJNJP.js +19 -0
  48. package/dist/chunk-FEOB24KP.js +18 -0
  49. package/dist/chunk-FFULAF2P.js +63 -0
  50. package/dist/chunk-FUK7QSN5.js +19 -0
  51. package/dist/chunk-GIOH5JB5.js +19 -0
  52. package/dist/chunk-GJOI2GHP.js +25 -0
  53. package/dist/chunk-GZ46OVGZ.js +31 -0
  54. package/dist/chunk-HCP3GMHS.js +44 -0
  55. package/dist/chunk-IDTDACM6.js +19 -0
  56. package/dist/chunk-IKSMX4TN.js +0 -0
  57. package/dist/chunk-IXZXFFFW.js +269 -0
  58. package/dist/chunk-J2RYSZMQ.js +16 -0
  59. package/dist/chunk-JEYQU3JT.js +36 -0
  60. package/dist/chunk-LSLSG67V.js +31 -0
  61. package/dist/chunk-LWFTHJIE.js +24 -0
  62. package/dist/chunk-MDEJHMKK.js +52 -0
  63. package/dist/chunk-N2JE7BWL.js +81 -0
  64. package/dist/chunk-NAEPSZAM.js +88 -0
  65. package/dist/chunk-NXR3BD2V.js +90 -0
  66. package/dist/chunk-PKC77VZP.js +18 -0
  67. package/dist/chunk-PMADYMUI.js +19 -0
  68. package/dist/chunk-PTXBIDG7.js +63 -0
  69. package/dist/chunk-PZOWBI3B.js +49 -0
  70. package/dist/chunk-QQ5W2AZP.js +515 -0
  71. package/dist/chunk-QS5L5YTJ.js +24 -0
  72. package/dist/chunk-QYZV2MQA.js +97 -0
  73. package/dist/chunk-R2ZV233X.js +233 -0
  74. package/dist/chunk-RAWJTA45.js +271 -0
  75. package/dist/chunk-RSDRMH54.js +18 -0
  76. package/dist/chunk-RVYYKYFL.js +18 -0
  77. package/dist/chunk-SSXBYF3P.js +18 -0
  78. package/dist/chunk-TJCNX6UZ.js +521 -0
  79. package/dist/chunk-TZK36BU7.js +38 -0
  80. package/dist/chunk-UC4FE5X3.js +65 -0
  81. package/dist/chunk-WP2CX2JO.js +235 -0
  82. package/dist/chunk-WYGD2UGN.js +14 -0
  83. package/dist/chunk-XJNECUPT.js +31 -0
  84. package/dist/chunk-XURS6Q6Q.js +127 -0
  85. package/dist/chunk-Y4I74YEG.js +33 -0
  86. package/dist/chunk-Z4TL2V7S.js +18 -0
  87. package/dist/chunk-Z6GXV2GO.js +121 -0
  88. package/dist/chunk-ZLG6FQFB.js +237 -0
  89. package/dist/chunk-ZMR77DDL.js +18 -0
  90. package/dist/class/class.codec.d.ts +10 -0
  91. package/dist/class/class.codec.js +15 -0
  92. package/dist/class/class.entity-type.d.ts +2 -0
  93. package/dist/class/class.parser.d.ts +3 -0
  94. package/dist/class/class.parser.js +14 -0
  95. package/dist/class/class.schema.d.ts +202 -0
  96. package/dist/class/class.schema.js +13 -0
  97. package/dist/class/class.slug.d.ts +11 -0
  98. package/dist/class/class.slug.js +8 -0
  99. package/dist/class/class.types.d.ts +74 -0
  100. package/dist/class/class.types.js +0 -0
  101. package/dist/data-srd/index.generated.d.ts +7 -0
  102. package/dist/data-srd/index.generated.js +146 -0
  103. package/dist/dnd/constants.d.ts +16 -0
  104. package/dist/dnd/constants.js +36 -0
  105. package/dist/dnd/formula-tags.d.ts +16 -0
  106. package/dist/dnd/formula-tags.js +119 -0
  107. package/dist/dnd/math.d.ts +24 -0
  108. package/dist/dnd/math.js +35 -0
  109. package/dist/dnd/prose-tags.d.ts +16 -0
  110. package/dist/dnd/prose-tags.js +18 -0
  111. package/dist/dnd/resource-die.d.ts +6 -0
  112. package/dist/dnd/resource-die.js +17 -0
  113. package/dist/dnd/resource-formula.d.ts +41 -0
  114. package/dist/dnd/resource-formula.js +159 -0
  115. package/dist/dnd/srd-tag-converter.d.ts +44 -0
  116. package/dist/dnd/srd-tag-converter.js +12 -0
  117. package/dist/dnd/tag-grammar.d.ts +19 -0
  118. package/dist/dnd/tag-grammar.js +12 -0
  119. package/dist/encounter/encounter.ai-schema.d.ts +29 -0
  120. package/dist/encounter/encounter.ai-schema.js +8 -0
  121. package/dist/encounter/encounter.entity-type.d.ts +2 -0
  122. package/dist/encounter/encounter.generatable.d.ts +2 -0
  123. package/dist/entities/base-item-resolver.d.ts +21 -0
  124. package/dist/entities/base-item-resolver.js +9 -0
  125. package/dist/entities/slug.d.ts +5 -0
  126. package/dist/entities/slug.js +6 -0
  127. package/dist/feat/feat.codec.d.ts +9 -0
  128. package/dist/feat/feat.codec.js +10 -0
  129. package/dist/feat/feat.entity-type.d.ts +2 -0
  130. package/dist/feat/feat.parser.d.ts +3 -0
  131. package/dist/feat/feat.parser.js +9 -0
  132. package/dist/feat/feat.schema.d.ts +194 -0
  133. package/dist/feat/feat.schema.js +8 -0
  134. package/dist/feat/feat.types.d.ts +42 -0
  135. package/dist/feat/feat.types.js +0 -0
  136. package/dist/index.d.ts +12 -0
  137. package/dist/index.js +139149 -0
  138. package/dist/inline-tag-parser.d.ts +11 -0
  139. package/dist/inline-tag-parser.js +6 -0
  140. package/dist/item/item.actions-map.d.ts +41 -0
  141. package/dist/item/item.actions-map.js +85 -0
  142. package/dist/item/item.ai-schema.d.ts +35 -0
  143. package/dist/item/item.ai-schema.js +6 -0
  144. package/dist/item/item.attunement.d.ts +18 -0
  145. package/dist/item/item.attunement.js +12 -0
  146. package/dist/item/item.bonuses.d.ts +2 -0
  147. package/dist/item/item.bonuses.js +7 -0
  148. package/dist/item/item.codec.d.ts +3 -0
  149. package/dist/item/item.codec.js +8 -0
  150. package/dist/item/item.conditions.d.ts +10 -0
  151. package/dist/item/item.conditions.js +8 -0
  152. package/dist/item/item.conditions.types.d.ts +28 -0
  153. package/dist/item/item.conditions.types.js +0 -0
  154. package/dist/item/item.enrichment.d.ts +4 -0
  155. package/dist/item/item.enrichment.js +9 -0
  156. package/dist/item/item.entity-type.d.ts +2 -0
  157. package/dist/item/item.generatable.d.ts +2 -0
  158. package/dist/item/item.parser.d.ts +3 -0
  159. package/dist/item/item.parser.js +7 -0
  160. package/dist/item/item.schema.d.ts +174 -0
  161. package/dist/item/item.schema.js +8 -0
  162. package/dist/item/item.types.d.ts +108 -0
  163. package/dist/item/item.types.js +0 -0
  164. package/dist/monster/monster.ai-schema.d.ts +67 -0
  165. package/dist/monster/monster.ai-schema.js +6 -0
  166. package/dist/monster/monster.codec.d.ts +13 -0
  167. package/dist/monster/monster.enrichment.d.ts +10 -0
  168. package/dist/monster/monster.enrichment.js +19 -0
  169. package/dist/monster/monster.entity-type.d.ts +2 -0
  170. package/dist/monster/monster.generatable.d.ts +2 -0
  171. package/dist/monster/monster.parser.d.ts +3 -0
  172. package/dist/monster/monster.parser.js +6 -0
  173. package/dist/monster/monster.resolve.d.ts +10 -0
  174. package/dist/monster/monster.types.d.ts +29 -0
  175. package/dist/monster/monster.types.js +1 -0
  176. package/dist/npc/npc.ai-schema.d.ts +6 -0
  177. package/dist/npc/npc.entity-type.d.ts +2 -0
  178. package/dist/npc/npc.generatable.d.ts +2 -0
  179. package/dist/optional-feature/optional-feature.codec.d.ts +14 -0
  180. package/dist/optional-feature/optional-feature.codec.js +11 -0
  181. package/dist/optional-feature/optional-feature.entity-type.d.ts +2 -0
  182. package/dist/optional-feature/optional-feature.parser.d.ts +3 -0
  183. package/dist/optional-feature/optional-feature.parser.js +10 -0
  184. package/dist/optional-feature/optional-feature.schema.d.ts +236 -0
  185. package/dist/optional-feature/optional-feature.schema.js +9 -0
  186. package/dist/pack.d.ts +11 -0
  187. package/dist/pc/conditions.constants.d.ts +3 -0
  188. package/dist/pc/conditions.constants.js +8 -0
  189. package/dist/pc/decision-recognizer.d.ts +4 -0
  190. package/dist/pc/decision-recognizer.js +8 -0
  191. package/dist/pc/pc.conditions.d.ts +3 -0
  192. package/dist/pc/pc.conditions.js +6 -0
  193. package/dist/pc/pc.decision-engine.d.ts +97 -0
  194. package/dist/pc/pc.decision-engine.js +19 -0
  195. package/dist/pc/pc.equipment.d.ts +12 -0
  196. package/dist/pc/pc.equipment.js +15 -0
  197. package/dist/pc/pc.feature-effects.d.ts +95 -0
  198. package/dist/pc/pc.feature-effects.js +9 -0
  199. package/dist/pc/pc.formula-context.d.ts +4 -0
  200. package/dist/pc/pc.formula-context.js +11 -0
  201. package/dist/pc/pc.parser.d.ts +3 -0
  202. package/dist/pc/pc.parser.js +41 -0
  203. package/dist/pc/pc.pools.d.ts +12 -0
  204. package/dist/pc/pc.pools.js +15 -0
  205. package/dist/pc/pc.proficiencies.d.ts +8 -0
  206. package/dist/pc/pc.proficiencies.js +47 -0
  207. package/dist/pc/pc.proficiency-query.d.ts +11 -0
  208. package/dist/pc/pc.proficiency-query.js +17 -0
  209. package/dist/pc/pc.recalc.d.ts +105 -0
  210. package/dist/pc/pc.recalc.js +662 -0
  211. package/dist/pc/pc.resolver.d.ts +34 -0
  212. package/dist/pc/pc.resolver.js +267 -0
  213. package/dist/pc/pc.resource-seed.d.ts +10 -0
  214. package/dist/pc/pc.resource-seed.js +40 -0
  215. package/dist/pc/pc.rest.d.ts +23 -0
  216. package/dist/pc/pc.rest.js +128 -0
  217. package/dist/pc/pc.schema.d.ts +294 -0
  218. package/dist/pc/pc.schema.js +7 -0
  219. package/dist/pc/pc.slotting.d.ts +22 -0
  220. package/dist/pc/pc.slotting.js +20 -0
  221. package/dist/pc/pc.spellcasting.d.ts +56 -0
  222. package/dist/pc/pc.spellcasting.js +16 -0
  223. package/dist/pc/pc.table-column.d.ts +12 -0
  224. package/dist/pc/pc.table-column.js +8 -0
  225. package/dist/pc/pc.types.d.ts +514 -0
  226. package/dist/pc/pc.types.js +0 -0
  227. package/dist/race/race.codec.d.ts +9 -0
  228. package/dist/race/race.codec.js +13 -0
  229. package/dist/race/race.entity-type.d.ts +2 -0
  230. package/dist/race/race.parser.d.ts +3 -0
  231. package/dist/race/race.parser.js +12 -0
  232. package/dist/race/race.schema.d.ts +67 -0
  233. package/dist/race/race.schema.js +11 -0
  234. package/dist/race/race.types.d.ts +44 -0
  235. package/dist/race/race.types.js +0 -0
  236. package/dist/schemas/attack-schema.d.ts +38 -0
  237. package/dist/schemas/choice-schema.d.ts +5 -0
  238. package/dist/schemas/choice-schema.js +7 -0
  239. package/dist/schemas/duration-schema.d.ts +13 -0
  240. package/dist/schemas/duration-schema.js +6 -0
  241. package/dist/schemas/equipment-grant-schema.d.ts +6 -0
  242. package/dist/schemas/equipment-grant-schema.js +12 -0
  243. package/dist/schemas/feature-effect-schema.d.ts +131 -0
  244. package/dist/schemas/feature-effect-schema.js +6 -0
  245. package/dist/schemas/feature-schema.d.ts +2 -0
  246. package/dist/schemas/feature-schema.js +10 -0
  247. package/dist/schemas/index.d.ts +6 -0
  248. package/dist/schemas/resource-schema.d.ts +95 -0
  249. package/dist/schemas/resource-schema.js +12 -0
  250. package/dist/schemas/selection-pool-schema.d.ts +34 -0
  251. package/dist/schemas/selection-pool-schema.js +10 -0
  252. package/dist/spell/spell.access.d.ts +15 -0
  253. package/dist/spell/spell.access.js +25 -0
  254. package/dist/spell/spell.ai-schema.d.ts +24 -0
  255. package/dist/spell/spell.ai-schema.js +6 -0
  256. package/dist/spell/spell.codec.d.ts +3 -0
  257. package/dist/spell/spell.codec.js +8 -0
  258. package/dist/spell/spell.enrichment.d.ts +2 -0
  259. package/dist/spell/spell.enrichment.js +9 -0
  260. package/dist/spell/spell.entity-type.d.ts +2 -0
  261. package/dist/spell/spell.filter.d.ts +15 -0
  262. package/dist/spell/spell.filter.js +87 -0
  263. package/dist/spell/spell.generatable.d.ts +2 -0
  264. package/dist/spell/spell.parser.d.ts +3 -0
  265. package/dist/spell/spell.parser.js +7 -0
  266. package/dist/spell/spell.scaling.d.ts +14 -0
  267. package/dist/spell/spell.scaling.js +28 -0
  268. package/dist/spell/spell.schema.d.ts +31 -0
  269. package/dist/spell/spell.schema.js +6 -0
  270. package/dist/spell/spell.types.d.ts +25 -0
  271. package/dist/spell/spell.types.js +0 -0
  272. package/dist/srd-store.d.ts +53 -0
  273. package/dist/subclass/subclass.codec.d.ts +11 -0
  274. package/dist/subclass/subclass.codec.js +14 -0
  275. package/dist/subclass/subclass.entity-type.d.ts +2 -0
  276. package/dist/subclass/subclass.parser.d.ts +3 -0
  277. package/dist/subclass/subclass.parser.js +13 -0
  278. package/dist/subclass/subclass.schema.d.ts +118 -0
  279. package/dist/subclass/subclass.schema.js +12 -0
  280. package/dist/subclass/subclass.types.d.ts +20 -0
  281. package/dist/subclass/subclass.types.js +0 -0
  282. package/dist/types/abilities.d.ts +8 -0
  283. package/dist/types/abilities.js +0 -0
  284. package/dist/types/ac.d.ts +5 -0
  285. package/dist/types/attack.d.ts +22 -0
  286. package/dist/types/attack.js +0 -0
  287. package/dist/types/casting-option.d.ts +14 -0
  288. package/dist/types/casting-option.js +0 -0
  289. package/dist/types/choice.d.ts +66 -0
  290. package/dist/types/choice.js +6 -0
  291. package/dist/types/edition.d.ts +4 -0
  292. package/dist/types/edition.js +0 -0
  293. package/dist/types/equipment-grant.d.ts +41 -0
  294. package/dist/types/equipment-grant.js +0 -0
  295. package/dist/types/feature-effect.d.ts +79 -0
  296. package/dist/types/feature-effect.js +0 -0
  297. package/dist/types/feature.d.ts +41 -0
  298. package/dist/types/feature.js +0 -0
  299. package/dist/types/formula-context.d.ts +7 -0
  300. package/dist/types/formula-context.js +0 -0
  301. package/dist/types/hp.d.ts +4 -0
  302. package/dist/types/index.d.ts +10 -0
  303. package/dist/types/item-conditions.types.d.ts +63 -0
  304. package/dist/types/item-conditions.types.js +0 -0
  305. package/dist/types/optional-feature.types.d.ts +60 -0
  306. package/dist/types/optional-feature.types.js +0 -0
  307. package/dist/types/resource.d.ts +44 -0
  308. package/dist/types/resource.js +0 -0
  309. package/dist/types/selection-pool.d.ts +37 -0
  310. package/dist/types/selection-pool.js +0 -0
  311. package/dist/types/speed.d.ts +8 -0
  312. package/dist/weapon/weapon.codec.d.ts +12 -0
  313. package/dist/weapon/weapon.codec.js +8 -0
  314. package/dist/weapon/weapon.entity-type.d.ts +2 -0
  315. package/dist/weapon/weapon.parser.d.ts +3 -0
  316. package/dist/weapon/weapon.parser.js +7 -0
  317. package/dist/weapon/weapon.schema.d.ts +34 -0
  318. package/dist/weapon/weapon.schema.js +6 -0
  319. package/dist/weapon/weapon.types.d.ts +36 -0
  320. package/dist/weapon/weapon.types.js +0 -0
  321. package/package.json +32 -0
  322. package/src/data-srd/ATTRIBUTION.md +2 -0
@@ -0,0 +1,662 @@
1
+ import {
2
+ computeSpellLimits,
3
+ deriveSpellSlots,
4
+ resolveSpellcasting
5
+ } from "../chunk-IXZXFFFW.js";
6
+ import "../chunk-5XUXXAQJ.js";
7
+ import {
8
+ computeConditionEffects
9
+ } from "../chunk-R2ZV233X.js";
10
+ import {
11
+ collectChosenAbilityPoints,
12
+ collectChosenProficiencies
13
+ } from "../chunk-TJCNX6UZ.js";
14
+ import "../chunk-BH6AMZVY.js";
15
+ import "../chunk-JEYQU3JT.js";
16
+ import {
17
+ computeAppliedBonuses,
18
+ computeSlotsAndAttacks,
19
+ emptyAppliedBonuses
20
+ } from "../chunk-QQ5W2AZP.js";
21
+ import "../chunk-UC4FE5X3.js";
22
+ import {
23
+ computeFeatureEffects
24
+ } from "../chunk-XURS6Q6Q.js";
25
+ import "../chunk-LWFTHJIE.js";
26
+ import "../chunk-DNAJRMNN.js";
27
+ import "../chunk-DC74TZNA.js";
28
+ import "../chunk-3TRGRSCH.js";
29
+ import "../chunk-J2RYSZMQ.js";
30
+ import {
31
+ abilityModifier,
32
+ attackBonus,
33
+ passive,
34
+ passivePerception,
35
+ proficiencyFromLevel,
36
+ saveDC,
37
+ savingThrow,
38
+ skillBonus
39
+ } from "../chunk-NAEPSZAM.js";
40
+ import {
41
+ ABILITY_KEYS,
42
+ ALL_SKILLS,
43
+ SKILL_ABILITY
44
+ } from "../chunk-WP2CX2JO.js";
45
+
46
+ // src/pc/pc.recalc.ts
47
+ function phbAverageForDie(hitDieStr) {
48
+ const n = parseDieSize(hitDieStr);
49
+ if (n == null) return 4;
50
+ return Math.floor(n / 2) + 1;
51
+ }
52
+ function parseDieSize(die) {
53
+ if (die == null) return null;
54
+ if (typeof die === "number") return die;
55
+ const m = die.match(/^d?(\d+)$/i);
56
+ return m ? parseInt(m[1], 10) : null;
57
+ }
58
+ function skillSlugFromDisplay(display) {
59
+ return display.toLowerCase().replace(/\s+/g, "-");
60
+ }
61
+ function skillSlugToAbilityLookup(slug) {
62
+ return slug.replace(/-/g, " ");
63
+ }
64
+ function flattenRaceAsi(race) {
65
+ var _a;
66
+ const out = {};
67
+ if (!(race == null ? void 0 : race.ability_score_increases)) return out;
68
+ for (const asi of race.ability_score_increases) {
69
+ if ("ability" in asi) {
70
+ out[asi.ability] = ((_a = out[asi.ability]) != null ? _a : 0) + asi.amount;
71
+ }
72
+ }
73
+ return out;
74
+ }
75
+ function subraceAsi(resolved) {
76
+ var _a, _b, _c, _d, _e;
77
+ const subSlug = (_b = (_a = resolved.definition.subrace) == null ? void 0 : _a.replace(/\[\[|\]\]/g, "")) != null ? _b : null;
78
+ if (!subSlug) return [];
79
+ const subraces = (_d = (_c = resolved.race) == null ? void 0 : _c.subraces) != null ? _d : [];
80
+ const sub = subraces.find((s) => s.slug === subSlug);
81
+ return (_e = sub == null ? void 0 : sub.ability_score_increases) != null ? _e : [];
82
+ }
83
+ function collectClassAsiBranch(resolved) {
84
+ var _a;
85
+ const out = {};
86
+ for (const c of resolved.classes) {
87
+ for (const [, choice] of Object.entries(c.choices)) {
88
+ const asi = choice == null ? void 0 : choice.asi;
89
+ if (!asi) continue;
90
+ for (const ab of ABILITY_KEYS) {
91
+ const v = asi[ab];
92
+ if (typeof v === "number") out[ab] = ((_a = out[ab]) != null ? _a : 0) + v;
93
+ }
94
+ }
95
+ }
96
+ return out;
97
+ }
98
+ function abilityBonusBreakdown(resolved) {
99
+ var _a, _b, _c, _d, _e, _f;
100
+ const race = flattenRaceAsi(resolved.race);
101
+ const origin = collectChosenAbilityPoints(resolved);
102
+ const sub = subraceAsi(resolved);
103
+ const classAsi = collectClassAsiBranch(resolved);
104
+ const featPoints = collectClassFeatAbilityPoints(resolved);
105
+ const out = {};
106
+ for (const ab of ABILITY_KEYS) {
107
+ let species = ((_a = race[ab]) != null ? _a : 0) + ((_b = origin.race[ab]) != null ? _b : 0);
108
+ for (const asi of sub) {
109
+ if (asi.ability === ab && typeof asi.amount === "number") species += asi.amount;
110
+ }
111
+ let feat = (_c = featPoints[ab]) != null ? _c : 0;
112
+ for (const f of resolved.feats) {
113
+ const bonus = (_d = f.ability_bonuses) == null ? void 0 : _d[ab];
114
+ if (typeof bonus === "number") feat += bonus;
115
+ }
116
+ out[ab] = { species, background: (_e = origin.background[ab]) != null ? _e : 0, class: (_f = classAsi[ab]) != null ? _f : 0, feat };
117
+ }
118
+ return out;
119
+ }
120
+ function multiclassMaxHP(classes, conMod) {
121
+ var _a;
122
+ let total = 0;
123
+ let firstLevelCounted = false;
124
+ for (const c of classes) {
125
+ const die = parseDieSize((_a = c.entity) == null ? void 0 : _a.hit_die);
126
+ if (!c.entity || die == null || c.level < 1) continue;
127
+ for (let lvl = 1; lvl <= c.level; lvl++) {
128
+ if (!firstLevelCounted) {
129
+ total += die + conMod;
130
+ firstLevelCounted = true;
131
+ } else {
132
+ total += Math.floor(die / 2) + 1 + conMod;
133
+ }
134
+ }
135
+ }
136
+ return Math.max(1, total);
137
+ }
138
+ function unarmoredAC(resolved, mods, warnings) {
139
+ return unarmoredACBreakdown(resolved, mods, warnings).total;
140
+ }
141
+ function unarmoredACBreakdown(resolved, mods, warnings) {
142
+ var _a, _b, _c, _d, _e, _f;
143
+ const baseTerms = [
144
+ { source: "Unarmored", amount: 10, kind: "unarmored" },
145
+ { source: "DEX modifier", amount: mods.dex, kind: "dex" }
146
+ ];
147
+ for (const rf of resolved.features) {
148
+ const eff = ((_a = rf.feature.effects) != null ? _a : []).find((e) => e.kind === "unarmored-ac");
149
+ if (eff && eff.kind === "unarmored-ac") {
150
+ const base = (_b = eff.base) != null ? _b : 10;
151
+ const terms = [
152
+ { source: "Unarmored", amount: base, kind: "unarmored" },
153
+ { source: "DEX modifier", amount: mods.dex, kind: "dex" }
154
+ ];
155
+ let total = base + mods.dex;
156
+ for (const ab of eff.abilities) {
157
+ if (ab === "dex") continue;
158
+ terms.push({ source: `${ab.toUpperCase()} modifier (${(_c = rf.feature.name) != null ? _c : "Unarmored"})`, amount: mods[ab], kind: "ability" });
159
+ total += mods[ab];
160
+ }
161
+ return { total, terms };
162
+ }
163
+ }
164
+ for (const rf of resolved.features) {
165
+ const feat = rf.feature;
166
+ if ((_d = feat.unarmored_defense) == null ? void 0 : _d.ability) {
167
+ const ab = feat.unarmored_defense.ability;
168
+ const amt = (_e = mods[ab]) != null ? _e : 0;
169
+ const terms = [
170
+ ...baseTerms,
171
+ { source: `${ab.toUpperCase()} modifier (Unarmored Defense)`, amount: amt, kind: "ability" }
172
+ ];
173
+ return { total: 10 + mods.dex + amt, terms };
174
+ }
175
+ }
176
+ for (const rf of resolved.features) {
177
+ const name = ((_f = rf.feature.name) != null ? _f : "").toLowerCase();
178
+ if (!/unarmored\s*defense/.test(name)) continue;
179
+ if (rf.source.kind === "class" && rf.source.slug.includes("monk")) {
180
+ warnings.push(`Unarmored Defense detected by name on ${rf.source.slug}; populate unarmored_defense:{ability:wis} flag for accuracy.`);
181
+ const terms = [
182
+ ...baseTerms,
183
+ { source: "WIS modifier (Unarmored Defense)", amount: mods.wis, kind: "ability" }
184
+ ];
185
+ return { total: 10 + mods.dex + mods.wis, terms };
186
+ }
187
+ if (rf.source.kind === "class" && rf.source.slug.includes("barbarian")) {
188
+ warnings.push(`Unarmored Defense detected by name on ${rf.source.slug}; populate unarmored_defense:{ability:con} flag for accuracy.`);
189
+ const terms = [
190
+ ...baseTerms,
191
+ { source: "CON modifier (Unarmored Defense)", amount: mods.con, kind: "ability" }
192
+ ];
193
+ return { total: 10 + mods.dex + mods.con, terms };
194
+ }
195
+ }
196
+ return { total: 10 + mods.dex, terms: baseTerms };
197
+ }
198
+ function initiativeBonus(dexMod, feats, edition) {
199
+ let bonus = dexMod;
200
+ for (const f of feats) {
201
+ if (f.slug === "alert" && edition === "2014") bonus += 5;
202
+ const initBonus = f.initiative_bonus;
203
+ if (typeof initBonus === "number") bonus += initBonus;
204
+ }
205
+ return bonus;
206
+ }
207
+ function speedFromRace(resolved) {
208
+ var _a, _b, _c;
209
+ const base = (_c = (_b = (_a = resolved.race) == null ? void 0 : _a.speed) == null ? void 0 : _b.walk) != null ? _c : 30;
210
+ let extra = 0;
211
+ for (const f of resolved.feats) {
212
+ const bonus = f.speed_bonus;
213
+ if (typeof bonus === "number") extra += bonus;
214
+ }
215
+ return base + extra;
216
+ }
217
+ function collectClassFeatAbilityPoints(resolved) {
218
+ var _a, _b;
219
+ const out = {};
220
+ const stripRef = (ref) => ref.replace(/^\[\[/, "").replace(/\]\]$/, "");
221
+ const featBySlug = /* @__PURE__ */ new Map();
222
+ for (const f of resolved.feats) featBySlug.set(f.slug, f);
223
+ for (const c of resolved.classes) {
224
+ for (const atLevel of Object.values(c.choices)) {
225
+ const block = atLevel;
226
+ if (!block) continue;
227
+ const featRef = block.feat;
228
+ if (typeof featRef !== "string") continue;
229
+ const feat = featBySlug.get(stripRef(featRef));
230
+ if (!feat) continue;
231
+ for (const ch of (_a = feat.choices) != null ? _a : []) {
232
+ if (ch.kind !== "ability-points") continue;
233
+ const raw = block[`feat:${ch.id}`];
234
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) continue;
235
+ let left = ch.points;
236
+ for (const ab of ABILITY_KEYS) {
237
+ if (ch.pool && !ch.pool.includes(ab)) continue;
238
+ const v = raw[ab];
239
+ if (typeof v !== "number" || v <= 0 || left <= 0) continue;
240
+ const take = Math.min(v, ch.max_per, left);
241
+ out[ab] = ((_b = out[ab]) != null ? _b : 0) + take;
242
+ left -= take;
243
+ }
244
+ }
245
+ }
246
+ }
247
+ return out;
248
+ }
249
+ function computeAbilityScores(resolved, overrides) {
250
+ var _a, _b, _c, _d, _e, _f;
251
+ const out = { ...resolved.definition.abilities };
252
+ const raceBonuses = flattenRaceAsi(resolved.race);
253
+ for (const ab of ABILITY_KEYS) {
254
+ const b = raceBonuses[ab];
255
+ if (typeof b === "number") out[ab] = ((_a = out[ab]) != null ? _a : 0) + b;
256
+ }
257
+ for (const asi of subraceAsi(resolved)) {
258
+ if (asi.ability && typeof asi.amount === "number") out[asi.ability] = ((_b = out[asi.ability]) != null ? _b : 0) + asi.amount;
259
+ }
260
+ const originPoints = collectChosenAbilityPoints(resolved);
261
+ for (const src of [originPoints.race, originPoints.background]) {
262
+ for (const ab of ABILITY_KEYS) {
263
+ const v = src[ab];
264
+ if (typeof v === "number") out[ab] = ((_c = out[ab]) != null ? _c : 0) + v;
265
+ }
266
+ }
267
+ const classAsi = collectClassAsiBranch(resolved);
268
+ for (const ab of ABILITY_KEYS) {
269
+ const v = classAsi[ab];
270
+ if (typeof v === "number") out[ab] = ((_d = out[ab]) != null ? _d : 0) + v;
271
+ }
272
+ const featPoints = collectClassFeatAbilityPoints(resolved);
273
+ for (const ab of ABILITY_KEYS) {
274
+ const v = featPoints[ab];
275
+ if (typeof v === "number") out[ab] = ((_e = out[ab]) != null ? _e : 0) + v;
276
+ }
277
+ for (const f of resolved.feats) {
278
+ const bonuses = f.ability_bonuses;
279
+ if (bonuses) {
280
+ for (const ab of ABILITY_KEYS) {
281
+ const v = bonuses[ab];
282
+ if (typeof v === "number") out[ab] = ((_f = out[ab]) != null ? _f : 0) + v;
283
+ }
284
+ }
285
+ }
286
+ if (overrides.scores) {
287
+ for (const ab of ABILITY_KEYS) {
288
+ const v = overrides.scores[ab];
289
+ if (typeof v === "number") out[ab] = v;
290
+ }
291
+ }
292
+ for (const ab of ABILITY_KEYS) {
293
+ if (typeof out[ab] !== "number") out[ab] = 10;
294
+ }
295
+ return out;
296
+ }
297
+ function normalizeProfList(input) {
298
+ var _a, _b;
299
+ if (!input) return { categories: [], specific: [] };
300
+ if (Array.isArray(input)) return { categories: [], specific: input };
301
+ return { categories: (_a = input.categories) != null ? _a : [], specific: (_b = input.specific) != null ? _b : [] };
302
+ }
303
+ function mergeInto(target, source) {
304
+ for (const c of source.categories) if (!target.categories.includes(c)) target.categories.push(c);
305
+ for (const s of source.specific) if (!target.specific.includes(s)) target.specific.push(s);
306
+ }
307
+ function dedupeDefenseList(lists) {
308
+ const seen = /* @__PURE__ */ new Set();
309
+ const out = [];
310
+ for (const list of lists) {
311
+ for (const v of list) {
312
+ const key = v.trim().toLowerCase();
313
+ if (!key || seen.has(key)) continue;
314
+ seen.add(key);
315
+ out.push(v);
316
+ }
317
+ }
318
+ return out;
319
+ }
320
+ function computeProficiencies(resolved) {
321
+ var _a, _b, _c, _d, _e, _f, _g;
322
+ const armor = { categories: [], specific: [] };
323
+ const weapons = { categories: [], specific: [] };
324
+ const tools = { categories: [], specific: [] };
325
+ const languages = /* @__PURE__ */ new Set();
326
+ for (const c of resolved.classes) {
327
+ const p = (_a = c.entity) == null ? void 0 : _a.proficiencies;
328
+ if (!p) continue;
329
+ mergeInto(armor, normalizeProfList(p.armor));
330
+ mergeInto(weapons, normalizeProfList(p.weapons));
331
+ mergeInto(tools, normalizeProfList(p.tools));
332
+ (_b = p.languages) == null ? void 0 : _b.forEach((l) => languages.add(l));
333
+ }
334
+ const racePr = (_c = resolved.race) == null ? void 0 : _c.proficiencies;
335
+ if (racePr) {
336
+ mergeInto(armor, normalizeProfList(racePr.armor));
337
+ mergeInto(weapons, normalizeProfList(racePr.weapons));
338
+ mergeInto(tools, normalizeProfList(racePr.tools));
339
+ (_d = racePr.languages) == null ? void 0 : _d.forEach((l) => languages.add(l));
340
+ }
341
+ const bgPr = (_e = resolved.background) == null ? void 0 : _e.proficiencies;
342
+ if (bgPr) {
343
+ mergeInto(armor, normalizeProfList(bgPr.armor));
344
+ mergeInto(weapons, normalizeProfList(bgPr.weapons));
345
+ mergeInto(tools, normalizeProfList(bgPr.tools));
346
+ (_f = bgPr.languages) == null ? void 0 : _f.forEach((l) => languages.add(l));
347
+ }
348
+ for (const f of resolved.feats) {
349
+ const grants = f.grants_proficiency;
350
+ if (!grants) continue;
351
+ mergeInto(armor, normalizeProfList(grants.armor));
352
+ mergeInto(weapons, normalizeProfList(grants.weapons));
353
+ mergeInto(tools, normalizeProfList(grants.tools));
354
+ (_g = grants.languages) == null ? void 0 : _g.forEach((l) => languages.add(l));
355
+ }
356
+ return {
357
+ armor,
358
+ weapons,
359
+ tools,
360
+ languages: Array.from(languages).sort(),
361
+ saves: []
362
+ };
363
+ }
364
+ function recalc(resolved, registry) {
365
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X;
366
+ const warnings = [];
367
+ const overrides = (_a = resolved.definition.overrides) != null ? _a : {};
368
+ const chosenProfs = collectChosenProficiencies(resolved);
369
+ const activeBuffs = new Set((_b = resolved.state.active_buffs) != null ? _b : []);
370
+ const buffFeatures = [];
371
+ for (const pool of (_c = resolved.pools) != null ? _c : []) {
372
+ for (const sel of (_d = pool.selected) != null ? _d : []) {
373
+ const e = sel.entity;
374
+ if ((e == null ? void 0 : e.activatable) && ((_f = (_e = e.effects) == null ? void 0 : _e.length) != null ? _f : 0) > 0) {
375
+ buffFeatures.push({
376
+ feature: { id: sel.slug, name: e.name, activatable: true, effects: e.effects },
377
+ // source is inert for the fold; attribute to the pool's owning class
378
+ // (never a hardcoded class) so the generic engine carries no homebrew name.
379
+ source: { kind: "class", slug: (_i = (_h = (_g = resolved.classes[pool.classIndex]) == null ? void 0 : _g.entity) == null ? void 0 : _h.slug) != null ? _i : pool.id, level: pool.anchorLevel }
380
+ });
381
+ }
382
+ }
383
+ }
384
+ const featureEffects = computeFeatureEffects([...resolved.features, ...buffFeatures], { activeBuffs });
385
+ const profsForApply = computeProficiencies(resolved);
386
+ for (const t of chosenProfs.tools) {
387
+ if (!profsForApply.tools.specific.includes(t)) profsForApply.tools.specific.push(t);
388
+ }
389
+ for (const l of chosenProfs.languages) {
390
+ if (!profsForApply.languages.includes(l)) profsForApply.languages.push(l);
391
+ }
392
+ for (const t of featureEffects.proficiencies.tools) {
393
+ if (!profsForApply.tools.specific.includes(t)) profsForApply.tools.specific.push(t);
394
+ }
395
+ for (const l of featureEffects.proficiencies.languages) {
396
+ if (!profsForApply.languages.includes(l)) profsForApply.languages.push(l);
397
+ }
398
+ for (const a of featureEffects.proficiencies.armor) {
399
+ if (!profsForApply.armor.categories.includes(a)) profsForApply.armor.categories.push(a);
400
+ }
401
+ for (const w of featureEffects.proficiencies.weapons) {
402
+ if (!profsForApply.weapons.categories.includes(w)) profsForApply.weapons.categories.push(w);
403
+ }
404
+ profsForApply.languages.sort();
405
+ const applied = registry ? computeAppliedBonuses(resolved, profsForApply, registry, warnings) : emptyAppliedBonuses();
406
+ const savesInformational = applied.informational.filter(
407
+ (i) => i.field === "saving_throws"
408
+ );
409
+ const spellcastingInformational = applied.informational.filter(
410
+ (i) => i.field === "spell_attack" || i.field === "spell_save_dc"
411
+ );
412
+ const speedInformational = applied.informational.filter(
413
+ (i) => i.field.startsWith("speed.")
414
+ );
415
+ const baseScores = computeAbilityScores(resolved, overrides);
416
+ const scores = { ...baseScores };
417
+ for (const ab of ABILITY_KEYS) {
418
+ const bonus = applied.ability_bonuses[ab];
419
+ if (typeof bonus === "number") scores[ab] += bonus;
420
+ }
421
+ for (const ab of ABILITY_KEYS) {
422
+ const stat = applied.ability_statics[ab];
423
+ if (typeof stat === "number" && stat > scores[ab]) scores[ab] = stat;
424
+ }
425
+ if (overrides.scores) {
426
+ for (const ab of ABILITY_KEYS) {
427
+ const o = overrides.scores[ab];
428
+ if (typeof o === "number") scores[ab] = o;
429
+ }
430
+ }
431
+ const mods = {
432
+ str: abilityModifier(scores.str),
433
+ dex: abilityModifier(scores.dex),
434
+ con: abilityModifier(scores.con),
435
+ int: abilityModifier(scores.int),
436
+ wis: abilityModifier(scores.wis),
437
+ cha: abilityModifier(scores.cha)
438
+ };
439
+ const totalLevel = resolved.classes.reduce((s, c) => s + c.level, 0);
440
+ const proficiencyBonus = proficiencyFromLevel(totalLevel || 1);
441
+ const conditionEffects = computeConditionEffects(
442
+ resolved.state,
443
+ resolved.definition.edition,
444
+ scores
445
+ );
446
+ const firstClass = (_k = (_j = resolved.classes[0]) == null ? void 0 : _j.entity) != null ? _k : null;
447
+ const saveProfs = new Set((_l = firstClass == null ? void 0 : firstClass.saving_throws) != null ? _l : []);
448
+ for (const ab of featureEffects.proficiencies.saves) saveProfs.add(ab);
449
+ const saves = {};
450
+ for (const ab of ABILITY_KEYS) {
451
+ const override = (_m = overrides.saves) == null ? void 0 : _m[ab];
452
+ const prof = (_n = override == null ? void 0 : override.proficient) != null ? _n : saveProfs.has(ab);
453
+ const derivedBonus = savingThrow(scores[ab], prof, proficiencyBonus) + applied.save_bonus + conditionEffects.d20_test_penalty;
454
+ const bonus = (_o = override == null ? void 0 : override.bonus) != null ? _o : derivedBonus;
455
+ saves[ab] = { bonus, proficient: prof };
456
+ }
457
+ const profSet = /* @__PURE__ */ new Set([
458
+ ...resolved.definition.skills.proficient,
459
+ ...chosenProfs.skills,
460
+ ...featureEffects.proficiencies.skills
461
+ ]);
462
+ const expSet = /* @__PURE__ */ new Set([...resolved.definition.skills.expertise, ...chosenProfs.expertise]);
463
+ const skills = {};
464
+ for (const skill of ALL_SKILLS) {
465
+ const skillKey = skillSlugFromDisplay(skill);
466
+ const ab = SKILL_ABILITY[skillSlugToAbilityLookup(skillKey)];
467
+ const override = (_p = overrides.skills) == null ? void 0 : _p[skillKey];
468
+ const tri = (_q = override == null ? void 0 : override.proficiency) != null ? _q : expSet.has(skillKey) ? "expertise" : profSet.has(skillKey) ? "proficient" : "none";
469
+ const bonus = (_r = override == null ? void 0 : override.bonus) != null ? _r : skillBonus(scores[ab], tri, proficiencyBonus) + conditionEffects.d20_test_penalty;
470
+ skills[skillKey] = {
471
+ bonus,
472
+ proficiency: tri,
473
+ ability: ab
474
+ };
475
+ }
476
+ const perceptionTri = skills.perception.proficiency;
477
+ const investigationTri = skills.investigation.proficiency;
478
+ const insightTri = skills.insight.proficiency;
479
+ const passives = {
480
+ perception: (_t = (_s = overrides.passives) == null ? void 0 : _s.perception) != null ? _t : passivePerception(scores.wis, perceptionTri, proficiencyBonus),
481
+ investigation: (_v = (_u = overrides.passives) == null ? void 0 : _u.investigation) != null ? _v : passive(scores.int, investigationTri, proficiencyBonus),
482
+ insight: (_x = (_w = overrides.passives) == null ? void 0 : _w.insight) != null ? _x : passive(scores.wis, insightTri, proficiencyBonus)
483
+ };
484
+ const hpMaxDerived = multiclassMaxHP(resolved.classes, mods.con) + featureEffects.hp_per_level_bonus * totalLevel;
485
+ const hpMaxAfterConditions = Math.floor(hpMaxDerived * conditionEffects.hp_max_multiplier);
486
+ const hpMax = (_z = (_y = overrides.hp) == null ? void 0 : _y.max) != null ? _z : hpMaxAfterConditions;
487
+ const featureAcTermsFor = (hasArmor) => featureEffects.ac_terms.filter((t) => !t.requires_armor || hasArmor).map((t) => ({ source: t.label, amount: t.value, kind: "feature" }));
488
+ const sumTerms = (terms) => terms.reduce((s, t) => s + t.amount, 0);
489
+ let weaponAbility = featureEffects.weaponAbility;
490
+ const wantsSpellcasting = resolved.features.some((rf) => {
491
+ var _a2;
492
+ return ((_a2 = rf.feature.effects) != null ? _a2 : []).some((e) => e.kind === "weapon-ability" && e.ability === "spellcasting");
493
+ });
494
+ if (wantsSpellcasting) {
495
+ let primaryCasterAbility = null;
496
+ for (const c of resolved.classes) {
497
+ if (!c.entity) continue;
498
+ const profile = resolveSpellcasting(c);
499
+ if (profile) {
500
+ primaryCasterAbility = profile.ability;
501
+ break;
502
+ }
503
+ }
504
+ if (primaryCasterAbility) weaponAbility = primaryCasterAbility;
505
+ }
506
+ let derivedEquipment = null;
507
+ let acDerived;
508
+ let acBreakdownDerived = [];
509
+ let acInformationalDerived = [];
510
+ if (registry) {
511
+ derivedEquipment = computeSlotsAndAttacks(resolved, mods, profsForApply, registry, warnings, proficiencyBonus, weaponAbility != null ? weaponAbility : void 0);
512
+ if (derivedEquipment.equippedSlots.armor) {
513
+ const featTerms = featureAcTermsFor(true);
514
+ acDerived = derivedEquipment.ac + sumTerms(featTerms);
515
+ acBreakdownDerived = [...derivedEquipment.acBreakdown, ...featTerms];
516
+ acInformationalDerived = derivedEquipment.acInformational;
517
+ } else {
518
+ const { total: unarmored, terms: unarmoredTerms } = unarmoredACBreakdown(resolved, mods, warnings);
519
+ const additive = derivedEquipment.acBreakdown.filter(
520
+ (b) => b.kind === "item" || b.kind === "override" || b.kind === "shield"
521
+ );
522
+ const featTerms = featureAcTermsFor(false);
523
+ const additiveSum = additive.reduce((sum, b) => sum + b.amount, 0);
524
+ acDerived = unarmored + additiveSum + sumTerms(featTerms);
525
+ acBreakdownDerived = [...unarmoredTerms, ...additive, ...featTerms];
526
+ acInformationalDerived = derivedEquipment.acInformational;
527
+ }
528
+ } else {
529
+ const { total, terms } = unarmoredACBreakdown(resolved, mods, warnings);
530
+ const featTerms = featureAcTermsFor(false);
531
+ acDerived = total + sumTerms(featTerms);
532
+ acBreakdownDerived = [...terms, ...featTerms];
533
+ }
534
+ const ac = (_A = overrides.ac) != null ? _A : acDerived;
535
+ const additiveSpeed = speedFromRace(resolved) + applied.speed_bonuses.walk + featureEffects.speed_walk_bonus;
536
+ const baseSpeed = Math.max(featureEffects.speed_walk_set, additiveSpeed);
537
+ const adjustedSpeed = baseSpeed * conditionEffects.speed_multiplier - conditionEffects.speed_reduction_ft;
538
+ const conditionSpeed = conditionEffects.speed_floor_zero ? 0 : Math.max(0, Math.floor(adjustedSpeed));
539
+ const speed = (_B = overrides.speed) != null ? _B : conditionSpeed;
540
+ if (!resolved.race) warnings.push("No race resolved; speed defaulted to 30.");
541
+ const init = (_C = overrides.initiative) != null ? _C : initiativeBonus(mods.dex, resolved.feats, resolved.definition.edition) + featureEffects.initiative_bonus;
542
+ const senses = {
543
+ darkvision: Math.max((_F = (_E = (_D = resolved.race) == null ? void 0 : _D.vision) == null ? void 0 : _E.darkvision) != null ? _F : 0, featureEffects.senses.darkvision, applied.senses.darkvision),
544
+ blindsight: Math.max(featureEffects.senses.blindsight, applied.senses.blindsight),
545
+ tremorsense: Math.max(featureEffects.senses.tremorsense, applied.senses.tremorsense),
546
+ truesight: Math.max(featureEffects.senses.truesight, applied.senses.truesight)
547
+ };
548
+ const spellcastingClasses = [];
549
+ const slotInputs = [];
550
+ const limitInputs = [];
551
+ for (const c of resolved.classes) {
552
+ if (!c.entity) continue;
553
+ const profile = resolveSpellcasting(c);
554
+ if (!profile) continue;
555
+ const dc = saveDC(scores[profile.ability], proficiencyBonus) + applied.spell_save_dc;
556
+ const atk = attackBonus(scores[profile.ability], proficiencyBonus) + applied.spell_attack;
557
+ spellcastingClasses.push({
558
+ classSlug: c.entity.slug,
559
+ className: c.entity.name,
560
+ ability: profile.ability,
561
+ saveDC: (_H = (_G = overrides.spellcasting) == null ? void 0 : _G.saveDC) != null ? _H : dc,
562
+ attackBonus: (_J = (_I = overrides.spellcasting) == null ? void 0 : _I.attackBonus) != null ? _J : atk,
563
+ casterType: profile.casterType,
564
+ preparation: profile.preparation
565
+ });
566
+ slotInputs.push({ casterType: profile.casterType, level: c.level });
567
+ limitInputs.push({ classSlug: c.entity.slug, level: c.level, profile, abilityScore: scores[profile.ability] });
568
+ }
569
+ const spellcasting = spellcastingClasses.length > 0 ? {
570
+ ability: spellcastingClasses[0].ability,
571
+ saveDC: spellcastingClasses[0].saveDC,
572
+ attackBonus: spellcastingClasses[0].attackBonus
573
+ } : null;
574
+ const derivedSlots = deriveSpellSlots(slotInputs);
575
+ const spellLimits = computeSpellLimits(limitInputs);
576
+ const defenses = {
577
+ resistances: dedupeDefenseList([
578
+ (_L = (_K = resolved.definition.defenses) == null ? void 0 : _K.resistances) != null ? _L : [],
579
+ applied.defenses.resistances,
580
+ featureEffects.resistances
581
+ ]),
582
+ immunities: dedupeDefenseList([
583
+ (_N = (_M = resolved.definition.defenses) == null ? void 0 : _M.immunities) != null ? _N : [],
584
+ applied.defenses.immunities
585
+ ]),
586
+ vulnerabilities: dedupeDefenseList([
587
+ (_P = (_O = resolved.definition.defenses) == null ? void 0 : _O.vulnerabilities) != null ? _P : [],
588
+ applied.defenses.vulnerabilities
589
+ ]),
590
+ condition_immunities: dedupeDefenseList([
591
+ (_R = (_Q = resolved.definition.defenses) == null ? void 0 : _Q.condition_immunities) != null ? _R : [],
592
+ applied.defenses.condition_immunities,
593
+ featureEffects.condition_immunities
594
+ ])
595
+ };
596
+ return {
597
+ totalLevel,
598
+ proficiencyBonus,
599
+ scores,
600
+ mods,
601
+ saves,
602
+ proficiencies: profsForApply,
603
+ skills,
604
+ passives,
605
+ senses,
606
+ hp: {
607
+ max: hpMax,
608
+ current: resolved.state.hp.current,
609
+ temp: resolved.state.hp.temp
610
+ },
611
+ ac,
612
+ speed,
613
+ initiative: init,
614
+ spellcasting,
615
+ spellcastingClasses,
616
+ derivedSpellSlots: derivedSlots.standard,
617
+ pactMagic: derivedSlots.pact,
618
+ spellLimits,
619
+ warnings,
620
+ defenses,
621
+ acBreakdown: acBreakdownDerived,
622
+ acInformational: acInformationalDerived,
623
+ savesInformational,
624
+ spellcastingInformational,
625
+ speedInformational,
626
+ // Always ≥ 1; non-stacking (Math.max) extra attacks fold in pc.feature-effects.
627
+ attacksPerAction: 1 + featureEffects.extraAttack,
628
+ // Consolidated post-apply over the built attack rows: the d20 condition
629
+ // penalty (always), plus display-only annotations conditionally spread so
630
+ // untouched rows keep `critRange`/`attackNotes` ABSENT (not 20 / not []).
631
+ // crit-range: folded weapon crit threshold, only when an effect lowered it.
632
+ // attackNotes: reroll-damage / attack-rule captions, only when non-empty.
633
+ attacks: ((_S = derivedEquipment == null ? void 0 : derivedEquipment.attacks) != null ? _S : []).map((a) => ({
634
+ ...a,
635
+ toHit: a.toHit + conditionEffects.d20_test_penalty,
636
+ ...featureEffects.critRange < 20 ? { critRange: featureEffects.critRange } : {},
637
+ ...featureEffects.attackNotes.length ? { attackNotes: featureEffects.attackNotes } : {}
638
+ })),
639
+ equippedSlots: (_T = derivedEquipment == null ? void 0 : derivedEquipment.equippedSlots) != null ? _T : {},
640
+ carriedWeight: (_U = derivedEquipment == null ? void 0 : derivedEquipment.carriedWeight) != null ? _U : 0,
641
+ attunementUsed: (_V = derivedEquipment == null ? void 0 : derivedEquipment.attunementUsed) != null ? _V : 0,
642
+ attunementLimit: (_X = derivedEquipment == null ? void 0 : derivedEquipment.attunementLimit) != null ? _X : (_W = overrides.attunement_limit) != null ? _W : 3,
643
+ conditionEffects,
644
+ rollModifiers: featureEffects.rollModifiers
645
+ };
646
+ }
647
+ export {
648
+ abilityBonusBreakdown,
649
+ collectClassAsiBranch,
650
+ collectClassFeatAbilityPoints,
651
+ computeAbilityScores,
652
+ computeProficiencies,
653
+ flattenRaceAsi,
654
+ initiativeBonus,
655
+ multiclassMaxHP,
656
+ parseDieSize,
657
+ phbAverageForDie,
658
+ recalc,
659
+ speedFromRace,
660
+ unarmoredAC,
661
+ unarmoredACBreakdown
662
+ };
@@ -0,0 +1,34 @@
1
+ import type { EntityRegistry } from "@archivist-gg/core";
2
+ import type { RaceEntity } from "@archivist-gg/dnd5e/race/race.types";
3
+ import type { BackgroundEntity } from "@archivist-gg/dnd5e/background/background.types";
4
+ import type { FeatEntity } from "@archivist-gg/dnd5e/feat/feat.types";
5
+ import type { Character, ResolvedCharacter, ResolvedClass, ResolvedFeature } from "./pc.types.js";
6
+ export interface ResolveResult {
7
+ character: ResolvedCharacter;
8
+ warnings: string[];
9
+ }
10
+ export declare function stripSlug(ref: string | null): string | null;
11
+ export declare class PCResolver {
12
+ private readonly entities;
13
+ constructor(entities: EntityRegistry);
14
+ /** Exposed so recalc() can apply equipment bonuses (Pass A / Pass B). */
15
+ get registry(): EntityRegistry;
16
+ resolve(character: Character): ResolveResult;
17
+ }
18
+ /**
19
+ * Walks class.choices for `feat` entries and adds background feat slugs.
20
+ * Returns bare slugs (no `[[ ]]`).
21
+ */
22
+ export declare function collectFeatSlugs(character: Character): string[];
23
+ /** Selected select-entity values (optional-features) and selected inline
24
+ * options carrying effects[] become synthesized resolved features (SP2 Plan 3 §9).
25
+ * The actions/resources engines consume these synthesized features, and the
26
+ * effects-application pass (pc.feature-effects.ts → recalc) consumes their
27
+ * effects[]. Scope: class/subclass level choices, plus race/background origin
28
+ * choices (origin_choices keys are namespaced "race:<id>" / "background:<id>"). */
29
+ export declare function collectChosenGrantedFeatures(character: Character, classes: ResolvedClass[], registry: {
30
+ getByTypeAndSlug(type: string, slug: string): {
31
+ data: Record<string, unknown>;
32
+ } | undefined;
33
+ }, race?: RaceEntity | null, background?: BackgroundEntity | null): ResolvedFeature[];
34
+ export declare function collectResolvedFeatures(race: RaceEntity | null, classes: ResolvedClass[], background: BackgroundEntity | null, feats: FeatEntity[]): ResolvedFeature[];