diff --git a/input-expressions b/input-expressions index 9251ca7..36db25b 160000 --- a/input-expressions +++ b/input-expressions @@ -1 +1 @@ -Subproject commit 9251ca72cad4776f19d91aae3e5b525e415bf90e +Subproject commit 36db25b5da1b0fa26b21808f3b2fcf49f1b09848 diff --git a/module.json b/module.json index bef2110..f330455 100644 --- a/module.json +++ b/module.json @@ -2,7 +2,6 @@ "id": "monsterblock", "title": "Monster Blocks", "description": "An NPC sheet designed to emulate standard 5e monster stat blocks as faithfully as possible.", - "author": "zeel", "authors": [ { "name": "zeel", @@ -11,7 +10,7 @@ "discord": "zeel#4200" } ], - "version": "3.5.1", + "version": "3.6.0", "compatibility": { "minimum": "10", "verified": "12" @@ -21,7 +20,7 @@ { "id": "dnd5e", "compatibility": { - "verified": "4.1.2" + "verified": "4.3.6" } } ], @@ -66,7 +65,6 @@ "download": "https://github.com/zeel01/MonsterBlocks/releases/latest/download/monsterblock.zip", "readme": "https://github.com/zeel01/MonsterBlocks/blob/master/readme.md", "bugs": "https://github.com/zeel01/MonsterBlocks/issues", - "allowBugReporter": true, "changelog": "https://github.com/zeel01/MonsterBlocks/releases", "media": [ { diff --git a/scripts/dnd5e/CastingPreper.js b/scripts/dnd5e/CastingPreper.js index fa39ffe..a875734 100644 --- a/scripts/dnd5e/CastingPreper.js +++ b/scripts/dnd5e/CastingPreper.js @@ -43,6 +43,8 @@ export default class CastingPreper extends ItemPreper { */ static isInnateSpellcasting(item) { const name = item.name.toLowerCase().replace(/\s+/g, "") ?? ""; + if (getTranslationArray("MOBLOKS5E.SpellcastingLocators").some(loc => name.includes(loc)) && item.system.activities?.some(a => a.type === "cast")) return true; + return getTranslationArray("MOBLOKS5E.InnateCastingLocators").some(loc => name.includes(loc)); } /** @@ -332,7 +334,7 @@ export default class CastingPreper extends ItemPreper { * @memberof CastingPreper */ get casterLevel() { - return this.sheet.actor.system.details?.spellLevel ?? 0; + return this.sheet.actor.system.attributes?.spell?.level ?? this.sheet.actor.system.details?.spellLevel ?? 0; } /** @@ -452,7 +454,7 @@ export default class CastingPreper extends ItemPreper { */ get casterStatsText() { return game.i18n.format("MOBLOKS5E.CastingStats", { - savedc: this.sheet.actor.system?.attributes?.spelldc, + savedc: this.sheet.actor.system?.attributes?.spell?.dc ?? this.sheet.actor.system?.attributes?.spelldc, bonus: `${this.tohit > -1 ? "+" : ""}${this.tohit}` }) } @@ -557,10 +559,10 @@ export default class CastingPreper extends ItemPreper { if (spelllevel !== undefined) { let spell = spelllevel.spells.find((s) => - s.system.ability && - s.system.ability != main + s.system.abilityMod && + s.system.abilityMod != main ); - castingability = spell?.data?.ability ?? main; + castingability = spell?.system?.ability ?? main; } return [ diff --git a/scripts/dnd5e/InnateSpellbookPrep.js b/scripts/dnd5e/InnateSpellbookPrep.js index 75d0940..afdcb85 100644 --- a/scripts/dnd5e/InnateSpellbookPrep.js +++ b/scripts/dnd5e/InnateSpellbookPrep.js @@ -20,9 +20,9 @@ export default class InnateSpellbookPrep { * @return {object} The completed innate spellbook * @memberof InnateSpellbookPrep */ - prepare() { // We need to completely re-organize the spellbook for an innate spellcaster - for (let level of this.spellbook) { // Spellbook is seperated into sections based on level, though all the innate spells are lumped together, we still want to check all the sections. - if (level.prop !== "innate") continue; // We don't care about sections that aren't marked as innate though + prepare() { // We need to completely re-organize the spellbook for an innate spellcaster + for (let level of this.spellbook) { // Spellbook is seperated into sections based on level, though all the innate spells are lumped together, we still want to check all the sections. + if (!["innate", "atwill"].includes(level.prop) && !level.spells.some(s => s.getFlag("dnd5e", "cachedFor"))) continue; // We don't care about sections that aren't marked as innate though this.prepareSpellLevel(level); } @@ -69,7 +69,11 @@ export default class InnateSpellbookPrep { */ sortSpell(spell) { // Max uses is what we are going to end up sorting the spellbook by. - this.getPage(spell.system.uses.max).spells.push(spell); + let uses = spell.system.uses.max; + if (spell.getFlag("dnd5e", "cachedFor")) { + uses = fromUuidSync(spell.getFlag("dnd5e", "cachedFor"), {relative: this.sheet.object}).uses.max; + } + this.getPage(uses).spells.push(spell); } /** diff --git a/scripts/dnd5e/MonsterBlock5e.js b/scripts/dnd5e/MonsterBlock5e.js index e99d7bd..8805a8a 100644 --- a/scripts/dnd5e/MonsterBlock5e.js +++ b/scripts/dnd5e/MonsterBlock5e.js @@ -94,10 +94,10 @@ export default class MonsterBlock5e extends dnd5e.applications.actor.ActorSheet5 for (let fk of Object.keys(data.features)) { for (let item of data.features[fk].items) { - const value = await TextEditor.enrichHTML(item.system.description.value, { secrets: (data.owner && !data.flags["hidden-secrets"])}); + const value = await TextEditor.enrichHTML(item.system.description.value, { relativeTo: item, secrets: (data.owner && !data.flags["hidden-secrets"])}); item.enrichedValue = value; - item.rechargeValue = isDndV4OrNewer() ? item.system.uses.recovery.find(r => r.period === "recharge")?.formula : item.system.recharge?.value; - item.activationCost = isDndV4OrNewer() ? item.system.activities.find(a => a.activation.value)?.activation.value : item.system.activation?.cost; + item.rechargeValue = isDndV4OrNewer() ? item.system.uses?.recovery.find(r => r.period === "recharge")?.formula : item.system.recharge?.value; + item.activationCost = isDndV4OrNewer() ? item.system.activities?.find(a => a.activation.value)?.activation.value : item.system.activation?.cost; } } @@ -130,6 +130,7 @@ export default class MonsterBlock5e extends dnd5e.applications.actor.ActorSheet5 data.themes = this.themes; data.sourceStringId = game.i18n.has("DND5E.Source") ? "DND5E.Source" : "DND5E.SOURCE.FIELDS.source.label"; + data.legActTitleId = game.i18n.has("DND5E.LegAct") ? "DND5E.LegAct" : "DND5E.NPC.SECTIONS.LegendaryActions"; this.templateData = data; return data; @@ -604,7 +605,10 @@ export default class MonsterBlock5e extends dnd5e.applications.actor.ActorSheet5 } prepAbilities(data) { Object.entries(data.abilities)?.forEach( - ([id, ability]) => ability.abbr = game.i18n.localize("MOBLOKS5E.Abbr" + id) + ([id, ability]) => { + ability.abbr = game.i18n.localize("MOBLOKS5E.Abbr" + id); + ability.saveValue = ability.save.value ?? ability.save; + } ) } /** diff --git a/templates/dnd5e/parts/header/attributes/damage.hbs b/templates/dnd5e/parts/header/attributes/damage.hbs index 5c7ed86..cfbfcb8 100644 --- a/templates/dnd5e/parts/header/attributes/damage.hbs +++ b/templates/dnd5e/parts/header/attributes/damage.hbs @@ -13,6 +13,12 @@ {{trait.physical}} {{~/if~}} + {{~#if trait.custom}} + + {{localize "MOBLOKS5E.SemiColon"}} + {{trait.custom}} + + {{~/if~}} {{/if}} diff --git a/templates/dnd5e/parts/header/attributes/saves.hbs b/templates/dnd5e/parts/header/attributes/saves.hbs index 664ed69..36022fa 100644 --- a/templates/dnd5e/parts/header/attributes/saves.hbs +++ b/templates/dnd5e/parts/header/attributes/saves.hbs @@ -6,7 +6,7 @@