@archivist-gg/dnd5e 0.3.11 → 0.3.17
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/LICENSES/SRD.md +38 -3
- package/dist/armor/armor.codec.js +3 -3
- package/dist/armor/armor.parser.js +2 -2
- package/dist/armor/armor.schema.d.ts +1 -0
- package/dist/armor/armor.schema.js +1 -1
- package/dist/armor/armor.types.d.ts +2 -0
- package/dist/background/background.codec.js +5 -5
- package/dist/background/background.parser.js +4 -4
- package/dist/background/background.schema.js +3 -3
- package/dist/{chunk-XTRDCSJB.js → chunk-2JSQE3DD.js} +15 -1
- package/dist/chunk-2LULMQTF.js +14 -0
- package/dist/{chunk-MAH6DCRI.js → chunk-2UDSN6VW.js} +1 -1
- package/dist/chunk-3LWMFYKS.js +57 -0
- package/dist/{chunk-JCEV7KYN.js → chunk-3MGQFFNE.js} +1 -1
- package/dist/chunk-3VZZDKRA.js +1014 -0
- package/dist/{chunk-2DFENQIJ.js → chunk-5GKJAUMX.js} +2 -0
- package/dist/{chunk-2M34FLAY.js → chunk-5MXFW4O3.js} +9 -3
- package/dist/{chunk-4DOMDEBZ.js → chunk-6EI737IO.js} +1 -1
- package/dist/{chunk-G247QXWW.js → chunk-7ATNGKBS.js} +1 -1
- package/dist/{chunk-XY6X2APM.js → chunk-7Q2VPKCD.js} +17 -0
- package/dist/{chunk-T2SNEJOE.js → chunk-7QN7XSAK.js} +1 -1
- package/dist/{chunk-DNAJRMNN.js → chunk-A54HHCWW.js} +6 -7
- package/dist/{chunk-7YU4NPKR.js → chunk-AZRGFUSR.js} +1 -1
- package/dist/{chunk-PP2UHLBA.js → chunk-B3DP42OM.js} +5 -0
- package/dist/chunk-CM5CWO4K.js +735 -0
- package/dist/{chunk-MQ747UCR.js → chunk-DOXSFYLH.js} +70 -2
- package/dist/chunk-EJP263OP.js +186 -0
- package/dist/{chunk-SMLXZUBH.js → chunk-EYX4JMAU.js} +4 -1
- package/dist/{chunk-V6LJFP4R.js → chunk-FANQVQ3D.js} +2 -2
- package/dist/{chunk-AW3EHF3L.js → chunk-FC4IGH5Y.js} +1 -1
- package/dist/{chunk-23LZLBJK.js → chunk-G6MHJDVT.js} +6 -2
- package/dist/{chunk-JANNRGJC.js → chunk-G6PBI2BU.js} +1 -1
- package/dist/chunk-H5PTKYXL.js +101 -0
- package/dist/{chunk-LWFTHJIE.js → chunk-HHBD4SQ6.js} +1 -1
- package/dist/{chunk-JC5UNIVM.js → chunk-HS6FIOLP.js} +2 -0
- package/dist/chunk-HTYMW5CH.js +228 -0
- package/dist/chunk-IHE6KVC3.js +13 -0
- package/dist/{chunk-QHMWA3SZ.js → chunk-JCMUVZIK.js} +1 -1
- package/dist/{chunk-V373TRJE.js → chunk-JSM6JFZR.js} +198 -125
- package/dist/{chunk-LDDCNPHD.js → chunk-K5NJBTE2.js} +1 -1
- package/dist/chunk-KR6JF3BF.js +14 -0
- package/dist/{chunk-4ISR76DH.js → chunk-KSTV7J2U.js} +9 -3
- package/dist/chunk-LBEXLN6I.js +91 -0
- package/dist/{chunk-BLRW2UU6.js → chunk-LF5AGJA4.js} +2 -2
- package/dist/{chunk-UBBKSYBY.js → chunk-LHZXYFBU.js} +30 -1
- package/dist/{chunk-BIA32MQ5.js → chunk-MBX25B6D.js} +1 -1
- package/dist/{chunk-R2ZV233X.js → chunk-MYY2C5MB.js} +3 -3
- package/dist/{chunk-J77CKYV3.js → chunk-OFCSP6MT.js} +1 -1
- package/dist/{chunk-2INMRR3Q.js → chunk-ONTGZR5T.js} +326 -149
- package/dist/{chunk-MWCCGPG3.js → chunk-OOSRUJBI.js} +3 -2
- package/dist/{chunk-U3BABVJF.js → chunk-PKX6TGRE.js} +2 -2
- package/dist/{chunk-PJMG5XV3.js → chunk-PQZ4LM4Z.js} +1 -1
- package/dist/chunk-PXVNAY7T.js +84 -0
- package/dist/chunk-QH36K2PU.js +158 -0
- package/dist/{chunk-R747YKS7.js → chunk-QZSWN2KT.js} +1 -1
- package/dist/{chunk-CHUALUIX.js → chunk-RYLXAYIQ.js} +1 -1
- package/dist/{chunk-GFZ7PITJ.js → chunk-S3YUXDSC.js} +6 -1
- package/dist/{chunk-PZCE7DJM.js → chunk-T6QIF5XR.js} +31 -1
- package/dist/{chunk-CBEKVAX4.js → chunk-TULQ4FFX.js} +41 -13
- package/dist/{chunk-S56D6B6C.js → chunk-UUBJCNQU.js} +12 -3
- package/dist/{chunk-5N2GW72F.js → chunk-UZXG6QSY.js} +2 -1
- package/dist/{chunk-4XTNPNQB.js → chunk-VPYBK2MG.js} +1 -1
- package/dist/{chunk-3HX5VLWP.js → chunk-VRV3NNTA.js} +2 -2
- package/dist/{chunk-HZA7OE7N.js → chunk-XCFC7LGF.js} +2 -1
- package/dist/{chunk-BL3VELKP.js → chunk-Y5VVP73C.js} +1 -1
- package/dist/{chunk-PI4LLJG4.js → chunk-YD2VAHA6.js} +2 -2
- package/dist/{chunk-UIRMA5KM.js → chunk-YDGMBMYI.js} +1 -1
- package/dist/{chunk-NWDF5HG3.js → chunk-YKL6SHRJ.js} +1 -1
- package/dist/{chunk-6X4KUDMU.js → chunk-ZCTUYGNV.js} +7 -2
- package/dist/chunk-ZUQW5VBZ.js +89 -0
- package/dist/class/class.codec.js +7 -7
- package/dist/class/class.parser.js +6 -6
- package/dist/class/class.schema.d.ts +18 -0
- package/dist/class/class.schema.js +5 -5
- package/dist/class/class.standard-array.d.ts +16 -0
- package/dist/class/class.standard-array.js +20 -0
- package/dist/class/class.types.d.ts +3 -0
- package/dist/dnd/amount-text.d.ts +38 -0
- package/dist/dnd/amount-text.js +13 -0
- package/dist/dnd/resource-words.d.ts +4 -0
- package/dist/dnd/resource-words.js +30 -0
- package/dist/entities/base-item-resolver.js +2 -2
- package/dist/entities/slug.d.ts +18 -8
- package/dist/entities/slug.js +3 -1
- package/dist/feat/feat.codec.js +5 -5
- package/dist/feat/feat.parser.js +4 -4
- package/dist/feat/feat.schema.d.ts +41 -3
- package/dist/feat/feat.schema.js +3 -3
- package/dist/index.js +7513 -4177
- package/dist/item/item.actions-map.d.ts +40 -5
- package/dist/item/item.actions-map.js +5 -84
- package/dist/item/item.attunement.js +3 -9
- package/dist/item/item.bonuses.js +5 -2
- package/dist/item/item.codec.js +3 -3
- package/dist/item/item.conditions.js +4 -1
- package/dist/item/item.conditions.types.d.ts +4 -0
- package/dist/item/item.cost.d.ts +3 -0
- package/dist/item/item.cost.js +29 -0
- package/dist/item/item.icon.d.ts +15 -0
- package/dist/item/item.icon.js +211 -0
- package/dist/item/item.parser.js +2 -2
- package/dist/item/item.schema.d.ts +16 -1
- package/dist/item/item.schema.js +3 -1
- package/dist/item/item.types.d.ts +9 -2
- package/dist/monster/monster.parser.js +5 -5
- package/dist/monster/monster.schema.js +4 -4
- package/dist/optional-feature/optional-feature.codec.js +5 -4
- package/dist/optional-feature/optional-feature.parser.js +4 -3
- package/dist/optional-feature/optional-feature.schema.d.ts +255 -3
- package/dist/optional-feature/optional-feature.schema.js +7 -2
- package/dist/pc/pc.additional-spells.js +3 -3
- package/dist/pc/pc.amount-bindings.d.ts +32 -0
- package/dist/pc/pc.amount-bindings.js +23 -0
- package/dist/pc/pc.asi-flatten.d.ts +62 -1
- package/dist/pc/pc.asi-flatten.js +16 -0
- package/dist/pc/pc.conditions.js +1 -1
- package/dist/pc/pc.containment.d.ts +45 -0
- package/dist/pc/pc.containment.js +24 -0
- package/dist/pc/pc.decision-engine.d.ts +24 -3
- package/dist/pc/pc.decision-engine.js +26 -16
- package/dist/pc/pc.entry-overrides.d.ts +79 -0
- package/dist/pc/pc.entry-overrides.js +40 -0
- package/dist/pc/pc.equipment.d.ts +2 -2
- package/dist/pc/pc.equipment.js +19 -7
- package/dist/pc/pc.feature-effects.d.ts +12 -0
- package/dist/pc/pc.feature-effects.js +2 -2
- package/dist/pc/pc.infusions.d.ts +228 -0
- package/dist/pc/pc.infusions.js +64 -0
- package/dist/pc/pc.member-choices.d.ts +90 -0
- package/dist/pc/pc.member-choices.js +32 -0
- package/dist/pc/pc.parser.js +18 -1
- package/dist/pc/pc.pools.d.ts +8 -1
- package/dist/pc/pc.pools.js +13 -3
- package/dist/pc/pc.proficiencies.js +23 -15
- package/dist/pc/pc.proficiency-query.js +6 -0
- package/dist/pc/pc.recalc.d.ts +39 -36
- package/dist/pc/pc.recalc.js +115 -70
- package/dist/pc/pc.resolver.d.ts +2 -1
- package/dist/pc/pc.resolver.js +22 -14
- package/dist/pc/pc.resource-seed.js +1 -1
- package/dist/pc/pc.resources.d.ts +6 -1
- package/dist/pc/pc.resources.js +3 -1
- package/dist/pc/pc.rest.d.ts +21 -1
- package/dist/pc/pc.rest.js +52 -19
- package/dist/pc/pc.schema.d.ts +43 -2
- package/dist/pc/pc.schema.js +1 -1
- package/dist/pc/pc.slotting.d.ts +36 -3
- package/dist/pc/pc.slotting.js +13 -5
- package/dist/pc/pc.tool-picks.d.ts +51 -0
- package/dist/pc/pc.tool-picks.js +18 -0
- package/dist/pc/pc.types.d.ts +115 -14
- package/dist/pc/pc.weapon-grant.d.ts +15 -0
- package/dist/pc/pool-layout.d.ts +8 -0
- package/dist/pc/pool-layout.js +5 -3
- package/dist/pc/resource-equivalents.d.ts +44 -0
- package/dist/pc/spell-source.js +4 -9
- package/dist/race/race.codec.js +6 -6
- package/dist/race/race.parser.js +5 -5
- package/dist/race/race.schema.js +4 -4
- package/dist/schemas/attack-schema.d.ts +1 -1
- package/dist/schemas/choice-schema.js +2 -2
- package/dist/schemas/feature-effect-schema.d.ts +41 -3
- package/dist/schemas/feature-effect-schema.js +1 -1
- package/dist/schemas/feature-schema.js +3 -3
- package/dist/schemas/selection-pool-schema.d.ts +10 -0
- package/dist/schemas/selection-pool-schema.js +1 -1
- package/dist/spell/spell.codec.js +2 -2
- package/dist/spell/spell.parser.d.ts +6 -1
- package/dist/spell/spell.parser.js +5 -3
- package/dist/spell/spell.types.d.ts +10 -0
- package/dist/srd/retired-items.d.ts +14 -0
- package/dist/srd/retired-items.js +11 -0
- package/dist/subclass/subclass.codec.js +7 -7
- package/dist/subclass/subclass.parser.js +6 -6
- package/dist/subclass/subclass.schema.d.ts +10 -0
- package/dist/subclass/subclass.schema.js +5 -5
- package/dist/types/choice.d.ts +13 -0
- package/dist/types/equipment-grant.d.ts +18 -1
- package/dist/types/equipment-grant.js +12 -0
- package/dist/types/feature-effect.d.ts +12 -1
- package/dist/types/optional-feature.types.d.ts +84 -1
- package/dist/types/selection-pool.d.ts +18 -3
- package/dist/weapon/weapon.classify.d.ts +10 -8
- package/dist/weapon/weapon.classify.js +2 -2
- package/dist/weapon/weapon.codec.js +3 -3
- package/dist/weapon/weapon.parser.js +2 -2
- package/dist/weapon/weapon.schema.d.ts +1 -0
- package/dist/weapon/weapon.schema.js +1 -1
- package/dist/weapon/weapon.types.d.ts +2 -0
- package/package.json +1 -1
- package/dist/chunk-O4SRD3J5.js +0 -165
- package/dist/chunk-WXWPK3LI.js +0 -55
|
@@ -1,34 +1,46 @@
|
|
|
1
|
+
import {
|
|
2
|
+
qualifiedGrantCovers
|
|
3
|
+
} from "./chunk-3LWMFYKS.js";
|
|
1
4
|
import {
|
|
2
5
|
classifyWeaponRange
|
|
3
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-YDGMBMYI.js";
|
|
4
7
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
isItemEntity,
|
|
8
|
-
isWeaponEntity,
|
|
9
|
-
resolveEntityForEntry
|
|
10
|
-
} from "./chunk-UBBKSYBY.js";
|
|
8
|
+
resolveContainment
|
|
9
|
+
} from "./chunk-EJP263OP.js";
|
|
11
10
|
import {
|
|
12
11
|
normKey
|
|
13
12
|
} from "./chunk-RLGQ5PZZ.js";
|
|
13
|
+
import {
|
|
14
|
+
WEAPON_DEFAULT_ACTION_COST,
|
|
15
|
+
resolveEntryEntity
|
|
16
|
+
} from "./chunk-3VZZDKRA.js";
|
|
14
17
|
import {
|
|
15
18
|
readNumericBonus
|
|
16
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-HHBD4SQ6.js";
|
|
20
|
+
import {
|
|
21
|
+
defaultSlotForType,
|
|
22
|
+
effectiveArmor,
|
|
23
|
+
effectiveWeapon,
|
|
24
|
+
isItemEntity,
|
|
25
|
+
isShieldArmor,
|
|
26
|
+
isTwoHandedWeapon,
|
|
27
|
+
slotAcTerms
|
|
28
|
+
} from "./chunk-LHZXYFBU.js";
|
|
17
29
|
import {
|
|
18
30
|
resolveBaseItem
|
|
19
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-CM5CWO4K.js";
|
|
20
32
|
import {
|
|
21
33
|
bareEntitySlug
|
|
22
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-ZCTUYGNV.js";
|
|
23
35
|
|
|
24
36
|
// src/weapon/weapon-mastery.ts
|
|
25
37
|
var MASTERY = {
|
|
26
38
|
cleave: { label: "Cleave", description: "If you hit a creature with a melee attack roll using this weapon, you can make an attack roll with the weapon against a second creature within 5 feet of the first that is also within your reach. On a hit, the second creature takes the weapon's damage, but don't add your ability modifier to that damage unless it is negative. You can make this extra attack only once per turn.", gist: "hit a 2nd creature in reach" },
|
|
27
39
|
graze: { label: "Graze", description: "If your attack roll with this weapon misses a creature, you can deal damage to that creature equal to the ability modifier you used to make the attack roll. This damage is the same type dealt by the weapon, and the damage can be increased only by increasing the ability modifier.", gist: "on miss: ability-mod damage" },
|
|
28
40
|
nick: { label: "Nick", description: "When you make the extra attack of the Light property, you can make it as part of the Attack action instead of as a Bonus Action. You can make this extra attack only once per turn.", gist: "extra Light attack (no Bonus Action)" },
|
|
29
|
-
push: { label: "Push", description: "If you hit a creature with this weapon, you can push the creature up to 10 feet straight away from yourself if it is Large or smaller.", gist: "on hit: pushed 10 ft" },
|
|
41
|
+
push: { label: "Push", description: "If you hit a creature with this weapon, you can push the creature up to 10 feet straight away from yourself if it is Large or smaller.", gist: "on hit: pushed 10 ft." },
|
|
30
42
|
sap: { label: "Sap", description: "If you hit a creature with this weapon, that creature has Disadvantage on its next attack roll before the start of your next turn.", gist: "on hit: target Disadvantage" },
|
|
31
|
-
slow: { label: "Slow", description: "If you hit a creature with this weapon and deal damage, you can reduce its Speed by 10 feet until the start of your next turn. If the creature is hit more than once by weapons that have this property, the Speed reduction doesn't exceed 10 feet.", gist: "on hit: -10 ft Speed" },
|
|
43
|
+
slow: { label: "Slow", description: "If you hit a creature with this weapon and deal damage, you can reduce its Speed by 10 feet until the start of your next turn. If the creature is hit more than once by weapons that have this property, the Speed reduction doesn't exceed 10 feet.", gist: "on hit: -10 ft. Speed" },
|
|
32
44
|
topple: { label: "Topple", description: "If you hit a creature with this weapon, you can force the creature to make a Constitution saving throw (DC 8 plus the ability modifier used to make the attack roll and your Proficiency Bonus). On a failed save, the creature has the Prone condition.", gist: "on fail: Prone" },
|
|
33
45
|
vex: { label: "Vex", description: "If you hit a creature with this weapon and deal damage, you have Advantage on your next attack roll against that creature before the end of your next turn.", gist: "on hit: you gain Advantage next" }
|
|
34
46
|
};
|
|
@@ -52,6 +64,7 @@ function emptyAppliedBonuses() {
|
|
|
52
64
|
ability_bonuses: {},
|
|
53
65
|
ability_statics: {},
|
|
54
66
|
save_bonus: 0,
|
|
67
|
+
save_terms: [],
|
|
55
68
|
speed_bonuses: { walk: 0, fly: null, swim: 0, climb: 0 },
|
|
56
69
|
spell_attack: 0,
|
|
57
70
|
spell_save_dc: 0,
|
|
@@ -75,13 +88,15 @@ function unwrapClassSlug(maybeWiki) {
|
|
|
75
88
|
const m = /^\[\[([^\]]+)\]\]$/.exec(maybeWiki);
|
|
76
89
|
return (m ? m[1] : maybeWiki).toLowerCase();
|
|
77
90
|
}
|
|
78
|
-
function buildConditionContext(resolved, equippedSlots) {
|
|
91
|
+
function buildConditionContext(resolved, equippedSlots, registry) {
|
|
79
92
|
var _a, _b;
|
|
80
93
|
return {
|
|
81
94
|
derived: { equippedSlots },
|
|
82
95
|
classList: (_a = resolved.definition.class) != null ? _a : [],
|
|
83
96
|
race: resolved.definition.race,
|
|
84
|
-
subclasses: ((_b = resolved.definition.class) != null ? _b : []).map((c) => c.subclass).filter((s) => typeof s === "string" && s.length > 0).map(unwrapClassSlug)
|
|
97
|
+
subclasses: ((_b = resolved.definition.class) != null ? _b : []).map((c) => c.subclass).filter((s) => typeof s === "string" && s.length > 0).map(unwrapClassSlug),
|
|
98
|
+
// H234 H2: `wielding_two_handed` reads a magic main-hand weapon's base through it.
|
|
99
|
+
registry
|
|
85
100
|
};
|
|
86
101
|
}
|
|
87
102
|
function pushApply(outcome, field, source, pool, onApply) {
|
|
@@ -92,29 +107,30 @@ function pushApply(outcome, field, source, pool, onApply) {
|
|
|
92
107
|
}
|
|
93
108
|
}
|
|
94
109
|
function computeAppliedBonuses(resolved, _profs, registry, warnings) {
|
|
95
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
|
|
110
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s;
|
|
96
111
|
const out = emptyAppliedBonuses();
|
|
97
112
|
const equipment = (_a = resolved.definition.equipment) != null ? _a : [];
|
|
98
|
-
const ctx = buildConditionContext(resolved, {});
|
|
99
|
-
for (
|
|
100
|
-
const
|
|
113
|
+
const ctx = buildConditionContext(resolved, {}, registry);
|
|
114
|
+
for (let index = 0; index < equipment.length; index++) {
|
|
115
|
+
const entry = equipment[index];
|
|
116
|
+
const { entity, entityType } = resolveEntryEntity(entry, index, registry, resolved.infusions);
|
|
101
117
|
if (!entity) {
|
|
102
118
|
const slug = unwrapSlug(entry.item);
|
|
103
|
-
if (slug && entry.equipped) warnings.push(`Equipped item [[${slug}]] not found in compendium.`);
|
|
119
|
+
if (slug && entry.equipped && !((_b = resolved.infusions) == null ? void 0 : _b.views.has(index))) warnings.push(`Equipped item [[${slug}]] not found in compendium.`);
|
|
104
120
|
continue;
|
|
105
121
|
}
|
|
106
122
|
if (!isAttunedActive(entry, entity)) continue;
|
|
107
123
|
if (entityType !== "item") continue;
|
|
108
124
|
const item = entity;
|
|
109
|
-
(
|
|
110
|
-
(
|
|
111
|
-
(
|
|
112
|
-
(
|
|
113
|
-
(
|
|
114
|
-
(
|
|
115
|
-
(
|
|
116
|
-
(
|
|
117
|
-
const gs = (
|
|
125
|
+
(_c = item.resist) == null ? void 0 : _c.forEach((s) => out.defenses.resistances.push(s));
|
|
126
|
+
(_d = item.immune) == null ? void 0 : _d.forEach((s) => out.defenses.immunities.push(s));
|
|
127
|
+
(_e = item.vulnerable) == null ? void 0 : _e.forEach((s) => out.defenses.vulnerabilities.push(s));
|
|
128
|
+
(_f = item.condition_immune) == null ? void 0 : _f.forEach((s) => out.defenses.condition_immunities.push(s));
|
|
129
|
+
(_h = (_g = entry.overrides) == null ? void 0 : _g.resist) == null ? void 0 : _h.forEach((s) => out.defenses.resistances.push(s));
|
|
130
|
+
(_j = (_i = entry.overrides) == null ? void 0 : _i.immune) == null ? void 0 : _j.forEach((s) => out.defenses.immunities.push(s));
|
|
131
|
+
(_l = (_k = entry.overrides) == null ? void 0 : _k.vulnerable) == null ? void 0 : _l.forEach((s) => out.defenses.vulnerabilities.push(s));
|
|
132
|
+
(_n = (_m = entry.overrides) == null ? void 0 : _m.condition_immune) == null ? void 0 : _n.forEach((s) => out.defenses.condition_immunities.push(s));
|
|
133
|
+
const gs = (_o = item.grants) == null ? void 0 : _o.senses;
|
|
118
134
|
if (gs) {
|
|
119
135
|
if (typeof gs.darkvision === "number") out.senses.darkvision = Math.max(out.senses.darkvision, gs.darkvision);
|
|
120
136
|
if (typeof gs.blindsight === "number") out.senses.blindsight = Math.max(out.senses.blindsight, gs.blindsight);
|
|
@@ -123,13 +139,15 @@ function computeAppliedBonuses(resolved, _profs, registry, warnings) {
|
|
|
123
139
|
}
|
|
124
140
|
const b = item.bonuses;
|
|
125
141
|
if (!b) continue;
|
|
142
|
+
const saveSource = (_q = (_p = entry.overrides) == null ? void 0 : _p.name) != null ? _q : item.name;
|
|
126
143
|
pushApply(
|
|
127
144
|
readNumericBonus(b.saving_throws, ctx),
|
|
128
145
|
"saving_throws",
|
|
129
|
-
|
|
146
|
+
saveSource,
|
|
130
147
|
out.informational,
|
|
131
148
|
(v) => {
|
|
132
149
|
out.save_bonus += v;
|
|
150
|
+
out.save_terms.push({ source: saveSource, amount: v });
|
|
133
151
|
}
|
|
134
152
|
);
|
|
135
153
|
pushApply(
|
|
@@ -150,7 +168,7 @@ function computeAppliedBonuses(resolved, _profs, registry, warnings) {
|
|
|
150
168
|
out.spell_save_dc += v;
|
|
151
169
|
}
|
|
152
170
|
);
|
|
153
|
-
if ((
|
|
171
|
+
if ((_r = b.ability_scores) == null ? void 0 : _r.bonus) {
|
|
154
172
|
for (const [k, v] of Object.entries(b.ability_scores.bonus)) {
|
|
155
173
|
if (!isAbilityKey(k)) continue;
|
|
156
174
|
pushApply(
|
|
@@ -165,7 +183,7 @@ function computeAppliedBonuses(resolved, _profs, registry, warnings) {
|
|
|
165
183
|
);
|
|
166
184
|
}
|
|
167
185
|
}
|
|
168
|
-
if ((
|
|
186
|
+
if ((_s = b.ability_scores) == null ? void 0 : _s.static) {
|
|
169
187
|
for (const [k, n] of Object.entries(b.ability_scores.static)) {
|
|
170
188
|
if (!isAbilityKey(k) || typeof n !== "number") continue;
|
|
171
189
|
const prev = out.ability_statics[k];
|
|
@@ -227,28 +245,22 @@ function computeAppliedBonuses(resolved, _profs, registry, warnings) {
|
|
|
227
245
|
}
|
|
228
246
|
return out;
|
|
229
247
|
}
|
|
230
|
-
function isTwoHanded(weapon) {
|
|
231
|
-
return weapon.properties.some((p) => p === "two_handed");
|
|
232
|
-
}
|
|
233
248
|
function assignSlots(resolved, registry, warnings) {
|
|
234
|
-
var _a
|
|
249
|
+
var _a;
|
|
235
250
|
const slots = {};
|
|
236
251
|
const eq = (_a = resolved.definition.equipment) != null ? _a : [];
|
|
237
252
|
for (let i = 0; i < eq.length; i++) {
|
|
238
253
|
const entry = eq[i];
|
|
239
254
|
if (!entry.equipped || !entry.slot) continue;
|
|
240
|
-
const { entity, entityType } =
|
|
241
|
-
if (slots[entry.slot])
|
|
242
|
-
warnings.push(`${entry.slot} slot conflict: ${entry.item} ignored (already taken).`);
|
|
243
|
-
continue;
|
|
244
|
-
}
|
|
255
|
+
const { entity, entityType } = resolveEntryEntity(entry, i, registry, resolved.infusions);
|
|
256
|
+
if (slots[entry.slot]) continue;
|
|
245
257
|
const placed = { index: i, entity, entityType, entry };
|
|
246
258
|
slots[entry.slot] = placed;
|
|
247
259
|
}
|
|
248
260
|
for (let i = 0; i < eq.length; i++) {
|
|
249
261
|
const entry = eq[i];
|
|
250
262
|
if (!entry.equipped || entry.slot) continue;
|
|
251
|
-
const { entity, entityType } =
|
|
263
|
+
const { entity, entityType } = resolveEntryEntity(entry, i, registry, resolved.infusions);
|
|
252
264
|
if (!entity || !entityType) continue;
|
|
253
265
|
const defaultSlot = defaultSlotForType(entityType, entity, registry);
|
|
254
266
|
if (!defaultSlot) {
|
|
@@ -263,23 +275,45 @@ function assignSlots(resolved, registry, warnings) {
|
|
|
263
275
|
if (defaultSlot === "mainhand") {
|
|
264
276
|
if (!slots.mainhand) slots.mainhand = placed;
|
|
265
277
|
else if (!slots.offhand) slots.offhand = placed;
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
if (slots[defaultSlot]) {
|
|
269
|
-
warnings.push(`${defaultSlot} slot already filled; ${entry.item} ignored.`);
|
|
270
|
-
} else {
|
|
271
|
-
slots[defaultSlot] = placed;
|
|
272
|
-
}
|
|
278
|
+
} else if (!slots[defaultSlot]) {
|
|
279
|
+
slots[defaultSlot] = placed;
|
|
273
280
|
}
|
|
274
281
|
}
|
|
275
|
-
const main = (_b = slots.mainhand) == null ? void 0 : _b.entity;
|
|
276
|
-
if (main && isWeaponEntity(main) && isTwoHanded(main) && slots.shield) {
|
|
277
|
-
warnings.push(`Two-handed weapon equipped; shield ignored.`);
|
|
278
|
-
}
|
|
279
282
|
return slots;
|
|
280
283
|
}
|
|
284
|
+
function pickShield(equippedSlots, equipment, registry, infusions) {
|
|
285
|
+
const slotted = equippedSlots.shield;
|
|
286
|
+
const slottedArmor = effectiveArmor(slotted == null ? void 0 : slotted.entity, registry);
|
|
287
|
+
if (slotted && slottedArmor && isShieldArmor(slottedArmor)) {
|
|
288
|
+
return { index: slotted.index, shield: slottedArmor, entity: slotted.entity, entry: slotted.entry };
|
|
289
|
+
}
|
|
290
|
+
for (let i = 0; i < equipment.length; i++) {
|
|
291
|
+
const entry = equipment[i];
|
|
292
|
+
if (!entry.equipped) continue;
|
|
293
|
+
const { entity } = resolveEntryEntity(entry, i, registry, infusions);
|
|
294
|
+
const armor = effectiveArmor(entity, registry);
|
|
295
|
+
if (armor && isShieldArmor(armor)) return { index: i, shield: armor, entity, entry };
|
|
296
|
+
}
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
function twoHandedShieldWarnings(equipment, registry, warnings, infusions) {
|
|
300
|
+
const equipped = equipment.flatMap((entry, i) => {
|
|
301
|
+
if (!entry.equipped) return [];
|
|
302
|
+
const { entity } = resolveEntryEntity(entry, i, registry, infusions);
|
|
303
|
+
return [{ entry, entity }];
|
|
304
|
+
});
|
|
305
|
+
const isShield = (e) => {
|
|
306
|
+
const armor = effectiveArmor(e.entity, registry);
|
|
307
|
+
return !!armor && isShieldArmor(armor);
|
|
308
|
+
};
|
|
309
|
+
for (const e of equipped) {
|
|
310
|
+
if (!e.entity || !isTwoHandedWeapon(e.entity, registry)) continue;
|
|
311
|
+
if (!equipped.some((other) => other !== e && isShield(other))) continue;
|
|
312
|
+
warnings.push(`${e.entity.name} is two-handed and a Shield is also equipped.`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
281
315
|
function computeAC(equippedSlots, resolved, mods, registry, warnings) {
|
|
282
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i
|
|
316
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
283
317
|
const breakdown = [];
|
|
284
318
|
const armorSlot = equippedSlots.armor;
|
|
285
319
|
const armorEntity = (_a = armorSlot == null ? void 0 : armorSlot.entity) != null ? _a : null;
|
|
@@ -289,13 +323,25 @@ function computeAC(equippedSlots, resolved, mods, registry, warnings) {
|
|
|
289
323
|
`Magic armor "${armorEntity.name}" base_item ${armorEntity.base_item} did not resolve to a base armor; AC not applied.`
|
|
290
324
|
);
|
|
291
325
|
}
|
|
326
|
+
const acCtx = buildConditionContext(resolved, equippedSlots, registry);
|
|
327
|
+
const skip = /* @__PURE__ */ new Set();
|
|
328
|
+
const slotBonus = (slot) => {
|
|
329
|
+
var _a2, _b2;
|
|
330
|
+
const view = slot ? (_a2 = resolved.infusions) == null ? void 0 : _a2.views.get(slot.index) : void 0;
|
|
331
|
+
if (!slot || !(view == null ? void 0 : view.entity) || !isItemEntity(view.entity) || !isAttunedActive(slot.entry, view.entity)) return 0;
|
|
332
|
+
const out = readNumericBonus((_b2 = view.entity.bonuses) == null ? void 0 : _b2.ac, acCtx);
|
|
333
|
+
if ((out == null ? void 0 : out.kind) !== "applied") return 0;
|
|
334
|
+
skip.add(slot.index);
|
|
335
|
+
return out.value;
|
|
336
|
+
};
|
|
337
|
+
const armorTerms = slotAcTerms(armor, armorSlot == null ? void 0 : armorSlot.entry);
|
|
292
338
|
let ac = 10;
|
|
293
|
-
if (armor) {
|
|
294
|
-
const armorBase = armor
|
|
339
|
+
if (armor && armorTerms.armor !== null) {
|
|
340
|
+
const armorBase = armorTerms.armor + slotBonus(armorSlot);
|
|
295
341
|
ac = armorBase;
|
|
296
|
-
breakdown.push({ source: (
|
|
342
|
+
breakdown.push({ source: (_b = armorEntity == null ? void 0 : armorEntity.name) != null ? _b : armor.name, amount: armorBase, kind: "armor" });
|
|
297
343
|
if (armor.ac.add_dex) {
|
|
298
|
-
const dexCap = (
|
|
344
|
+
const dexCap = (_c = armor.ac.dex_max) != null ? _c : Number.POSITIVE_INFINITY;
|
|
299
345
|
const dexAdd = Math.min(mods.dex, dexCap);
|
|
300
346
|
ac += dexAdd;
|
|
301
347
|
const capped = mods.dex > dexCap;
|
|
@@ -306,28 +352,34 @@ function computeAC(equippedSlots, resolved, mods, registry, warnings) {
|
|
|
306
352
|
breakdown.push({ source: "Base", amount: 10, kind: "unarmored" });
|
|
307
353
|
breakdown.push({ source: "DEX modifier", amount: mods.dex, kind: "dex" });
|
|
308
354
|
}
|
|
309
|
-
const acBonus =
|
|
310
|
-
if (
|
|
355
|
+
const acBonus = armorTerms.override;
|
|
356
|
+
if (acBonus !== 0) {
|
|
311
357
|
ac += acBonus;
|
|
312
|
-
breakdown.push({ source: `${(
|
|
358
|
+
breakdown.push({ source: `${(_d = armorEntity == null ? void 0 : armorEntity.name) != null ? _d : "Armor"} (override)`, amount: acBonus, kind: "override" });
|
|
313
359
|
}
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
const n = shield.ac.base + ((_j = shield.ac.flat) != null ? _j : 0);
|
|
360
|
+
const picked = pickShield(equippedSlots, (_e = resolved.definition.equipment) != null ? _e : [], registry, resolved.infusions);
|
|
361
|
+
if (picked) {
|
|
362
|
+
const { shield, entity: shieldEntity } = picked;
|
|
363
|
+
const shieldTerms = slotAcTerms(shield, picked.entry);
|
|
364
|
+
const n = shieldTerms.armor + slotBonus(picked);
|
|
320
365
|
ac += n;
|
|
321
|
-
breakdown.push({ source: (
|
|
366
|
+
breakdown.push({ source: (_f = shieldEntity == null ? void 0 : shieldEntity.name) != null ? _f : shield.name, amount: n, kind: "shield" });
|
|
367
|
+
const shieldAcBonus = shieldTerms.override;
|
|
368
|
+
if (shieldAcBonus !== 0) {
|
|
369
|
+
ac += shieldAcBonus;
|
|
370
|
+
breakdown.push({ source: `${(_g = shieldEntity == null ? void 0 : shieldEntity.name) != null ? _g : shield.name} (override)`, amount: shieldAcBonus, kind: "shield" });
|
|
371
|
+
}
|
|
322
372
|
}
|
|
323
373
|
const acInformational = [];
|
|
324
|
-
const
|
|
325
|
-
for (
|
|
326
|
-
|
|
374
|
+
const acEquipment = (_h = resolved.definition.equipment) != null ? _h : [];
|
|
375
|
+
for (let index = 0; index < acEquipment.length; index++) {
|
|
376
|
+
if (skip.has(index)) continue;
|
|
377
|
+
const entry = acEquipment[index];
|
|
378
|
+
const { entity, entityType } = resolveEntryEntity(entry, index, registry, resolved.infusions);
|
|
327
379
|
if (!entity || entityType !== "item") continue;
|
|
328
380
|
if (!isAttunedActive(entry, entity)) continue;
|
|
329
381
|
const item = entity;
|
|
330
|
-
const out = readNumericBonus((
|
|
382
|
+
const out = readNumericBonus((_i = item.bonuses) == null ? void 0 : _i.ac, acCtx);
|
|
331
383
|
if (!out) continue;
|
|
332
384
|
if (out.kind === "applied") {
|
|
333
385
|
ac += out.value;
|
|
@@ -365,6 +417,7 @@ function attackAbility(weapon, mods, weaponAbilities) {
|
|
|
365
417
|
function isWeaponSlugProficient(weapon, weaponSlug, profs) {
|
|
366
418
|
const wp = profs.weapons;
|
|
367
419
|
if (wp.specific.some((e) => e === weaponSlug || normKey(e) === normKey(weapon.name))) return true;
|
|
420
|
+
if (wp.specific.some((e) => qualifiedGrantCovers(e, weapon))) return true;
|
|
368
421
|
const cat = weapon.category;
|
|
369
422
|
if (typeof cat === "string") {
|
|
370
423
|
if (/^simple\b/.test(cat) && wp.categories.includes("simple")) return true;
|
|
@@ -373,9 +426,9 @@ function isWeaponSlugProficient(weapon, weaponSlug, profs) {
|
|
|
373
426
|
}
|
|
374
427
|
return false;
|
|
375
428
|
}
|
|
376
|
-
function magicBonusesForWeaponEntry(entry, registry, ctx) {
|
|
429
|
+
function magicBonusesForWeaponEntry(entry, registry, ctx, index, infusions) {
|
|
377
430
|
var _a;
|
|
378
|
-
const { entity } =
|
|
431
|
+
const { entity } = resolveEntryEntity(entry, index, registry, infusions);
|
|
379
432
|
const ovr = (_a = entry.overrides) != null ? _a : {};
|
|
380
433
|
const entryAttack = typeof ovr.bonus === "number" ? ovr.bonus : 0;
|
|
381
434
|
const entryDamage = typeof ovr.damage_bonus === "number" ? ovr.damage_bonus : 0;
|
|
@@ -440,8 +493,8 @@ function buildAttackRow(args) {
|
|
|
440
493
|
{ source: magicSource, amount: magic.dmg, kind: "item" }
|
|
441
494
|
];
|
|
442
495
|
const modes = classifyWeaponRange(weapon);
|
|
443
|
-
const fmtRanged = (r) => `${r.normal}/${r.long} ft
|
|
444
|
-
const range = modes.melee ? `${modes.melee.reach} ft
|
|
496
|
+
const fmtRanged = (r) => `${r.normal}/${r.long} ft.`;
|
|
497
|
+
const range = modes.melee ? `${modes.melee.reach} ft.` : modes.ranged ? fmtRanged(modes.ranged) : void 0;
|
|
445
498
|
const thrownRange = modes.melee && modes.ranged ? fmtRanged(modes.ranged) : void 0;
|
|
446
499
|
const stringProps = weapon.properties.filter(
|
|
447
500
|
(p) => typeof p === "string"
|
|
@@ -455,14 +508,17 @@ function buildAttackRow(args) {
|
|
|
455
508
|
toHit,
|
|
456
509
|
damageDice,
|
|
457
510
|
damageType: (_b = magic.damageTypeOverride) != null ? _b : weapon.damage.type,
|
|
458
|
-
// Riders: item damage_riders (Task 2) + the
|
|
511
|
+
// Riders: item damage_riders (Task 2) + the entry's overrides.extra_damage string. The override
|
|
512
|
+
// rider carries NO source: the player typed it on this very row, so the weapons table attributes it
|
|
513
|
+
// to nothing (no "(manual)" in a caption, no hover title on its chip).
|
|
459
514
|
...(() => {
|
|
460
515
|
const riders = [
|
|
461
516
|
...magic.riders,
|
|
462
|
-
...magic.extra ? [{ amount: magic.extra
|
|
517
|
+
...magic.extra ? [{ amount: magic.extra }] : []
|
|
463
518
|
];
|
|
464
519
|
return riders.length ? { damageRiders: riders } : {};
|
|
465
520
|
})(),
|
|
521
|
+
weaponCategory: modes.melee ? "melee" : "ranged",
|
|
466
522
|
properties: finalProps,
|
|
467
523
|
proficient,
|
|
468
524
|
breakdown: { toHit: toHitBreakdown, damage: damageBreakdown },
|
|
@@ -496,7 +552,7 @@ function buildUnarmedRow(mods, proficiencyBonus, spec) {
|
|
|
496
552
|
id: "unarmed-strike",
|
|
497
553
|
name: "Unarmed Strike",
|
|
498
554
|
unarmed: true,
|
|
499
|
-
range: "5 ft",
|
|
555
|
+
range: "5 ft.",
|
|
500
556
|
toHit,
|
|
501
557
|
damageDice,
|
|
502
558
|
damageType: "bludgeoning",
|
|
@@ -507,8 +563,10 @@ function buildUnarmedRow(mods, proficiencyBonus, spec) {
|
|
|
507
563
|
{ source: label, amount: abilityMod, kind: "ability" },
|
|
508
564
|
{ source: "Proficiency bonus", amount: proficiencyBonus, kind: "ability" }
|
|
509
565
|
],
|
|
566
|
+
// T17 T-33: the base term is the one the total sums. With no die the strike deals 1 + mod, so the base is +1
|
|
567
|
+
// (it read +0 beside a total of 1 + mod); a die carries its own base, as a weapon's does, so it stays +0.
|
|
510
568
|
damage: [
|
|
511
|
-
{ source: "Base damage", amount: 0, kind: "ability" },
|
|
569
|
+
{ source: "Base damage", amount: (spec == null ? void 0 : spec.dice) ? 0 : 1, kind: "ability" },
|
|
512
570
|
{ source: label, amount: abilityMod, kind: "ability" }
|
|
513
571
|
]
|
|
514
572
|
},
|
|
@@ -528,45 +586,22 @@ function formatWeaponSubLabel(weapon, properties, modes) {
|
|
|
528
586
|
if (properties.length > 0) parts.push(properties.map(humanizeWeaponToken).join(", "));
|
|
529
587
|
return parts.join(" \xB7 ");
|
|
530
588
|
}
|
|
531
|
-
function computeAttacks(equippedSlots, mods, profs, registry, warnings, proficiencyBonus, ctx, weaponMasteries, weaponAbilities = []) {
|
|
532
|
-
var _a, _b
|
|
589
|
+
function computeAttacks(equippedSlots, equipment, mods, profs, registry, warnings, proficiencyBonus, ctx, weaponMasteries, weaponAbilities = [], infusions) {
|
|
590
|
+
var _a, _b;
|
|
533
591
|
const rows = [];
|
|
534
|
-
const
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
];
|
|
538
|
-
for (const { key, placed } of handedSlots) {
|
|
539
|
-
if (!placed) continue;
|
|
540
|
-
const { entity, entry } = placed;
|
|
541
|
-
if (!entity) continue;
|
|
542
|
-
let weapon = null;
|
|
543
|
-
if (isWeaponEntity(entity)) {
|
|
544
|
-
weapon = entity;
|
|
545
|
-
} else if (isItemEntity(entity) && typeof entity.base_item === "string") {
|
|
546
|
-
const baseSlug = entity.base_item;
|
|
547
|
-
const found = resolveBaseItem(baseSlug, registry);
|
|
548
|
-
if (found && found.entityType === "weapon" && isWeaponEntity(found.data)) {
|
|
549
|
-
weapon = found.data;
|
|
550
|
-
} else {
|
|
551
|
-
warnings.push(`Magic weapon ${entity.name} references missing base_item ${baseSlug}.`);
|
|
552
|
-
continue;
|
|
553
|
-
}
|
|
554
|
-
} else {
|
|
555
|
-
continue;
|
|
556
|
-
}
|
|
557
|
-
const slugFromEntry = (_a = unwrapSlug(entry.item)) != null ? _a : weapon.slug;
|
|
592
|
+
const rowFor = (index, entry, weapon, slotKey, versatileDice) => {
|
|
593
|
+
var _a2, _b2, _c, _d, _e;
|
|
594
|
+
const slugFromEntry = (_a2 = unwrapSlug(entry.item)) != null ? _a2 : weapon.slug;
|
|
558
595
|
const proficient = isWeaponSlugProficient(weapon, slugFromEntry, profs) || isWeaponSlugProficient(weapon, weapon.slug, profs);
|
|
559
596
|
if (!proficient) {
|
|
560
597
|
warnings.push(`Character not proficient with ${weapon.name}; proficiency bonus excluded.`);
|
|
561
598
|
}
|
|
562
599
|
const ability = attackAbility(weapon, mods, weaponAbilities);
|
|
563
|
-
const magic = magicBonusesForWeaponEntry(entry, registry, ctx);
|
|
564
|
-
const ovr = (
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
id: `${placed.index}:standard`,
|
|
569
|
-
name: displayName,
|
|
600
|
+
const magic = magicBonusesForWeaponEntry(entry, registry, ctx, index, infusions);
|
|
601
|
+
const ovr = (_b2 = entry.overrides) != null ? _b2 : {};
|
|
602
|
+
return buildAttackRow({
|
|
603
|
+
id: `${index}:standard`,
|
|
604
|
+
name: (_d = (_c = ovr.name) != null ? _c : magic.itemName) != null ? _d : weapon.name,
|
|
570
605
|
weapon,
|
|
571
606
|
baseDice: weapon.damage.dice,
|
|
572
607
|
ability,
|
|
@@ -574,13 +609,38 @@ function computeAttacks(equippedSlots, mods, profs, registry, warnings, proficie
|
|
|
574
609
|
proficient,
|
|
575
610
|
proficiencyBonus,
|
|
576
611
|
magic,
|
|
577
|
-
slotKey
|
|
578
|
-
actionCost: (_e = ovr.action) != null ? _e :
|
|
612
|
+
slotKey,
|
|
613
|
+
actionCost: (_e = ovr.action) != null ? _e : WEAPON_DEFAULT_ACTION_COST,
|
|
579
614
|
versatileDice,
|
|
580
615
|
weaponMasteries
|
|
581
616
|
});
|
|
582
|
-
|
|
617
|
+
};
|
|
618
|
+
const handedSlots = [
|
|
619
|
+
{ key: "mainhand", placed: equippedSlots.mainhand },
|
|
620
|
+
{ key: "offhand", placed: equippedSlots.offhand }
|
|
621
|
+
];
|
|
622
|
+
for (const { key, placed } of handedSlots) {
|
|
623
|
+
if (!placed) continue;
|
|
624
|
+
const { entity, entry } = placed;
|
|
625
|
+
if (!entity) continue;
|
|
626
|
+
const weapon = effectiveWeapon(entity, registry);
|
|
627
|
+
if (!weapon) {
|
|
628
|
+
if (isItemEntity(entity) && typeof entity.base_item === "string") {
|
|
629
|
+
warnings.push(`Magic weapon ${entity.name} references missing base_item ${entity.base_item}.`);
|
|
630
|
+
}
|
|
631
|
+
continue;
|
|
632
|
+
}
|
|
633
|
+
const versatileDice = key === "mainhand" && hasProperty(weapon, "versatile") && typeof weapon.damage.versatile_dice === "string" && !equippedSlots.offhand ? weapon.damage.versatile_dice : void 0;
|
|
634
|
+
rows.push(rowFor(placed.index, entry, weapon, key, versatileDice));
|
|
583
635
|
}
|
|
636
|
+
const handIndex = /* @__PURE__ */ new Set([(_a = equippedSlots.mainhand) == null ? void 0 : _a.index, (_b = equippedSlots.offhand) == null ? void 0 : _b.index]);
|
|
637
|
+
equipment.forEach((entry, index) => {
|
|
638
|
+
if (!entry.equipped || handIndex.has(index)) return;
|
|
639
|
+
const { entity } = resolveEntryEntity(entry, index, registry, infusions);
|
|
640
|
+
const weapon = effectiveWeapon(entity, registry);
|
|
641
|
+
if (!weapon) return;
|
|
642
|
+
rows.push(rowFor(index, entry, weapon));
|
|
643
|
+
});
|
|
584
644
|
return rows;
|
|
585
645
|
}
|
|
586
646
|
function coerceWeight(raw) {
|
|
@@ -593,25 +653,38 @@ function coerceWeight(raw) {
|
|
|
593
653
|
}
|
|
594
654
|
function computeCarriedWeight(resolved, registry) {
|
|
595
655
|
var _a;
|
|
656
|
+
const equipment = (_a = resolved.definition.equipment) != null ? _a : [];
|
|
657
|
+
const view = resolveContainment(equipment, registry);
|
|
658
|
+
const weightlessContainer = equipment.map((entry) => {
|
|
659
|
+
var _a2, _b, _c;
|
|
660
|
+
const data = (_a2 = resolveBaseItem(entry.item, registry)) == null ? void 0 : _a2.data;
|
|
661
|
+
return ((_c = (_b = data == null ? void 0 : data.container) == null ? void 0 : _b.capacity) == null ? void 0 : _c.weightless) === true;
|
|
662
|
+
});
|
|
663
|
+
const exempt = (i) => {
|
|
664
|
+
for (let p = view.parentOf[i]; p !== null; p = view.parentOf[p]) if (weightlessContainer[p]) return true;
|
|
665
|
+
return false;
|
|
666
|
+
};
|
|
596
667
|
let total = 0;
|
|
597
|
-
|
|
668
|
+
equipment.forEach((entry, i) => {
|
|
669
|
+
if (exempt(i)) return;
|
|
598
670
|
const found = resolveBaseItem(entry.item, registry);
|
|
599
|
-
if (!found)
|
|
671
|
+
if (!found) return;
|
|
600
672
|
const entity = found.data;
|
|
601
673
|
const w = coerceWeight(entity.weight);
|
|
602
|
-
if (w === 0)
|
|
674
|
+
if (w === 0) return;
|
|
603
675
|
const qty = typeof entry.qty === "number" && entry.qty >= 0 ? entry.qty : 1;
|
|
604
676
|
total += w * qty;
|
|
605
|
-
}
|
|
677
|
+
});
|
|
606
678
|
return total;
|
|
607
679
|
}
|
|
608
680
|
function computeSlotsAndAttacks(resolved, mods, profs, registry, warnings, proficiencyBonus, weaponAbilities = [], weaponMasteries = [], attunementFloor = 0) {
|
|
609
|
-
var _a, _b, _c;
|
|
681
|
+
var _a, _b, _c, _d, _e;
|
|
610
682
|
const equippedSlots = assignSlots(resolved, registry, warnings);
|
|
611
|
-
|
|
683
|
+
twoHandedShieldWarnings((_a = resolved.definition.equipment) != null ? _a : [], registry, warnings, resolved.infusions);
|
|
684
|
+
const overrides = (_b = resolved.definition.overrides) != null ? _b : {};
|
|
612
685
|
const acOut = computeAC(equippedSlots, resolved, mods, registry, warnings);
|
|
613
|
-
const ctx = buildConditionContext(resolved, equippedSlots);
|
|
614
|
-
const attacks = computeAttacks(equippedSlots, mods, profs, registry, warnings, proficiencyBonus, ctx, weaponMasteries, weaponAbilities);
|
|
686
|
+
const ctx = buildConditionContext(resolved, equippedSlots, registry);
|
|
687
|
+
const attacks = computeAttacks(equippedSlots, (_c = resolved.definition.equipment) != null ? _c : [], mods, profs, registry, warnings, proficiencyBonus, ctx, weaponMasteries, weaponAbilities, resolved.infusions);
|
|
615
688
|
return {
|
|
616
689
|
ac: acOut.ac,
|
|
617
690
|
acBreakdown: acOut.breakdown,
|
|
@@ -620,10 +693,10 @@ function computeSlotsAndAttacks(resolved, mods, profs, registry, warnings, profi
|
|
|
620
693
|
equippedSlots,
|
|
621
694
|
carriedWeight: computeCarriedWeight(resolved, registry),
|
|
622
695
|
// Attunement is persistent: an attuned item still occupies a slot even when unequipped (SRD).
|
|
623
|
-
attunementUsed: ((
|
|
696
|
+
attunementUsed: ((_d = resolved.definition.equipment) != null ? _d : []).filter((e) => e.attuned).length,
|
|
624
697
|
// PRECEDENCE: an explicit character override wins outright (a number the player typed is intent);
|
|
625
698
|
// otherwise the baseline 3 is raised — never lowered — by whatever features granted.
|
|
626
|
-
attunementLimit: (
|
|
699
|
+
attunementLimit: (_e = overrides.attunement_limit) != null ? _e : Math.max(3, attunementFloor)
|
|
627
700
|
};
|
|
628
701
|
}
|
|
629
702
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// src/pc/spell-source.ts
|
|
2
|
+
var SPELL_SOURCE = {
|
|
3
|
+
class: { ownAbility: false, section: "spellbook", freeCastWhenNoSlot: false, showInPrepare: true, countsAsGranted: false },
|
|
4
|
+
feat: { ownAbility: true, section: "spellbook", freeCastWhenNoSlot: true, showInPrepare: true, countsAsGranted: true },
|
|
5
|
+
item: { ownAbility: false, section: "consumable", freeCastWhenNoSlot: false, showInPrepare: false, countsAsGranted: true },
|
|
6
|
+
race: { ownAbility: true, section: "spellbook", freeCastWhenNoSlot: true, showInPrepare: true, countsAsGranted: true },
|
|
7
|
+
domain: { ownAbility: false, section: "spellbook", freeCastWhenNoSlot: false, showInPrepare: true, countsAsGranted: false }
|
|
8
|
+
};
|
|
9
|
+
var spellSource = (s) => SPELL_SOURCE[s.source];
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
SPELL_SOURCE,
|
|
13
|
+
spellSource
|
|
14
|
+
};
|
|
@@ -59,6 +59,9 @@ function resolveFeatureResources(features) {
|
|
|
59
59
|
}
|
|
60
60
|
return out;
|
|
61
61
|
}
|
|
62
|
+
function pickResourceId(entry) {
|
|
63
|
+
return entry.param != null ? entry.key : entry.slug;
|
|
64
|
+
}
|
|
62
65
|
function resolveResourceIndex(resolved) {
|
|
63
66
|
var _a, _b, _c, _d, _e;
|
|
64
67
|
const out = new Map(resolveFeatureResources((_a = resolved.features) != null ? _a : []));
|
|
@@ -67,16 +70,18 @@ function resolveResourceIndex(resolved) {
|
|
|
67
70
|
const classSlug = (_c = rc == null ? void 0 : rc.entity) == null ? void 0 : _c.slug;
|
|
68
71
|
if (!classSlug) continue;
|
|
69
72
|
for (const entry of [...(_d = pool.selected) != null ? _d : [], ...(_e = pool.grants) != null ? _e : []]) {
|
|
73
|
+
if (entry.itemBound === true) continue;
|
|
70
74
|
const uses = entry.entity.uses;
|
|
71
|
-
|
|
75
|
+
const id = pickResourceId(entry);
|
|
76
|
+
if (!uses || out.has(id)) continue;
|
|
72
77
|
const maxFormula = String(uses.max);
|
|
73
78
|
if (!isValidMaxFormula(maxFormula)) {
|
|
74
79
|
warnOnce(`pool-uses:${entry.slug}`, `optional feature "${entry.slug}" has a prose uses.max (${JSON.stringify(uses.max)}); no tracker`);
|
|
75
80
|
continue;
|
|
76
81
|
}
|
|
77
82
|
const pres = entry.entity;
|
|
78
|
-
out.set(
|
|
79
|
-
id
|
|
83
|
+
out.set(id, {
|
|
84
|
+
id,
|
|
80
85
|
name: entry.entity.name,
|
|
81
86
|
reset: uses.recharge,
|
|
82
87
|
maxFormula,
|
|
@@ -101,6 +106,7 @@ export {
|
|
|
101
106
|
resourceLevelFor,
|
|
102
107
|
resolveRecovery,
|
|
103
108
|
resolveFeatureResources,
|
|
109
|
+
pickResourceId,
|
|
104
110
|
resolveResourceIndex,
|
|
105
111
|
poolSaveDC
|
|
106
112
|
};
|