@archivist-gg/dnd5e 0.3.0 → 0.3.4

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 (260) hide show
  1. package/LICENSES/SRD.md +25 -6
  2. package/dist/armor/armor.codec.js +4 -3
  3. package/dist/armor/armor.parser.js +3 -2
  4. package/dist/armor/armor.schema.d.ts +4 -0
  5. package/dist/armor/armor.schema.js +2 -1
  6. package/dist/armor/armor.types.d.ts +8 -0
  7. package/dist/background/background.codec.js +7 -6
  8. package/dist/background/background.parser.js +6 -5
  9. package/dist/background/background.schema.d.ts +34 -0
  10. package/dist/background/background.schema.js +9 -6
  11. package/dist/background/background.types.d.ts +22 -0
  12. package/dist/{chunk-4TIJKN6O.js → chunk-23LZLBJK.js} +1 -1
  13. package/dist/{chunk-AFKTJXRA.js → chunk-2DFENQIJ.js} +18 -0
  14. package/dist/chunk-2FRUSVZS.js +489 -0
  15. package/dist/chunk-4FWNK4FD.js +159 -0
  16. package/dist/chunk-4U55VNC7.js +22 -0
  17. package/dist/{chunk-4JHOHWBI.js → chunk-4XTNPNQB.js} +1 -1
  18. package/dist/{chunk-HCP3GMHS.js → chunk-5N2GW72F.js} +15 -2
  19. package/dist/{chunk-42Y5IJKT.js → chunk-5U5FFFA3.js} +5 -1
  20. package/dist/{chunk-4F6AIZ36.js → chunk-5XMQPTHD.js} +1 -1
  21. package/dist/{chunk-GJOI2GHP.js → chunk-6GNXYAYP.js} +1 -1
  22. package/dist/chunk-6PKV5V2H.js +178 -0
  23. package/dist/{chunk-66XXW2Z2.js → chunk-6PXTNCBP.js} +2 -2
  24. package/dist/chunk-6X2NV665.js +33 -0
  25. package/dist/{chunk-LV4SNWZQ.js → chunk-6X4KUDMU.js} +8 -1
  26. package/dist/{chunk-ZMR77DDL.js → chunk-AW3EHF3L.js} +1 -1
  27. package/dist/chunk-CJYCVNW2.js +7 -0
  28. package/dist/chunk-COEOOQX2.js +133 -0
  29. package/dist/{chunk-XTIQWSFM.js → chunk-D5U7GMPX.js} +1 -1
  30. package/dist/{chunk-RAWJTA45.js → chunk-DEEHHLAP.js} +1 -1
  31. package/dist/chunk-DPUJDOKV.js +365 -0
  32. package/dist/{chunk-73HOFQQ5.js → chunk-ESS46GR4.js} +1 -1
  33. package/dist/chunk-EVMUJYUD.js +195 -0
  34. package/dist/{chunk-2BWAID3Y.js → chunk-FDBWYMZK.js} +29 -3
  35. package/dist/{chunk-K6Q5OUGK.js → chunk-GFZ7PITJ.js} +86 -10
  36. package/dist/chunk-H7KBLR4X.js +81 -0
  37. package/dist/{chunk-L7E2UT5A.js → chunk-HB7XZXDK.js} +5 -1
  38. package/dist/{chunk-VTYUBINW.js → chunk-HJ5EEZ47.js} +1 -1
  39. package/dist/{chunk-6NK2MUD3.js → chunk-HZA7OE7N.js} +9 -2
  40. package/dist/{chunk-A4WENOUQ.js → chunk-I2NDWFDD.js} +52 -8
  41. package/dist/{chunk-RSDRMH54.js → chunk-IMZFOOVL.js} +1 -1
  42. package/dist/{chunk-3DZ74AKK.js → chunk-IN4M4X7P.js} +8 -2
  43. package/dist/chunk-IUAAXXNO.js +58 -0
  44. package/dist/{chunk-XJNECUPT.js → chunk-JC5UNIVM.js} +19 -0
  45. package/dist/chunk-JJ46T5XX.js +297 -0
  46. package/dist/{chunk-TUIFSVLI.js → chunk-JRDPUDT5.js} +30 -3
  47. package/dist/{chunk-24GFQU7T.js → chunk-JSZ4H6AQ.js} +1 -1
  48. package/dist/chunk-KU3YLF6D.js +106 -0
  49. package/dist/{chunk-5INR4NSL.js → chunk-LDDCNPHD.js} +1 -1
  50. package/dist/{chunk-DZXSWSBP.js → chunk-LMBAEIP6.js} +1 -1
  51. package/dist/chunk-LVQLWRCU.js +15 -0
  52. package/dist/chunk-MI7P3NX3.js +161 -0
  53. package/dist/chunk-O4SRD3J5.js +165 -0
  54. package/dist/{chunk-EMOORSQ7.js → chunk-OSQFPGLM.js} +45 -6
  55. package/dist/chunk-OZP6JFGT.js +162 -0
  56. package/dist/{chunk-NAEPSZAM.js → chunk-PDJSUH24.js} +24 -2
  57. package/dist/chunk-PP2UHLBA.js +50 -0
  58. package/dist/chunk-PVCEA4HZ.js +6 -0
  59. package/dist/{chunk-XLSVNUTK.js → chunk-R3VAKWKW.js} +1 -1
  60. package/dist/chunk-RIXI5D3R.js +89 -0
  61. package/dist/chunk-RLGQ5PZZ.js +16 -0
  62. package/dist/chunk-S7REMWW4.js +21 -0
  63. package/dist/{chunk-7VJ6BOA5.js → chunk-SMLXZUBH.js} +9 -1
  64. package/dist/{chunk-NXSWH3AE.js → chunk-T26ZVCL5.js} +1 -1
  65. package/dist/chunk-TOM66P2P.js +38 -0
  66. package/dist/chunk-UA5KS2PL.js +26 -0
  67. package/dist/{chunk-N6N74K6X.js → chunk-UBBKSYBY.js} +1 -1
  68. package/dist/{chunk-353WXS6S.js → chunk-UIRMA5KM.js} +1 -1
  69. package/dist/{chunk-GE4TLPWM.js → chunk-US5IKYCW.js} +1 -1
  70. package/dist/chunk-UXPGW4TI.js +101 -0
  71. package/dist/chunk-VQAXTIBW.js +100 -0
  72. package/dist/{chunk-WP2CX2JO.js → chunk-WELIKLZF.js} +22 -1
  73. package/dist/chunk-WPWOHGPI.js +172 -0
  74. package/dist/{chunk-GZ46OVGZ.js → chunk-WSVMLNTO.js} +1 -1
  75. package/dist/{chunk-4LZJXJJS.js → chunk-WXWPK3LI.js} +6 -2
  76. package/dist/{chunk-ZZGEVJKX.js → chunk-XBQMCEI2.js} +1 -1
  77. package/dist/{chunk-PHQRGDRQ.js → chunk-XGOFP7AQ.js} +5 -1
  78. package/dist/chunk-XJ2F2WO6.js +8 -0
  79. package/dist/chunk-XQCSGNXE.js +1580 -0
  80. package/dist/chunk-YBLDE5CG.js +42 -0
  81. package/dist/{chunk-JEYQU3JT.js → chunk-YHYTW5QA.js} +2 -18
  82. package/dist/{chunk-TLYDBT5F.js → chunk-YJJTISBN.js} +64 -7
  83. package/dist/{chunk-PZOWBI3B.js → chunk-YLL2QXY2.js} +7 -2
  84. package/dist/{chunk-MDEJHMKK.js → chunk-ZONJ66NE.js} +26 -5
  85. package/dist/class/class.codec.js +12 -9
  86. package/dist/class/class.parser.js +11 -8
  87. package/dist/class/class.schema.d.ts +55 -3
  88. package/dist/class/class.schema.js +9 -7
  89. package/dist/class/class.types.d.ts +48 -3
  90. package/dist/condition/condition.codec.d.ts +9 -0
  91. package/dist/condition/condition.entity-type.d.ts +2 -0
  92. package/dist/condition/condition.parser.d.ts +13 -0
  93. package/dist/condition/condition.parser.js +7 -0
  94. package/dist/condition/condition.schema.d.ts +29 -0
  95. package/dist/condition/condition.types.d.ts +15 -0
  96. package/dist/condition/condition.types.js +0 -0
  97. package/dist/dnd/constants.d.ts +22 -0
  98. package/dist/dnd/constants.js +9 -3
  99. package/dist/dnd/formula-tags.js +2 -2
  100. package/dist/dnd/math.d.ts +26 -0
  101. package/dist/dnd/math.js +12 -2
  102. package/dist/dnd/prose-tags.js +1 -1
  103. package/dist/dnd/resource-formula.d.ts +36 -10
  104. package/dist/dnd/resource-formula.js +10 -155
  105. package/dist/dnd/srd-tag-converter.js +3 -3
  106. package/dist/dnd/tag-grammar.js +1 -1
  107. package/dist/dnd/warn-once.d.ts +2 -0
  108. package/dist/dnd/warn-once.js +8 -0
  109. package/dist/entities/base-item-resolver.js +2 -2
  110. package/dist/entities/slug.d.ts +13 -0
  111. package/dist/entities/slug.js +5 -3
  112. package/dist/entities/wikilink-display.d.ts +3 -0
  113. package/dist/entities/wikilink-display.js +10 -0
  114. package/dist/feat/feat.category-codes.d.ts +32 -0
  115. package/dist/feat/feat.category-codes.js +14 -0
  116. package/dist/feat/feat.codec.js +7 -5
  117. package/dist/feat/feat.parser.js +6 -4
  118. package/dist/feat/feat.schema.d.ts +230 -0
  119. package/dist/feat/feat.schema.js +5 -3
  120. package/dist/feat/feat.types.d.ts +37 -1
  121. package/dist/index.js +2120 -6923
  122. package/dist/item/item.ai-schema.d.ts +1 -1
  123. package/dist/item/item.codec.js +4 -3
  124. package/dist/item/item.enrichment.js +4 -4
  125. package/dist/item/item.parser.js +3 -2
  126. package/dist/item/item.schema.d.ts +69 -3
  127. package/dist/item/item.schema.js +2 -1
  128. package/dist/item/item.types.d.ts +69 -3
  129. package/dist/monster/monster.codec.d.ts +7 -6
  130. package/dist/monster/monster.enrichment.js +4 -4
  131. package/dist/monster/monster.format.d.ts +135 -0
  132. package/dist/monster/monster.format.js +78 -0
  133. package/dist/monster/monster.parser.d.ts +6 -1
  134. package/dist/monster/monster.parser.js +10 -1
  135. package/dist/monster/monster.schema.d.ts +369 -0
  136. package/dist/monster/monster.schema.js +34 -0
  137. package/dist/monster/monster.types.d.ts +200 -14
  138. package/dist/optional-feature/optional-feature.codec.js +6 -5
  139. package/dist/optional-feature/optional-feature.parser.js +5 -4
  140. package/dist/optional-feature/optional-feature.schema.d.ts +176 -0
  141. package/dist/optional-feature/optional-feature.schema.js +4 -3
  142. package/dist/pack.d.ts +1 -1
  143. package/dist/pc/conditions.constants.d.ts +0 -1
  144. package/dist/pc/conditions.constants.js +1 -3
  145. package/dist/pc/decision-recognizer.js +1 -1
  146. package/dist/pc/pc.additional-spells.d.ts +47 -0
  147. package/dist/pc/pc.additional-spells.js +23 -0
  148. package/dist/pc/pc.asi-flatten.d.ts +18 -0
  149. package/dist/pc/pc.decision-engine.d.ts +106 -5
  150. package/dist/pc/pc.decision-engine.js +28 -10
  151. package/dist/pc/pc.defense-normalize.d.ts +11 -0
  152. package/dist/pc/pc.defense-normalize.js +6 -0
  153. package/dist/pc/pc.equipment.d.ts +30 -1
  154. package/dist/pc/pc.equipment.js +12 -7
  155. package/dist/pc/pc.feature-effects.d.ts +225 -11
  156. package/dist/pc/pc.feature-effects.js +21 -5
  157. package/dist/pc/pc.parser.js +2 -2
  158. package/dist/pc/pc.pools.d.ts +11 -3
  159. package/dist/pc/pc.pools.js +9 -13
  160. package/dist/pc/pc.proficiencies.d.ts +60 -4
  161. package/dist/pc/pc.proficiencies.js +89 -37
  162. package/dist/pc/pc.proficiency-grants.d.ts +58 -0
  163. package/dist/pc/pc.proficiency-normalize.d.ts +29 -0
  164. package/dist/pc/pc.proficiency-normalize.js +12 -0
  165. package/dist/pc/pc.proficiency-query.d.ts +13 -0
  166. package/dist/pc/pc.proficiency-query.js +5 -1
  167. package/dist/pc/pc.recalc.d.ts +69 -14
  168. package/dist/pc/pc.recalc.js +291 -142
  169. package/dist/pc/pc.resolver.d.ts +34 -8
  170. package/dist/pc/pc.resolver.js +27 -12
  171. package/dist/pc/pc.resource-seed.d.ts +3 -2
  172. package/dist/pc/pc.resource-seed.js +19 -17
  173. package/dist/pc/pc.resources.d.ts +87 -0
  174. package/dist/pc/pc.resources.js +21 -0
  175. package/dist/pc/pc.rest.d.ts +10 -1
  176. package/dist/pc/pc.rest.js +66 -12
  177. package/dist/pc/pc.schema.d.ts +147 -7
  178. package/dist/pc/pc.schema.js +4 -2
  179. package/dist/pc/pc.slotting.js +3 -3
  180. package/dist/pc/pc.spellcasting.d.ts +80 -7
  181. package/dist/pc/pc.spellcasting.js +18 -6
  182. package/dist/pc/pc.table-column.d.ts +23 -0
  183. package/dist/pc/pc.table-column.js +13 -3
  184. package/dist/pc/pc.types.d.ts +253 -16
  185. package/dist/pc/pool-layout.d.ts +47 -0
  186. package/dist/pc/pool-layout.js +16 -0
  187. package/dist/pc/roll-scope.d.ts +2 -0
  188. package/dist/pc/roll-scope.js +8 -0
  189. package/dist/pc/roll-tag-labels.d.ts +40 -0
  190. package/dist/pc/roll-tag-labels.js +17 -0
  191. package/dist/pc/spell-source.d.ts +21 -0
  192. package/dist/pc/spell-source.js +13 -0
  193. package/dist/race/race.codec.js +9 -7
  194. package/dist/race/race.parser.js +8 -6
  195. package/dist/race/race.schema.d.ts +51 -1
  196. package/dist/race/race.schema.js +8 -5
  197. package/dist/race/race.structural.d.ts +7 -0
  198. package/dist/race/race.structural.js +6 -0
  199. package/dist/race/race.types.d.ts +20 -0
  200. package/dist/schemas/attack-schema.d.ts +5 -0
  201. package/dist/schemas/caster-type-schema.d.ts +14 -0
  202. package/dist/schemas/caster-type-schema.js +6 -0
  203. package/dist/schemas/choice-schema.js +2 -2
  204. package/dist/schemas/entity-extras-schema.d.ts +53 -0
  205. package/dist/schemas/entity-extras-schema.js +10 -0
  206. package/dist/schemas/equipment-grant-schema.d.ts +2 -0
  207. package/dist/schemas/equipment-grant-schema.js +1 -1
  208. package/dist/schemas/feature-alias.d.ts +34 -0
  209. package/dist/schemas/feature-effect-schema.d.ts +124 -0
  210. package/dist/schemas/feature-effect-schema.js +1 -1
  211. package/dist/schemas/feature-schema.d.ts +8 -0
  212. package/dist/schemas/feature-schema.js +6 -4
  213. package/dist/schemas/resource-schema.d.ts +43 -0
  214. package/dist/schemas/resource-schema.js +1 -1
  215. package/dist/schemas/selection-pool-schema.d.ts +2 -0
  216. package/dist/schemas/selection-pool-schema.js +1 -1
  217. package/dist/spell/spell.access.js +23 -3
  218. package/dist/spell/spell.codec.js +4 -3
  219. package/dist/spell/spell.enrichment.js +4 -4
  220. package/dist/spell/spell.parser.d.ts +30 -0
  221. package/dist/spell/spell.parser.js +9 -2
  222. package/dist/spell/spell.scaling.d.ts +27 -6
  223. package/dist/spell/spell.scaling.js +25 -6
  224. package/dist/spell/spell.schema.d.ts +44 -2
  225. package/dist/spell/spell.schema.js +2 -1
  226. package/dist/spell/spell.types.d.ts +14 -0
  227. package/dist/subclass/subclass.codec.d.ts +1 -1
  228. package/dist/subclass/subclass.codec.js +11 -8
  229. package/dist/subclass/subclass.parser.js +10 -7
  230. package/dist/subclass/subclass.schema.d.ts +54 -7
  231. package/dist/subclass/subclass.schema.js +8 -6
  232. package/dist/subclass/subclass.types.d.ts +20 -2
  233. package/dist/types/choice.d.ts +56 -0
  234. package/dist/types/choice.js +21 -3
  235. package/dist/types/equipment-grant.d.ts +8 -1
  236. package/dist/types/feature-effect.d.ts +78 -23
  237. package/dist/types/feature.d.ts +70 -1
  238. package/dist/types/optional-feature.types.d.ts +28 -0
  239. package/dist/types/resource.d.ts +21 -1
  240. package/dist/types/selection-pool.d.ts +17 -2
  241. package/dist/weapon/weapon.classify.js +2 -2
  242. package/dist/weapon/weapon.codec.js +4 -3
  243. package/dist/weapon/weapon.parser.js +3 -2
  244. package/dist/weapon/weapon.schema.d.ts +4 -0
  245. package/dist/weapon/weapon.schema.js +2 -1
  246. package/dist/weapon/weapon.types.d.ts +8 -0
  247. package/package.json +2 -2
  248. package/dist/chunk-5MZSB6OM.js +0 -1089
  249. package/dist/chunk-5XUXXAQJ.js +0 -30
  250. package/dist/chunk-7P65Q426.js +0 -158
  251. package/dist/chunk-AIRFC5NQ.js +0 -143
  252. package/dist/chunk-BH6AMZVY.js +0 -25
  253. package/dist/chunk-CZPMTQOY.js +0 -68
  254. package/dist/chunk-M2EMFD6S.js +0 -269
  255. package/dist/chunk-N2JE7BWL.js +0 -81
  256. package/dist/chunk-NXR3BD2V.js +0 -90
  257. package/dist/chunk-Q5K6LWBZ.js +0 -45
  258. package/dist/chunk-TZK36BU7.js +0 -38
  259. package/dist/chunk-W7HWMMBF.js +0 -46
  260. package/dist/chunk-Y4I74YEG.js +0 -33
@@ -0,0 +1,297 @@
1
+ import {
2
+ normalizeRollScope
3
+ } from "./chunk-TOM66P2P.js";
4
+ import {
5
+ toProfSlug
6
+ } from "./chunk-RLGQ5PZZ.js";
7
+ import {
8
+ warnOnce
9
+ } from "./chunk-LVQLWRCU.js";
10
+ import {
11
+ bareEntitySlug
12
+ } from "./chunk-6X4KUDMU.js";
13
+ import {
14
+ normalizeAbility
15
+ } from "./chunk-WELIKLZF.js";
16
+
17
+ // src/pc/pc.feature-effects.ts
18
+ function emptyFeatureEffectTotals() {
19
+ return {
20
+ initiative_bonus: 0,
21
+ hp_per_level_bonus: 0,
22
+ hp_per_level_terms: [],
23
+ speed_walk_bonus: 0,
24
+ speed_walk_set: 0,
25
+ ability_bonus: {},
26
+ senses: { darkvision: 0, blindsight: 0, tremorsense: 0, truesight: 0 },
27
+ ac_terms: [],
28
+ resistances: [],
29
+ immunities: [],
30
+ vulnerabilities: [],
31
+ condition_immunities: [],
32
+ proficiencies: { skills: [], skillExpertise: [], tools: [], languages: [], saves: [], armor: [], weapons: [] },
33
+ weaponAbilities: [],
34
+ rollModifiers: [],
35
+ saveOutcomes: [],
36
+ critRange: 20,
37
+ extraAttack: 0,
38
+ attackNotes: [],
39
+ damageBonuses: []
40
+ };
41
+ }
42
+ function pushUnique(list, value) {
43
+ const key = value.trim().toLowerCase();
44
+ if (!key) return;
45
+ if (!list.some((v) => v.trim().toLowerCase() === key)) list.push(value.trim());
46
+ }
47
+ function pushDefenseGrant(list, value, source, condition) {
48
+ const key = value.trim().toLowerCase();
49
+ if (!key) return;
50
+ const existing = list.find((g) => g.value.trim().toLowerCase() === key);
51
+ if (existing) {
52
+ if (!existing.sources.includes(source)) existing.sources.push(source);
53
+ if (!existing.condition && condition) existing.condition = condition;
54
+ return;
55
+ }
56
+ list.push({ value: value.trim(), sources: [source], ...condition ? { condition } : {} });
57
+ }
58
+ function foldsNow(rf, activeBuffs) {
59
+ if (rf.feature.activatable !== true) return true;
60
+ const id = rf.feature.id;
61
+ return !!id && activeBuffs.has(id);
62
+ }
63
+ function assembleEffectFeatures(resolved) {
64
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
65
+ const activeBuffs = new Set((_b = (_a = resolved.state) == null ? void 0 : _a.active_buffs) != null ? _b : []);
66
+ const featureIds = new Set(((_c = resolved.features) != null ? _c : []).map((f) => f.feature.id).filter((id) => !!id));
67
+ const aliases = [];
68
+ for (const pool of (_d = resolved.pools) != null ? _d : []) {
69
+ for (const entry of [...(_e = pool.selected) != null ? _e : [], ...(_f = pool.grants) != null ? _f : []]) {
70
+ if (activeBuffs.has(entry.slug)) continue;
71
+ const bare = bareEntitySlug(entry.slug);
72
+ for (const stored of activeBuffs) {
73
+ if (featureIds.has(stored)) continue;
74
+ if (bareEntitySlug(stored) === bare) {
75
+ aliases.push(entry.slug);
76
+ break;
77
+ }
78
+ }
79
+ }
80
+ }
81
+ for (const a of aliases) activeBuffs.add(a);
82
+ const buffFeatures = [];
83
+ const pushBoon = (item, pool) => {
84
+ var _a2, _b2, _c2, _d2, _e2, _f2, _g2;
85
+ const e = item.entity;
86
+ if (!e || ((_b2 = (_a2 = e.effects) == null ? void 0 : _a2.length) != null ? _b2 : 0) === 0) return;
87
+ buffFeatures.push({
88
+ feature: { id: item.slug, name: e.name, activatable: (_c2 = e.activatable) != null ? _c2 : false, effects: e.effects },
89
+ // source is inert for the fold; attribute to the pool's owning class
90
+ // (never a hardcoded class) so the generic engine carries no homebrew name.
91
+ source: { kind: "class", slug: (_g2 = (_f2 = (_e2 = (_d2 = resolved.classes) == null ? void 0 : _d2[pool.classIndex]) == null ? void 0 : _e2.entity) == null ? void 0 : _f2.slug) != null ? _g2 : pool.id, level: pool.anchorLevel }
92
+ });
93
+ };
94
+ for (const pool of (_g = resolved.pools) != null ? _g : []) {
95
+ for (const sel of (_h = pool.selected) != null ? _h : []) pushBoon(sel, pool);
96
+ for (const g of (_i = pool.grants) != null ? _i : []) pushBoon(g, pool);
97
+ }
98
+ return { features: [...(_j = resolved.features) != null ? _j : [], ...buffFeatures], activeBuffs };
99
+ }
100
+ function foldsOnSelf(eff) {
101
+ if (eff.subject === void 0 || eff.subject === "self") return true;
102
+ warnOnce(eff.subject, `archivist: unrecognised effect subject "${eff.subject}" (only "self" folds)`);
103
+ return false;
104
+ }
105
+ function selfEffectsOf(f) {
106
+ var _a;
107
+ return ((_a = f.effects) != null ? _a : []).filter(foldsOnSelf);
108
+ }
109
+ function computeFeatureEffects(features, opts) {
110
+ var _a, _b, _c;
111
+ const out = emptyFeatureEffectTotals();
112
+ for (const rf of features) {
113
+ if (!foldsNow(rf, (_a = opts == null ? void 0 : opts.activeBuffs) != null ? _a : /* @__PURE__ */ new Set())) continue;
114
+ const level = (_b = opts == null ? void 0 : opts.levelFor) == null ? void 0 : _b.call(opts, rf.source);
115
+ for (const eff of selfEffectsOf(rf.feature)) {
116
+ applyEffect(out, eff, (_c = rf.feature.name) != null ? _c : "Feature", level);
117
+ }
118
+ }
119
+ return out;
120
+ }
121
+ function classifyProficiencyEffect(eff) {
122
+ if (eff.kind !== "proficiency") return null;
123
+ const raw = eff.value;
124
+ const expertise = eff.expertise === true;
125
+ switch (eff.proficiency_type) {
126
+ case "skill":
127
+ return { bucket: "skills", value: toProfSlug(raw), raw, expertise };
128
+ case "tool":
129
+ return { bucket: "tools", value: toProfSlug(raw), raw, expertise };
130
+ case "language":
131
+ return { bucket: "languages", value: toProfSlug(raw), raw, expertise };
132
+ case "armor":
133
+ return { bucket: "armor", value: raw.toLowerCase(), raw, expertise };
134
+ case "weapon":
135
+ return { bucket: "weapons", value: raw.toLowerCase(), raw, expertise };
136
+ default: {
137
+ const ab = normalizeAbility(raw);
138
+ return ab ? { bucket: "saves", value: ab, raw, expertise } : null;
139
+ }
140
+ }
141
+ }
142
+ function collectProficiencyEffectGrants(features, activeBuffs) {
143
+ const out = {
144
+ skills: [],
145
+ tools: [],
146
+ languages: [],
147
+ saves: [],
148
+ armor: [],
149
+ weapons: []
150
+ };
151
+ for (const rf of features) {
152
+ if (!foldsNow(rf, activeBuffs)) continue;
153
+ for (const eff of selfEffectsOf(rf.feature)) {
154
+ const c = classifyProficiencyEffect(eff);
155
+ if (!c) continue;
156
+ out[c.bucket].push({
157
+ value: c.value,
158
+ raw: c.raw,
159
+ sourceKind: rf.source.kind,
160
+ sourceSlug: rf.source.slug,
161
+ ...c.expertise ? { expertise: true } : {}
162
+ });
163
+ }
164
+ }
165
+ return out;
166
+ }
167
+ function scaled(base, key, scalesAt, level) {
168
+ if (level === void 0 || !(scalesAt == null ? void 0 : scalesAt.length)) return base;
169
+ let best = base;
170
+ let bestLevel = -1;
171
+ for (const step of scalesAt) {
172
+ if (step.level > level || step.level <= bestLevel) continue;
173
+ const v = step[key];
174
+ if (typeof v !== "number") continue;
175
+ best = v;
176
+ bestLevel = step.level;
177
+ }
178
+ return best;
179
+ }
180
+ function applyEffect(out, eff, label, level) {
181
+ var _a, _b, _c, _d, _e;
182
+ switch (eff.kind) {
183
+ case "initiative-bonus":
184
+ out.initiative_bonus += eff.value;
185
+ break;
186
+ case "hp-per-level-bonus":
187
+ out.hp_per_level_bonus += eff.value;
188
+ out.hp_per_level_terms.push({ label, value: eff.value });
189
+ break;
190
+ case "speed-bonus": {
191
+ const value = scaled(eff.value, "value", eff.scales_at, level);
192
+ if (eff.mode === "walk") {
193
+ if (eff.set) out.speed_walk_set = Math.max(out.speed_walk_set, value);
194
+ else out.speed_walk_bonus += value;
195
+ }
196
+ break;
197
+ }
198
+ case "sense":
199
+ out.senses[eff.type] = Math.max(out.senses[eff.type], eff.range);
200
+ break;
201
+ case "resistance":
202
+ pushDefenseGrant(out.resistances, eff.damage_type, label, eff.condition);
203
+ break;
204
+ case "immunity":
205
+ pushDefenseGrant(out.immunities, eff.damage_type, label, eff.condition);
206
+ break;
207
+ case "vulnerability":
208
+ pushDefenseGrant(out.vulnerabilities, eff.damage_type, label, eff.condition);
209
+ break;
210
+ case "immune-condition":
211
+ if (!eff.while) pushDefenseGrant(out.condition_immunities, eff.condition, label);
212
+ break;
213
+ case "proficiency": {
214
+ const c = classifyProficiencyEffect(eff);
215
+ if (!c) break;
216
+ if (c.bucket === "saves") {
217
+ pushUnique(out.proficiencies.saves, c.value);
218
+ } else {
219
+ pushUnique(out.proficiencies[c.bucket], c.value);
220
+ }
221
+ if (c.expertise && c.bucket === "skills") pushUnique(out.proficiencies.skillExpertise, c.value);
222
+ break;
223
+ }
224
+ case "ac-bonus":
225
+ out.ac_terms.push({
226
+ value: eff.value,
227
+ requires_armor: eff.requires_armor === true,
228
+ label,
229
+ condition: eff.condition
230
+ });
231
+ break;
232
+ case "weapon-ability": {
233
+ if (eff.ability !== "spellcasting") {
234
+ const w = eff.weapons;
235
+ const weaponSlugs = !w || w === "chosen" ? void 0 : Array.isArray(w) ? w.map(bareEntitySlug) : [bareEntitySlug(w)];
236
+ out.weaponAbilities.push({ ability: eff.ability, weaponSlugs });
237
+ }
238
+ break;
239
+ }
240
+ case "roll-modifier": {
241
+ const rolls = eff.roll === "any" ? ["ability-check", "saving-throw", "attack"] : [eff.roll];
242
+ const scopes = (_a = normalizeRollScope(eff.scope, rolls[0])) != null ? _a : [eff.scope];
243
+ for (const roll of rolls) for (const scope of scopes)
244
+ out.rollModifiers.push({ mode: eff.mode, roll, scope, condition: eff.condition, label });
245
+ break;
246
+ }
247
+ case "save-outcome":
248
+ out.saveOutcomes.push({
249
+ ability: eff.ability === "any" ? void 0 : eff.ability,
250
+ on_success: eff.on_success,
251
+ on_failure: eff.on_failure,
252
+ appliesTo: (_b = eff.applies_to) != null ? _b : void 0,
253
+ condition: eff.condition,
254
+ label
255
+ });
256
+ break;
257
+ case "crit-range":
258
+ if (((_c = eff.applies_to) != null ? _c : "weapon") !== "spell") out.critRange = Math.min(out.critRange, eff.min_roll);
259
+ break;
260
+ case "extra-attack":
261
+ out.extraAttack = Math.max(out.extraAttack, scaled(eff.count, "count", eff.scales_at, level));
262
+ break;
263
+ case "reroll-damage":
264
+ out.attackNotes.push(`Reroll ${eff.max_reroll}s${eff.once_per_die ? " (once/die)" : ""}`);
265
+ break;
266
+ case "attack-rule":
267
+ if (eff.flag === "no-ranged-in-melee-disadvantage") out.attackNotes.push("No disadvantage firing in melee");
268
+ break;
269
+ case "damage-bonus":
270
+ if (((_d = eff.applies_to) != null ? _d : "weapon") !== "spell") {
271
+ out.damageBonuses.push({
272
+ amount: eff.amount,
273
+ damage_type: eff.damage_type,
274
+ source: label,
275
+ ...eff.condition ? { condition: eff.condition } : {}
276
+ });
277
+ }
278
+ break;
279
+ case "ability-score-increase":
280
+ if (eff.abilities === "chosen") break;
281
+ for (const ab of eff.abilities) out.ability_bonus[ab] = ((_e = out.ability_bonus[ab]) != null ? _e : 0) + eff.amount;
282
+ break;
283
+ default:
284
+ break;
285
+ }
286
+ }
287
+
288
+ export {
289
+ emptyFeatureEffectTotals,
290
+ foldsNow,
291
+ assembleEffectFeatures,
292
+ foldsOnSelf,
293
+ selfEffectsOf,
294
+ computeFeatureEffects,
295
+ classifyProficiencyEffect,
296
+ collectProficiencyEffectGrants
297
+ };
@@ -1,9 +1,13 @@
1
1
  import {
2
2
  startingEquipmentEntrySchema
3
- } from "./chunk-3DZ74AKK.js";
3
+ } from "./chunk-IN4M4X7P.js";
4
4
  import {
5
5
  choiceSchema
6
- } from "./chunk-NXSWH3AE.js";
6
+ } from "./chunk-T26ZVCL5.js";
7
+ import {
8
+ additionalSpellsEntrySchema,
9
+ imageField
10
+ } from "./chunk-UA5KS2PL.js";
7
11
 
8
12
  // src/background/background.schema.ts
9
13
  import { z } from "zod";
@@ -75,9 +79,32 @@ var backgroundEntitySchema = z.object({
75
79
  }).nullable(),
76
80
  origin_feat: z.string().regex(wikilinkRegex).nullable(),
77
81
  suggested_characteristics: suggestedCharSchema.nullable(),
78
- choices: z.array(choiceSchema).optional()
82
+ choices: z.array(choiceSchema).optional(),
83
+ rendering_hint: z.string().optional(),
84
+ // 175
85
+ has_fluff_images: z.boolean().optional(),
86
+ // 87
87
+ tables: z.array(z.object({
88
+ // 88 tables / 67 docs; key-sets EXACT (finding 6)
89
+ name: z.string().min(1),
90
+ dice: z.string().min(1),
91
+ rows: z.array(z.object({ roll: z.string(), text: z.string() }))
92
+ })).optional(),
93
+ additional_spells: z.array(additionalSpellsEntrySchema).optional(),
94
+ // 15
95
+ prerequisites: z.array(z.object({
96
+ // 4 docs; kind CLOSED AT THE EMITTER
97
+ kind: z.literal("campaign"),
98
+ slug: z.string().min(1)
99
+ // (background-mapper.ts pushes
100
+ })).optional(),
101
+ // {kind:"campaign", slug} literally — finding 24)
102
+ // The one-arm union STAYS (§2.6 adjudication): it is closed by CONSTRUCTION at the converter's
103
+ // only push site, and a future kind turns into a visible census REFUSAL — the correct loud failure.
104
+ image: imageField
79
105
  });
80
106
 
81
107
  export {
108
+ langProfSchema,
82
109
  backgroundEntitySchema
83
110
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseSpell
3
- } from "./chunk-N2JE7BWL.js";
3
+ } from "./chunk-OZP6JFGT.js";
4
4
 
5
5
  // src/spell/spell.codec.ts
6
6
  import yaml from "js-yaml";
@@ -0,0 +1,106 @@
1
+ // src/types/choice.ts
2
+ var ALL_SKILL_SLUGS = [
3
+ "acrobatics",
4
+ "animal-handling",
5
+ "arcana",
6
+ "athletics",
7
+ "deception",
8
+ "history",
9
+ "insight",
10
+ "intimidation",
11
+ "investigation",
12
+ "medicine",
13
+ "nature",
14
+ "perception",
15
+ "performance",
16
+ "persuasion",
17
+ "religion",
18
+ "sleight-of-hand",
19
+ "stealth",
20
+ "survival"
21
+ ];
22
+ var STANDARD_LANGUAGES = [
23
+ "common",
24
+ "dwarvish",
25
+ "elvish",
26
+ "giant",
27
+ "gnomish",
28
+ "goblin",
29
+ "halfling",
30
+ "orc"
31
+ ];
32
+ var EXOTIC_LANGUAGES = [
33
+ "abyssal",
34
+ "celestial",
35
+ "deep-speech",
36
+ "draconic",
37
+ "infernal",
38
+ "primordial",
39
+ "sylvan",
40
+ "undercommon"
41
+ ];
42
+ var SECRET_LANGUAGES = ["druidic", "thieves'-cant"];
43
+ var ALL_LANGUAGES = [
44
+ ...STANDARD_LANGUAGES,
45
+ ...EXOTIC_LANGUAGES,
46
+ ...SECRET_LANGUAGES
47
+ ];
48
+ var ARTISANS_TOOLS = [
49
+ "alchemist's-supplies",
50
+ "brewer's-supplies",
51
+ "calligrapher's-supplies",
52
+ "carpenter's-tools",
53
+ "cartographer's-tools",
54
+ "cobbler's-tools",
55
+ "cook's-utensils",
56
+ "glassblower's-tools",
57
+ "jeweler's-tools",
58
+ "leatherworker's-tools",
59
+ "mason's-tools",
60
+ "painter's-supplies",
61
+ "potter's-tools",
62
+ "smith's-tools",
63
+ "tinker's-tools",
64
+ "weaver's-tools",
65
+ "woodcarver's-tools"
66
+ ];
67
+ var MUSICAL_INSTRUMENTS = [
68
+ "bagpipes",
69
+ "drum",
70
+ "dulcimer",
71
+ "flute",
72
+ "horn",
73
+ "lute",
74
+ "lyre",
75
+ "pan-flute",
76
+ "shawm",
77
+ "viol"
78
+ ];
79
+ var GAMING_SETS = ["dice-set", "playing-cards"];
80
+ var OTHER_TOOLS = [
81
+ "disguise-kit",
82
+ "forgery-kit",
83
+ "herbalism-kit",
84
+ "navigator's-tools",
85
+ "poisoner's-kit",
86
+ "thieves'-tools"
87
+ ];
88
+ var ALL_TOOLS = [
89
+ ...ARTISANS_TOOLS,
90
+ ...MUSICAL_INSTRUMENTS,
91
+ ...GAMING_SETS,
92
+ ...OTHER_TOOLS
93
+ ];
94
+
95
+ export {
96
+ ALL_SKILL_SLUGS,
97
+ STANDARD_LANGUAGES,
98
+ EXOTIC_LANGUAGES,
99
+ SECRET_LANGUAGES,
100
+ ALL_LANGUAGES,
101
+ ARTISANS_TOOLS,
102
+ MUSICAL_INSTRUMENTS,
103
+ GAMING_SETS,
104
+ OTHER_TOOLS,
105
+ ALL_TOOLS
106
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseWeapon
3
- } from "./chunk-6NK2MUD3.js";
3
+ } from "./chunk-HZA7OE7N.js";
4
4
 
5
5
  // src/weapon/weapon.codec.ts
6
6
  import yaml from "js-yaml";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  backgroundEntitySchema
3
- } from "./chunk-TUIFSVLI.js";
3
+ } from "./chunk-JRDPUDT5.js";
4
4
 
5
5
  // src/background/background.parser.ts
6
6
  import { parseYaml } from "@archivist-gg/core";
@@ -0,0 +1,15 @@
1
+ // src/dnd/warn-once.ts
2
+ var seen = /* @__PURE__ */ new Set();
3
+ function warnOnce(key, msg) {
4
+ if (seen.has(key)) return;
5
+ seen.add(key);
6
+ console.warn(msg);
7
+ }
8
+ function __resetWarnOnceForTests() {
9
+ seen.clear();
10
+ }
11
+
12
+ export {
13
+ warnOnce,
14
+ __resetWarnOnceForTests
15
+ };
@@ -0,0 +1,161 @@
1
+ import {
2
+ RACE_STRUCTURAL_PSEUDO
3
+ } from "./chunk-PVCEA4HZ.js";
4
+ import {
5
+ warnOnce
6
+ } from "./chunk-LVQLWRCU.js";
7
+ import {
8
+ slugify
9
+ } from "./chunk-6X4KUDMU.js";
10
+ import {
11
+ mirrorSpellShapes
12
+ } from "./chunk-OZP6JFGT.js";
13
+ import {
14
+ ABILITY_KEYS
15
+ } from "./chunk-WELIKLZF.js";
16
+
17
+ // src/pc/pc.additional-spells.ts
18
+ var entriesOf = (a) => Array.isArray(a) ? a : a && typeof a === "object" ? [a] : [];
19
+ var editionOf = (x) => {
20
+ const e = x == null ? void 0 : x.edition;
21
+ return typeof e === "string" ? e : void 0;
22
+ };
23
+ var prefixOf = (slug) => {
24
+ var _a;
25
+ return (_a = slug.split("_")[0]) != null ? _a : slug;
26
+ };
27
+ function normalizeSpellRef(ref) {
28
+ var _a, _b, _c;
29
+ if (typeof ref !== "string") return null;
30
+ let s = ref.trim();
31
+ const wl = /^\[\[(.+)\]\]$/.exec(s);
32
+ if (wl) {
33
+ const target = (_a = wl[1].split("|")[0]) != null ? _a : "";
34
+ const tail = (_b = target.split("/").pop()) != null ? _b : target;
35
+ const w = slugify(tail.replace(/#.*$/, "").trim());
36
+ return w.length > 0 ? w : null;
37
+ }
38
+ s = s.replace(/#.*$/, "");
39
+ s = (_c = s.split("|")[0]) != null ? _c : "";
40
+ const out = slugify(s.trim());
41
+ return out.length > 0 ? out : null;
42
+ }
43
+ function buildSpellNameIndex(entities) {
44
+ var _a;
45
+ const byName = /* @__PURE__ */ new Map();
46
+ for (const e of entities.search("", "spell", Number.POSITIVE_INFINITY)) {
47
+ const raw = e.data.name;
48
+ const key = slugify(typeof raw === "string" ? raw : e.name);
49
+ const list = (_a = byName.get(key)) != null ? _a : [];
50
+ list.push(e);
51
+ byName.set(key, list);
52
+ }
53
+ return { byName };
54
+ }
55
+ function resolveSpellByName(opts) {
56
+ var _a;
57
+ const { entities, index, nameSlug, carrierSlug, carrierEdition, alreadyCollected } = opts;
58
+ const own = alreadyCollected.find((s) => {
59
+ var _a2;
60
+ return slugify(String((_a2 = s.entity.name) != null ? _a2 : "")) === nameSlug;
61
+ });
62
+ if (own) return { entity: own.entity, slug: own.slug };
63
+ const exact = entities.getByTypeAndSlug("spell", `${prefixOf(carrierSlug)}_spell_${nameSlug}`);
64
+ if (exact) return { entity: mirrorSpellShapes(exact.data), slug: exact.slug };
65
+ const cands = (_a = index.byName.get(nameSlug)) != null ? _a : [];
66
+ if (cands.length === 0) {
67
+ warnOnce(`addspells:miss:${nameSlug}`, `archivist: additional_spells ref "${nameSlug}" matches no spell in the compendium`);
68
+ return null;
69
+ }
70
+ const prefix = prefixOf(carrierSlug);
71
+ let pool = cands.filter((e) => prefixOf(e.slug) === prefix);
72
+ if (pool.length === 0) pool = cands;
73
+ if (carrierEdition !== void 0) {
74
+ const same = pool.filter((e) => editionOf(e.data) === carrierEdition);
75
+ if (same.length > 0) pool = same;
76
+ }
77
+ pool = [...pool].sort((a, b) => a.slug < b.slug ? -1 : a.slug > b.slug ? 1 : 0);
78
+ if (pool.length > 1) warnOnce(`addspells:multi:${nameSlug}`, `archivist: additional_spells ref "${nameSlug}" matches ${cands.length} spells; using "${pool[0].slug}"`);
79
+ return { entity: mirrorSpellShapes(pool[0].data), slug: pool[0].slug };
80
+ }
81
+ var hasContentSelectInline = (choices) => Array.isArray(choices) && choices.some((c) => (c == null ? void 0 : c.kind) === "select-inline" && typeof c.id === "string" && !RACE_STRUCTURAL_PSEUDO.has(c.id) || Array.isArray(c == null ? void 0 : c.options) && c.options.some((o) => hasContentSelectInline(o == null ? void 0 : o.choices)));
82
+ function raceIsUngated(race) {
83
+ var _a;
84
+ const r = race;
85
+ if (hasContentSelectInline(r.choices)) return false;
86
+ return !((_a = r.traits) != null ? _a : []).some((t) => hasContentSelectInline(t == null ? void 0 : t.choices));
87
+ }
88
+ var readAbility = (v, own) => {
89
+ if (v && typeof v === "object" && "choose" in v) return "choose";
90
+ if (typeof v !== "string") return void 0;
91
+ if (v === "inherit") return own != null ? own : void 0;
92
+ return ABILITY_KEYS.includes(v) ? v : void 0;
93
+ };
94
+ var entryHasInSliceLeaf = (e, comparand, own) => readAbility(e.ability, own) !== "choose" && ["known", "prepared"].some((b) => {
95
+ const lv = e[b];
96
+ return !!lv && typeof lv === "object" && !Array.isArray(lv) && Object.entries(lv).some(([k, v]) => (k === "_" || Number(k) <= comparand) && Array.isArray(v) && v.some((x) => typeof x === "string"));
97
+ });
98
+ function collectAdditionalSpells(args) {
99
+ const { race, feats, classes, totalLevel, ownAbility, alreadyCollected, entities, warnings } = args;
100
+ let index = null;
101
+ const indexOf = () => index != null ? index : index = buildSpellNameIndex(entities);
102
+ const out = [];
103
+ const carriers = [];
104
+ const asRaw = (x) => x == null ? void 0 : x.additional_spells;
105
+ if (race && raceIsUngated(race)) carriers.push({ root: "race", slug: race.slug, edition: editionOf(race), entries: entriesOf(asRaw(race)), comparand: totalLevel, classSlug: null });
106
+ for (const f of feats) carriers.push({ root: "feat", slug: f.slug, edition: editionOf(f), entries: entriesOf(asRaw(f)), comparand: totalLevel, classSlug: null });
107
+ for (const c of classes) {
108
+ if (!c.entity) continue;
109
+ carriers.push({ root: "class", slug: c.entity.slug, edition: editionOf(c.entity), entries: entriesOf(asRaw(c.entity)), comparand: c.level, classSlug: c.entity.slug });
110
+ if (c.subclass) carriers.push({ root: "subclass", slug: c.subclass.slug, edition: editionOf(c.subclass), entries: entriesOf(asRaw(c.subclass)), comparand: c.level, classSlug: c.entity.slug });
111
+ }
112
+ for (const car of carriers) {
113
+ if (car.entries.length === 0) continue;
114
+ if (car.entries.length > 1) {
115
+ if (car.entries.some((e) => entryHasInSliceLeaf(e, car.comparand, ownAbility))) warnOnce(`addspells:siblings:${car.slug}`, `archivist: additional_spells on "${car.slug}" has ${car.entries.length} sibling entries (a choose-one grant); skipped`);
116
+ continue;
117
+ }
118
+ const entry = car.entries[0];
119
+ const ability = readAbility(entry.ability, ownAbility);
120
+ if (ability === "choose") continue;
121
+ for (const bucket of ["known", "prepared"]) {
122
+ const levels = entry[bucket];
123
+ if (!levels || typeof levels !== "object" || Array.isArray(levels)) continue;
124
+ for (const [k, v] of Object.entries(levels)) {
125
+ if (!(k === "_" || Number(k) <= car.comparand)) continue;
126
+ if (!Array.isArray(v)) {
127
+ warnOnce(`addspells:usage:${car.slug}:${bucket}:${k}`, `archivist: additional_spells on "${car.slug}" ${bucket}.${k} is a usage economy (skipped; G8)`);
128
+ continue;
129
+ }
130
+ for (const leaf of v) {
131
+ const nameSlug = normalizeSpellRef(leaf);
132
+ if (!nameSlug) continue;
133
+ const hit = resolveSpellByName({ entities, index: indexOf(), nameSlug, carrierSlug: car.slug, carrierEdition: car.edition, alreadyCollected: [...alreadyCollected, ...out] });
134
+ if (!hit) {
135
+ warnings.push(`additional_spells ref "${String(leaf)}" on ${car.slug} not found in compendium.`);
136
+ continue;
137
+ }
138
+ out.push({
139
+ entity: hit.entity,
140
+ slug: hit.slug,
141
+ classSlug: car.classSlug,
142
+ source: car.root === "race" ? "race" : car.root === "feat" ? "feat" : "class",
143
+ prepared: true,
144
+ alwaysPrepared: true,
145
+ ...ability ? { ability } : {}
146
+ });
147
+ }
148
+ }
149
+ }
150
+ }
151
+ return out;
152
+ }
153
+
154
+ export {
155
+ prefixOf,
156
+ normalizeSpellRef,
157
+ buildSpellNameIndex,
158
+ resolveSpellByName,
159
+ raceIsUngated,
160
+ collectAdditionalSpells
161
+ };