diff --git a/daggerheart.mjs b/daggerheart.mjs index 22586e90..9239d338 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -66,7 +66,7 @@ Hooks.once('init', () => { CONFIG.Token.documentClass = documents.DhToken; CONFIG.Token.prototypeSheetClass = applications.sheetConfigs.DhPrototypeTokenConfig; DocumentSheetConfig.unregisterSheet(TokenDocument, 'core', foundry.applications.sheets.TokenConfig); - DocumentSheetConfig.registerSheet(TokenDocument, 'dnd5e', applications.sheetConfigs.DhTokenConfig, { + DocumentSheetConfig.registerSheet(TokenDocument, SYSTEM.id, applications.sheetConfigs.DhTokenConfig, { makeDefault: true }); diff --git a/lang/en.json b/lang/en.json index 9f929c77..7e25f0d2 100755 --- a/lang/en.json +++ b/lang/en.json @@ -108,15 +108,6 @@ "Character": { "age": "Age", "companionFeatures": "Companion Features", - "contextMenu": { - "consume": "Consume Item", - "equip": "Equip", - "sendToChat": "Send To Chat", - "toLoadout": "Send to Loadout", - "toVault": "Send to Vault", - "unequip": "Unequip", - "useItem": "Use Item" - }, "faith": "Faith", "levelUp": "You can level up", "pronouns": "Pronouns", @@ -206,6 +197,16 @@ "requestingSpotlight": "Requesting The Spotlight", "requestSpotlight": "Request The Spotlight" }, + "ContextMenu": { + "disableEffect": "Disable Effect", + "enableEffect": "Enable Effect", + "equip": "Equip", + "sendToChat": "Send To Chat", + "toLoadout": "Send to Loadout", + "toVault": "Send to Vault", + "unequip": "Unequip", + "useItem": "Use Item" + }, "Countdown": { "addCountdown": "Add Countdown", "FIELDS": { @@ -1581,6 +1582,8 @@ "featureNotMastery": "This feature is used as something else than a Mastery feature and cannot be used here." }, "Tooltip": { + "disableEffect": "Disable Effect", + "enableEffect": "Enable Effect", "openItemWorld": "Open Item World", "openActorWorld": "Open Actor World", "sendToChat": "Send to Chat", diff --git a/module/applications/sheets-configs/action-config.mjs b/module/applications/sheets-configs/action-config.mjs index e1a62e0d..db3c4d27 100644 --- a/module/applications/sheets-configs/action-config.mjs +++ b/module/applications/sheets-configs/action-config.mjs @@ -109,7 +109,7 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) { context.getEffectDetails = this.getEffectDetails.bind(this); context.costOptions = this.getCostOptions(); context.disableOption = this.disableOption.bind(this); - context.isNPC = this.action.actor && this.action.actor.type !== 'character'; + context.isNPC = this.action.actor?.isNPC; context.hasRoll = this.action.hasRoll; const settingsTiers = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers).tiers; diff --git a/module/applications/sheets-configs/environment-settings.mjs b/module/applications/sheets-configs/environment-settings.mjs index 7422f5fc..c249d6d5 100644 --- a/module/applications/sheets-configs/environment-settings.mjs +++ b/module/applications/sheets-configs/environment-settings.mjs @@ -1,3 +1,4 @@ +import { getDocFromElement } from '../../helpers/utils.mjs'; import DHBaseActorSettings from '../sheets/api/actor-setting.mjs'; /**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */ @@ -9,8 +10,7 @@ export default class DHEnvironmentSettings extends DHBaseActorSettings { actions: { addCategory: DHEnvironmentSettings.#addCategory, removeCategory: DHEnvironmentSettings.#removeCategory, - viewAdversary: this.#viewAdversary, - deleteAdversary: this.#deleteAdversary + deleteAdversary: DHEnvironmentSettings.#deleteAdversary }, dragDrop: [ { dragSelector: null, dropSelector: '.category-container' }, @@ -69,37 +69,30 @@ export default class DHEnvironmentSettings extends DHBaseActorSettings { await this.actor.update({ [`system.potentialAdversaries.-=${target.dataset.categoryId}`]: null }); } - static async #viewAdversary(_, button) { - const adversary = await foundry.utils.fromUuid(button.dataset.adversary); - if (!adversary) { - ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.adversaryMissing')); - return; - } + /** + * + * @type {ApplicationClickAction} + * @returns + */ + static async #deleteAdversary(_event, target) { + const doc = getDocFromElement(target); + const { category } = target.dataset; + const path = `system.potentialAdversaries.${category}.adversaries`; - adversary.sheet.render({ force: true }); - } + const confirmed = await foundry.applications.api.DialogV2.confirm({ + window: { + title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', { + type: game.i18n.localize('TYPES.Actor.adversary'), + name: doc.name + }) + }, + content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { name: doc.name }) + }); - static async #deleteAdversary(event, target) { - const adversaryKey = target.dataset.adversary; - const path = `system.potentialAdversaries.${target.dataset.potentialAdversary}.adversaries`; - const property = foundry.utils.getProperty(this.actor, path); - const adversary = property.find(x => (x?.uuid ?? x) === adversaryKey); + if (!confirmed) return; - if (adversary) { - const confirmed = await foundry.applications.api.DialogV2.confirm({ - window: { - title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', { - type: game.i18n.localize('TYPES.Actor.adversary'), - name: adversary.name - }) - }, - content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { name: adversary.name }) - }); - - if (!confirmed) return; - } - - const newAdversaries = property.filter(x => x && (x?.uuid ?? x) !== adversaryKey); + const adversaries = foundry.utils.getProperty(this.actor, path); + const newAdversaries = adversaries.filter(a => a.uuid !== doc.uuid); await this.actor.update({ [path]: newAdversaries }); } diff --git a/module/applications/sheets/actors/adversary.mjs b/module/applications/sheets/actors/adversary.mjs index af89abe6..e6508695 100644 --- a/module/applications/sheets/actors/adversary.mjs +++ b/module/applications/sheets/actors/adversary.mjs @@ -9,9 +9,6 @@ export default class AdversarySheet extends DHBaseActorSheet { window: { resizable: true }, actions: { reactionRoll: AdversarySheet.#reactionRoll, - useItem: this.useItem, - useAction: this.useItem, - toChat: this.toChat }, window: { resizable: true @@ -29,7 +26,7 @@ export default class AdversarySheet extends DHBaseActorSheet { /** @inheritdoc */ static TABS = { primary: { - tabs: [{ id: 'features' }, { id: 'notes' }, { id: 'effects' }], + tabs: [{ id: 'features' }, { id: 'effects' }, { id: 'notes' }], initial: 'features', labelPrefix: 'DAGGERHEART.GENERAL.Tabs' } @@ -42,10 +39,63 @@ export default class AdversarySheet extends DHBaseActorSheet { return context; } - getItem(element) { - const itemId = (element.target ?? element).closest('[data-item-id]').dataset.itemId, - item = this.document.items.get(itemId); - return item; + /**@inheritdoc */ + async _preparePartContext(partId, context, options) { + context = await super._preparePartContext(partId, context, options); + switch (partId) { + case 'header': + await this._prepareHeaderContext(context, options); + break; + case 'notes': + await this._prepareNotesContext(context, options); + break; + } + return context; + } + + /** + * Prepare render context for the Biography part. + * @param {ApplicationRenderContext} context + * @param {ApplicationRenderOptions} options + * @returns {Promise} + * @protected + */ + async _prepareNotesContext(context, _options) { + const { system } = this.document; + const { TextEditor } = foundry.applications.ux; + + const paths = { + notes: 'notes' + }; + + for (const [key, path] of Object.entries(paths)) { + const value = foundry.utils.getProperty(system, path); + context[key] = { + field: system.schema.getField(path), + value, + enriched: await TextEditor.implementation.enrichHTML(value, { + secrets: this.document.isOwner, + relativeTo: this.document + }) + }; + } + } + + /** + * Prepare render context for the Header part. + * @param {ApplicationRenderContext} context + * @param {ApplicationRenderOptions} options + * @returns {Promise} + * @protected + */ + async _prepareHeaderContext(context, _options) { + const { system } = this.document; + const { TextEditor } = foundry.applications.ux; + + context.description = await TextEditor.implementation.enrichHTML(system.description, { + secrets: this.document.isOwner, + relativeTo: this.document + }); } /* -------------------------------------------- */ @@ -73,42 +123,4 @@ export default class AdversarySheet extends DHBaseActorSheet { this.actor.diceRoll(config); } - - /** - * - * @type {ApplicationClickAction} - */ - static async useItem(event) { - const action = this.getItem(event) ?? this.actor.system.attack; - action.use(event); - } - - /** - * - * @type {ApplicationClickAction} - */ - static async toChat(event, button) { - if (button?.dataset?.type === 'experience') { - const experience = this.document.system.experiences[button.dataset.uuid]; - const cls = getDocumentClass('ChatMessage'); - const systemData = { - name: game.i18n.localize('DAGGERHEART.GENERAL.Experience.single'), - description: `${experience.name} ${experience.value.signedString()}` - }; - const msg = new cls({ - type: 'abilityUse', - user: game.user.id, - system: systemData, - content: await foundry.applications.handlebars.renderTemplate( - 'systems/daggerheart/templates/ui/chat/ability-use.hbs', - systemData - ) - }); - - cls.create(msg.toObject()); - } else { - const item = this.getItem(event) ?? this.document.system.attack; - item.toChat(this.document.id); - } - } } diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index 0f6f8284..8f157c96 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -4,7 +4,7 @@ import { abilities } from '../../../config/actorConfig.mjs'; import DhCharacterlevelUp from '../../levelup/characterLevelup.mjs'; import DhCharacterCreation from '../../characterCreation/characterCreation.mjs'; import FilterMenu from '../../ux/filter-menu.mjs'; -import { itemAbleRollParse } from '../../../helpers/utils.mjs'; +import { getDocFromElement, itemAbleRollParse } from '../../../helpers/utils.mjs'; /**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */ @@ -15,7 +15,6 @@ export default class CharacterSheet extends DHBaseActorSheet { classes: ['character'], position: { width: 850, height: 800 }, actions: { - triggerContextMenu: CharacterSheet.#triggerContextMenu, toggleVault: CharacterSheet.#toggleVault, rollAttribute: CharacterSheet.#rollAttribute, toggleHope: CharacterSheet.#toggleHope, @@ -24,11 +23,8 @@ export default class CharacterSheet extends DHBaseActorSheet { makeDeathMove: CharacterSheet.#makeDeathMove, levelManagement: CharacterSheet.#levelManagement, toggleEquipItem: CharacterSheet.#toggleEquipItem, - useItem: this.useItem, //TODO Fix this - useAction: this.useAction, - toggleResourceDice: this.toggleResourceDice, - handleResourceDice: this.handleResourceDice, - toChat: this.toChat, + toggleResourceDice: CharacterSheet.#toggleResourceDice, + handleResourceDice: CharacterSheet.#handleResourceDice, useDowntime: this.useDowntime }, window: { @@ -42,8 +38,24 @@ export default class CharacterSheet extends DHBaseActorSheet { ], contextMenus: [ { - handler: CharacterSheet._getContextMenuOptions, - selector: '[data-item-id]', + handler: CharacterSheet.#getDomainCardContextOptions, + selector: '[data-item-uuid][data-type="domainCard"]', + options: { + parentClassHooks: false, + fixed: true + } + }, + { + handler: CharacterSheet.#getEquipamentContextOptions, + selector: '[data-item-uuid][data-type="armor"], [data-item-uuid][data-type="weapon"]', + options: { + parentClassHooks: false, + fixed: true + } + }, + { + handler: CharacterSheet.#getItemContextOptions, + selector: '[data-item-uuid][data-type="consumable"], [data-item-uuid][data-type="miscellaneous"]', options: { parentClassHooks: false, fixed: true @@ -123,20 +135,6 @@ export default class CharacterSheet extends DHBaseActorSheet { this._createSearchFilter(); } - /* -------------------------------------------- */ - - getItem(element) { - const listElement = (element.target ?? element).closest('[data-item-id]'); - const itemId = listElement.dataset.itemId; - - switch (listElement.dataset.type) { - case 'effect': - return this.document.effects.get(itemId); - default: - return this.document.items.get(itemId); - } - } - /* -------------------------------------------- */ /* Prepare Context */ /* -------------------------------------------- */ @@ -186,124 +184,135 @@ export default class CharacterSheet extends DHBaseActorSheet { case 'sidebar': await this._prepareSidebarContext(context, options); break; + case 'biography': + await this._prepareBiographyContext(context, options); + break; } return context; } + /** + * Prepare render context for the Loadout part. + * @param {ApplicationRenderContext} context + * @param {ApplicationRenderOptions} options + * @returns {Promise} + * @protected + */ async _prepareLoadoutContext(context, _options) { - context.listView = game.user.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.displayDomainCardsAsList); + context.cardView = !game.user.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.displayDomainCardsAsList); } + /** + * Prepare render context for the Sidebar part. + * @param {ApplicationRenderContext} context + * @param {ApplicationRenderOptions} options + * @returns {Promise} + * @protected + */ async _prepareSidebarContext(context, _options) { context.isDeath = this.document.system.deathMoveViable; } + /** + * Prepare render context for the Biography part. + * @param {ApplicationRenderContext} context + * @param {ApplicationRenderOptions} options + * @returns {Promise} + * @protected + */ + async _prepareBiographyContext(context, _options) { + const { system } = this.document; + const { TextEditor } = foundry.applications.ux; + + const paths = { + background: 'biography.background', + connections: 'biography.connections' + }; + + for (const [key, path] of Object.entries(paths)) { + const value = foundry.utils.getProperty(system, path); + context[key] = { + field: system.schema.getField(path), + value, + enriched: await TextEditor.implementation.enrichHTML(value, { + secrets: this.document.isOwner, + relativeTo: this.document + }) + }; + } + } + /* -------------------------------------------- */ /* Context Menu */ /* -------------------------------------------- */ /** - * Get the set of ContextMenu options. + * Get the set of ContextMenu options for DomainCards. * @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance * @this {CharacterSheet} * @protected */ - static _getContextMenuOptions() { - /** - * Get the item from the element. - * @param {HTMLElement} el - * @returns {foundry.documents.Item?} - */ - const getItem = element => { - const listElement = (element.target ?? element).closest('[data-item-id]'); - const itemId = listElement.dataset.itemId; - - switch (listElement.dataset.type) { - case 'effect': - return this.document.effects.get(itemId); - default: - return this.document.items.get(itemId); + static #getDomainCardContextOptions() { + /**@type {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} */ + const options = [ + { + name: 'toLoadout', + icon: 'fa-solid fa-arrow-up', + condition: target => getDocFromElement(target).system.inVault, + callback: target => getDocFromElement(target).update({ 'system.inVault': false }) + }, + { + name: 'toVault', + icon: 'fa-solid fa-arrow-down', + condition: target => !getDocFromElement(target).system.inVault, + callback: target => getDocFromElement(target).update({ 'system.inVault': true }) } - }; + ].map(option => ({ + ...option, + name: `DAGGERHEART.APPLICATIONS.ContextMenu.${option.name}`, + icon: `` + })); - return [ - { - name: 'DAGGERHEART.ACTORS.Character.contextMenu.useItem', - icon: '', - condition: el => { - const item = getItem(el); - return !['class', 'subclass'].includes(item.type); - }, - callback: (button, event) => CharacterSheet.useItem.call(this, event, button) - }, - { - name: 'DAGGERHEART.ACTORS.Character.contextMenu.equip', - icon: '', - condition: el => { - const item = getItem(el); - return ['weapon', 'armor'].includes(item.type) && !item.system.equipped; - }, - callback: CharacterSheet.#toggleEquipItem.bind(this) - }, - { - name: 'DAGGERHEART.ACTORS.Character.contextMenu.unequip', - icon: '', - condition: el => { - const item = getItem(el); - return ['weapon', 'armor'].includes(item.type) && item.system.equipped; - }, - callback: CharacterSheet.#toggleEquipItem.bind(this) - }, - { - name: 'DAGGERHEART.ACTORS.Character.contextMenu.toLoadout', - icon: '', - condition: el => { - const item = getItem(el); - return ['domainCard'].includes(item.type) && item.system.inVault; - }, - callback: target => getItem(target).update({ 'system.inVault': false }) - }, - { - name: 'DAGGERHEART.ACTORS.Character.contextMenu.toVault', - icon: '', - condition: el => { - const item = getItem(el); - return ['domainCard'].includes(item.type) && !item.system.inVault; - }, - callback: target => getItem(target).update({ 'system.inVault': true }) - }, - { - name: 'DAGGERHEART.ACTORS.Character.contextMenu.sendToChat', - icon: '', - callback: CharacterSheet.toChat.bind(this) - }, - { - name: 'CONTROLS.CommonEdit', - icon: '', - callback: target => getItem(target).sheet.render({ force: true }) - }, - { - name: 'CONTROLS.CommonDelete', - icon: '', - callback: async el => { - const item = getItem(el); - const confirmed = await foundry.applications.api.DialogV2.confirm({ - window: { - title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', { - type: game.i18n.localize(`TYPES.${item.documentName}.${item.type}`), - name: item.name - }) - }, - content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { - name: item.name - }) - }); - if (!confirmed) return; + return [...options, ...this._getContextMenuCommonOptions.call(this, { usable: true, toChat: true })]; + } - item.delete(); - } + /** + * Get the set of ContextMenu options for Armors and Weapons. + * @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance + * @this {CharacterSheet} + * @protected + */ + static #getEquipamentContextOptions() { + const options = [ + { + name: 'equip', + icon: 'fa-solid fa-hands', + condition: target => !getDocFromElement(target).system.equipped, + callback: (target, event) => CharacterSheet.#toggleEquipItem.call(this, event, target) + }, + { + name: 'unequip', + icon: 'fa-solid fa-hands', + condition: target => getDocFromElement(target).system.equipped, + callback: (target, event) => CharacterSheet.#toggleEquipItem.call(this, event, target) } - ]; + ].map(option => ({ + ...option, + name: `DAGGERHEART.APPLICATIONS.ContextMenu.${option.name}`, + icon: `` + })); + + return [...options, ...this._getContextMenuCommonOptions.call(this, { usable: true, toChat: true })]; + } + + /** + * Get the set of ContextMenu options for Consumable and Miscellaneous. + * @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance + * @this {CharacterSheet} + * @protected + */ + static #getItemContextOptions() { + return this._getContextMenuCommonOptions.call(this, { usable: true, toChat: true }); } /* -------------------------------------------- */ /* Filter Tracking */ @@ -397,7 +406,7 @@ export default class CharacterSheet extends DHBaseActorSheet { this.#filteredItems.inventory.search.clear(); for (const li of html.querySelectorAll('.inventory-item')) { - const item = this.document.items.get(li.dataset.itemId); + const item = getDocFromElement(li); const matchesSearch = !query || foundry.applications.ux.SearchFilter.testQuery(rgx, item.name); if (matchesSearch) this.#filteredItems.inventory.search.add(item.id); const { menu } = this.#filteredItems.inventory; @@ -417,7 +426,7 @@ export default class CharacterSheet extends DHBaseActorSheet { this.#filteredItems.loadout.search.clear(); for (const li of html.querySelectorAll('.items-list .inventory-item, .card-list .card-item')) { - const item = this.document.items.get(li.dataset.itemId); + const item = getDocFromElement(li); const matchesSearch = !query || foundry.applications.ux.SearchFilter.testQuery(rgx, item.name); if (matchesSearch) this.#filteredItems.loadout.search.add(item.id); const { menu } = this.#filteredItems.loadout; @@ -468,7 +477,7 @@ export default class CharacterSheet extends DHBaseActorSheet { this.#filteredItems.inventory.menu.clear(); for (const li of html.querySelectorAll('.inventory-item')) { - const item = this.document.items.get(li.dataset.itemId); + const item = getDocFromElement(li); const matchesMenu = filters.length === 0 || filters.some(f => foundry.applications.ux.SearchFilter.evaluateFilter(item, f)); @@ -489,7 +498,7 @@ export default class CharacterSheet extends DHBaseActorSheet { this.#filteredItems.loadout.menu.clear(); for (const li of html.querySelectorAll('.items-list .inventory-item, .card-list .card-item')) { - const item = this.document.items.get(li.dataset.itemId); + const item = getDocFromElement(li); const matchesMenu = filters.length === 0 || filters.some(f => foundry.applications.ux.SearchFilter.evaluateFilter(item, f)); @@ -503,22 +512,21 @@ export default class CharacterSheet extends DHBaseActorSheet { /* -------------------------------------------- */ /* Application Listener Actions */ /* -------------------------------------------- */ + async updateItemResource(event) { - const item = this.getItem(event.currentTarget); + const item = getDocFromElement(event.currentTarget); if (!item) return; const max = item.system.resource.max ? itemAbleRollParse(item.system.resource.max, this.document, item) : null; const value = max ? Math.min(Number(event.currentTarget.value), max) : event.currentTarget.value; await item.update({ 'system.resource.value': value }); - this.render(); } async updateItemQuantity(event) { - const item = this.getItem(event.currentTarget); + const item = getDocFromElement(event.currentTarget); if (!item) return; await item.update({ 'system.quantity': event.currentTarget.value }); - this.render(); } async updateArmorMarks(event) { @@ -528,7 +536,6 @@ export default class CharacterSheet extends DHBaseActorSheet { const maxMarks = this.document.system.armorScore; const value = Math.min(Math.max(Number(event.currentTarget.value), 0), maxMarks); await armor.update({ 'system.marks.value': value }); - this.render(); } /* -------------------------------------------- */ @@ -588,13 +595,14 @@ export default class CharacterSheet extends DHBaseActorSheet { this.document.diceRoll(config); } + //TODO: redo toggleEquipItem method + /** * Toggles the equipped state of an item (armor or weapon). * @type {ApplicationClickAction} */ static async #toggleEquipItem(_event, button) { - //TODO: redo this - const item = this.actor.items.get(button.closest('[data-item-id]')?.dataset.itemId); + const item = getDocFromElement(button); if (!item) return; if (item.system.equipped) { await item.update({ 'system.equipped': false }); @@ -642,64 +650,23 @@ export default class CharacterSheet extends DHBaseActorSheet { * Toggles whether an item is stored in the vault. * @type {ApplicationClickAction} */ - static async #toggleVault(event, button) { - const docId = button.closest('[data-item-id]')?.dataset.itemId; - const doc = this.document.items.get(docId); + static async #toggleVault(_event, button) { + const doc = getDocFromElement(button); await doc?.update({ 'system.inVault': !doc.system.inVault }); } - /** - * Trigger the context menu. - * @type {ApplicationClickAction} - */ - static #triggerContextMenu(event, _) { - return CONFIG.ux.ContextMenu.triggerContextMenu(event); - } - - /** - * Use a item - * @type {ApplicationClickAction} - */ - static async useItem(event, button) { - const item = this.getItem(button); - if (!item) return; - - // Should dandle its actions. Or maybe they'll be separate buttons as per an Issue on the board - if (item.type === 'feature') { - item.use(event); - } else if (item instanceof ActiveEffect) { - item.toChat(this); - } else { - item.use(event); - } - } - - /** - * Use an action - * @type {ApplicationClickAction} - */ - static async useAction(event, button) { - const item = this.getItem(button); - if (!item) return; - - const action = item.system.actions.find(x => x.id === button.dataset.actionId); - if (!action) return; - - action.use(event); - } - /** * Toggle the used state of a resource dice. * @type {ApplicationClickAction} */ - static async toggleResourceDice(event) { - const target = event.target.closest('.item-resource'); - const item = this.getItem(event); - if (!item) return; + static async #toggleResourceDice(event, target) { + const item = getDocFromElement(target); + + const { dice } = event.target.closest('.item-resource').dataset; + const diceState = item.system.resource.diceStates[dice]; - const diceState = item.system.resource.diceStates[target.dataset.dice]; await item.update({ - [`system.resource.diceStates.${target.dataset.dice}.used`]: diceState?.used ? !diceState.used : true + [`system.resource.diceStates.${dice}.used`]: diceState ? !diceState.used : true }); } @@ -707,8 +674,8 @@ export default class CharacterSheet extends DHBaseActorSheet { * Handle the roll values of resource dice. * @type {ApplicationClickAction} */ - static async handleResourceDice(event) { - const item = this.getItem(event); + static async #handleResourceDice(_, target) { + const item = getDocFromElement(target); if (!item) return; const rollValues = await game.system.api.applications.dialogs.ResourceDiceDialog.create(item, this.document); @@ -720,37 +687,6 @@ export default class CharacterSheet extends DHBaseActorSheet { return acc; }, {}) }); - this.render(); - } - - /** - * Send item to Chat - * @type {ApplicationClickAction} - */ - static async toChat(event, button) { - if (button?.dataset?.type === 'experience') { - const experience = this.document.system.experiences[button.dataset.uuid]; - const cls = getDocumentClass('ChatMessage'); - const systemData = { - name: game.i18n.localize('DAGGERHEART.GENERAL.Experience.single'), - description: `${experience.name} ${experience.value.signedString()}` - }; - const msg = new cls({ - type: 'abilityUse', - user: game.user.id, - system: systemData, - content: await foundry.applications.handlebars.renderTemplate( - 'systems/daggerheart/templates/ui/chat/ability-use.hbs', - systemData - ) - }); - - cls.create(msg.toObject()); - } else { - const item = this.getItem(event); - if (!item) return; - item.toChat(this.document.id); - } } static useDowntime(_, button) { @@ -760,7 +696,7 @@ export default class CharacterSheet extends DHBaseActorSheet { } async _onDragStart(event) { - const item = this.getItem(event); + const item = getDocFromElement(event.target); const dragData = { type: item.documentName, diff --git a/module/applications/sheets/actors/companion.mjs b/module/applications/sheets/actors/companion.mjs index a1d00d96..87cfda27 100644 --- a/module/applications/sheets/actors/companion.mjs +++ b/module/applications/sheets/actors/companion.mjs @@ -6,11 +6,7 @@ export default class DhCompanionSheet extends DHBaseActorSheet { static DEFAULT_OPTIONS = { classes: ['actor', 'companion'], position: { width: 300 }, - actions: { - viewActor: this.viewActor, - useItem: this.useItem, - toChat: this.toChat - } + actions: {} }; static PARTS = { @@ -29,52 +25,4 @@ export default class DhCompanionSheet extends DHBaseActorSheet { labelPrefix: 'DAGGERHEART.GENERAL.Tabs' } }; - - /* -------------------------------------------- */ - /* Application Clicks Actions */ - /* -------------------------------------------- */ - - static async viewActor(_, button) { - const target = button.closest('[data-item-uuid]'); - const actor = await foundry.utils.fromUuid(target.dataset.itemUuid); - if (!actor) return; - - actor.sheet.render(true); - } - - getAction(element) { - const itemId = (element.target ?? element).closest('[data-item-id]').dataset.itemId, - item = this.document.system.actions.find(x => x.id === itemId); - return item; - } - - static async useItem(event) { - const action = this.getAction(event) ?? this.actor.system.attack; - action.use(event); - } - - static async toChat(event, button) { - if (button?.dataset?.type === 'experience') { - const experience = this.document.system.experiences[button.dataset.uuid]; - const cls = getDocumentClass('ChatMessage'); - const systemData = { - name: game.i18n.localize('DAGGERHEART.GENERAL.Experience.single'), - description: `${experience.name} ${experience.value.signedString()}` - }; - const msg = new cls({ - type: 'abilityUse', - user: game.user.id, - system: systemData, - content: await foundry.applications.handlebars.renderTemplate( - 'systems/daggerheart/templates/ui/chat/ability-use.hbs', - systemData - ) - }); - - cls.create(msg.toObject()); - } else { - const item = this.getAction(event) ?? this.document.system.attack; - item.toChat(this.document.id); - } - } } diff --git a/module/applications/sheets/actors/environment.mjs b/module/applications/sheets/actors/environment.mjs index 05958cf5..7a9c92ac 100644 --- a/module/applications/sheets/actors/environment.mjs +++ b/module/applications/sheets/actors/environment.mjs @@ -9,11 +9,7 @@ export default class DhpEnvironment extends DHBaseActorSheet { position: { width: 500 }, - actions: { - useItem: this.useItem, - useAction: this.useItem, - toChat: this.toChat - }, + actions: {}, dragDrop: [{ dragSelector: '.action-section .inventory-item', dropSelector: null }] }; @@ -36,47 +32,67 @@ export default class DhpEnvironment extends DHBaseActorSheet { } }; - /* -------------------------------------------- */ - - getItem(element) { - const itemId = (element.target ?? element).closest('[data-item-id]').dataset.itemId, - item = this.document.items.get(itemId); - return item; + /**@inheritdoc */ + async _preparePartContext(partId, context, options) { + context = await super._preparePartContext(partId, context, options); + switch (partId) { + case 'header': + await this._prepareHeaderContext(context, options); + break; + case 'notes': + await this._prepareNotesContext(context, options); + break; + } + return context; } - /* -------------------------------------------- */ - /* Application Clicks Actions */ - /* -------------------------------------------- */ - /** - * - * @type {ApplicationClickAction} + * Prepare render context for the Biography part. + * @param {ApplicationRenderContext} context + * @param {ApplicationRenderOptions} options + * @returns {Promise} + * @protected */ - async viewAdversary(_, button) { - const target = button.closest('[data-item-uuid]'); - const adversary = await foundry.utils.fromUuid(target.dataset.itemUuid); - if (!adversary) { - ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.adversaryMissing')); - return; - } + async _prepareNotesContext(context, _options) { + const { system } = this.document; + const { TextEditor } = foundry.applications.ux; - adversary.sheet.render({ force: true }); - } + const paths = { + notes: 'notes' + }; - static async useItem(event, button) { - const action = this.getItem(event); - if (!action) { - await this.viewAdversary(event, button); - } else { - action.use(event); + for (const [key, path] of Object.entries(paths)) { + const value = foundry.utils.getProperty(system, path); + context[key] = { + field: system.schema.getField(path), + value, + enriched: await TextEditor.implementation.enrichHTML(value, { + secrets: this.document.isOwner, + relativeTo: this.document + }) + }; } } - static async toChat(event) { - const item = this.getItem(event); - item.toChat(this.document.id); + /** + * Prepare render context for the Header part. + * @param {ApplicationRenderContext} context + * @param {ApplicationRenderOptions} options + * @returns {Promise} + * @protected + */ + async _prepareHeaderContext(context, _options) { + const { system } = this.document; + const { TextEditor } = foundry.applications.ux; + + context.description = await TextEditor.implementation.enrichHTML(system.description, { + secrets: this.document.isOwner, + relativeTo: this.document + }); } + /* -------------------------------------------- */ + async _onDragStart(event) { const item = event.currentTarget.closest('.inventory-item'); diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index 0e5f211d..b343eb62 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -1,5 +1,10 @@ const { HandlebarsApplicationMixin } = foundry.applications.api; -import { tagifyElement } from '../../../helpers/utils.mjs'; +import { getDocFromElement, tagifyElement } from '../../../helpers/utils.mjs'; +import DHActionConfig from '../../sheets-configs/action-config.mjs'; + +/** + * @typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction + */ /** * @typedef {object} DragDropConfig @@ -71,11 +76,32 @@ export default function DHApplicationMixin(Base) { static DEFAULT_OPTIONS = { classes: ['daggerheart', 'sheet', 'dh-style'], actions: { + triggerContextMenu: DHSheetV2.#triggerContextMenu, createDoc: DHSheetV2.#createDoc, editDoc: DHSheetV2.#editDoc, - deleteDoc: DHSheetV2.#deleteDoc + deleteDoc: DHSheetV2.#deleteDoc, + toChat: DHSheetV2.#toChat, + useItem: DHSheetV2.#useItem, + useAction: DHSheetV2.#useAction, + toggleEffect: DHSheetV2.#toggleEffect, + toggleExtended: DHSheetV2.#toggleExtended, }, - contextMenus: [], + contextMenus: [{ + handler: DHSheetV2.#getEffectContextOptions, + selector: '[data-item-uuid][data-type="effect"]', + options: { + parentClassHooks: false, + fixed: true + }, + }, + { + handler: DHSheetV2.#getActionContextOptions, + selector: '[data-item-uuid][data-type="action"]', + options: { + parentClassHooks: false, + fixed: true + } + }], dragDrop: [], tagifyConfigs: [] }; @@ -99,6 +125,27 @@ export default function DHApplicationMixin(Base) { this._createTagifyElements(this.options.tagifyConfigs); } + /* -------------------------------------------- */ + /* Sync Parts */ + /* -------------------------------------------- */ + + /**@inheritdoc */ + _syncPartState(partId, newElement, priorElement, state) { + super._syncPartState(partId, newElement, priorElement, state); + for (const el of priorElement.querySelectorAll(".extensible.extended")) { + const { actionId, itemUuid } = el.parentElement.dataset; + const selector = `${actionId ? `[data-action-id="${actionId}"]` : `[data-item-uuid="${itemUuid}"]`} .extensible`; + const newExtensible = newElement.querySelector(selector); + + if (!newExtensible) continue; + newExtensible.classList.add("extended"); + const descriptionElement = newExtensible.querySelector('.invetory-description'); + if (descriptionElement) { + this.#prepareInventoryDescription(newExtensible, descriptionElement); + } + } + } + /* -------------------------------------------- */ /* Tags */ /* -------------------------------------------- */ @@ -162,14 +209,14 @@ export default function DHApplicationMixin(Base) { * @param {DragEvent} event * @protected */ - _onDragStart(event) {} + _onDragStart(event) { } /** * Handle drop event. * @param {DragEvent} event * @protected */ - _onDrop(event) {} + _onDrop(event) { } /* -------------------------------------------- */ /* Context Menu */ @@ -185,12 +232,140 @@ export default function DHApplicationMixin(Base) { /* -------------------------------------------- */ /** - * Get the set of ContextMenu options which should be used for journal entry pages in the sidebar. - * @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} + * Get the set of ContextMenu options for DomainCards. + * @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance + * @this {CharacterSheet} * @protected */ - _getEntryContextOptions() { - return []; + static #getEffectContextOptions() { + /**@type {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} */ + const options = [ + { + name: 'disableEffect', + icon: 'fa-solid fa-lightbulb', + condition: target => !getDocFromElement(target).disabled, + callback: target => getDocFromElement(target).update({ disabled: true }) + }, + { + name: 'enableEffect', + icon: 'fa-regular fa-lightbulb', + condition: target => getDocFromElement(target).disabled, + callback: target => getDocFromElement(target).update({ disabled: false }) + }, + ].map(option => ({ + ...option, + name: `DAGGERHEART.APPLICATIONS.ContextMenu.${option.name}`, + icon: `` + })); + + return [...options, ...this._getContextMenuCommonOptions.call(this, { toChat: true })]; + } + + /** + * Get the set of ContextMenu options for Actions. + * @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance + * @this {DHSheetV2} + * @protected + */ + static #getActionContextOptions() { + /**@type {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} */ + const getAction = (target) => { + const { actionId } = target.closest('[data-action-id]').dataset; + const { actions, attack } = this.document.system; + return attack?.id === actionId ? attack : actions?.find(a => a.id === actionId); + }; + + const options = [ + { + name: 'DAGGERHEART.APPLICATIONS.ContextMenu.useItem', + icon: 'fa-solid fa-burst', + condition: this.document instanceof foundry.documents.Actor || + (this.document instanceof foundry.documents.Item && this.document.parent), + callback: (target, event) => getAction(target).use(event), + }, + { + name: 'DAGGERHEART.APPLICATIONS.ContextMenu.sendToChat', + icon: 'fa-solid fa-message', + callback: (target) => getAction(target).toChat(this.document.id), + }, + { + name: 'CONTROLS.CommonEdit', + icon: 'fa-solid fa-pen-to-square', + callback: (target) => new DHActionConfig(getAction(target)).render({ force: true }) + }, + { + name: 'CONTROLS.CommonDelete', + icon: 'fa-solid fa-trash', + condition: (target) => { + const { actionId } = target.closest('[data-action-id]').dataset; + const { attack } = this.document.system; + return attack?.id !== actionId + }, + callback: async (target) => { + const action = getAction(target) + const confirmed = await foundry.applications.api.DialogV2.confirm({ + window: { + title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', { + type: game.i18n.localize(`DAGGERHEART.GENERAL.Action.single`), + name: action.name + }) + }, + content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { name: action.name }) + }); + if (!confirmed) return; + + return this.document.update({ + 'system.actions': this.document.system.actions.filter((a) => a.id !== action.id) + }); + } + } + ].map(option => ({ + ...option, + icon: `` + })); + + return options; + } + + /** + * Get the set of ContextMenu options. + * @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance + */ + _getContextMenuCommonOptions({ usable = false, toChat = false, deletable = true }) { + const options = [ + { + name: 'CONTROLS.CommonEdit', + icon: 'fa-solid fa-pen-to-square', + callback: target => getDocFromElement(target).sheet.render({ force: true }) + }, + ]; + + if (usable) options.unshift({ + name: 'DAGGERHEART.APPLICATIONS.ContextMenu.useItem', + icon: 'fa-solid fa-burst', + callback: (target, event) => getDocFromElement(target).use(event), + }); + + if (toChat) options.unshift({ + name: 'DAGGERHEART.APPLICATIONS.ContextMenu.sendToChat', + icon: 'fa-solid fa-message', + callback: (target) => getDocFromElement(target).toChat(this.document.id), + }); + + if (deletable) options.push({ + name: 'CONTROLS.CommonDelete', + icon: 'fa-solid fa-trash', + callback: (target, event) => { + const doc = getDocFromElement(target); + if (event.shiftKey) return doc.delete(); + else return doc.deleteDialog(); + } + }) + + return options.map(option => ({ + ...option, + icon: `` + })) } /* -------------------------------------------- */ @@ -207,66 +382,229 @@ export default function DHApplicationMixin(Base) { return context; } + /* -------------------------------------------- */ + /* Prepare Descriptions */ + /* -------------------------------------------- */ + + /** + * Prepares and enriches an inventory item or action description for display. + * @param {HTMLElement} extensibleElement - The parent element containing the description. + * @param {HTMLElement} descriptionElement - The element where the enriched description will be rendered. + * @returns {Promise} + */ + async #prepareInventoryDescription(extensibleElement, descriptionElement) { + const parent = extensibleElement.closest('[data-item-uuid], [data-action-id]'); + const { actionId, itemUuid } = parent?.dataset || {}; + if (!actionId && !itemUuid) return; + + const doc = itemUuid + ? getDocFromElement(extensibleElement) + : this.document.system.attack?.id === actionId + ? this.document.system.attack + : this.document.system.actions?.find(a => a.id === actionId); + if (!doc) return; + + const description = doc.system?.description ?? doc.description; + const isAction = !!actionId; + descriptionElement.innerHTML = await foundry.applications.ux.TextEditor.implementation.enrichHTML(description, { + relativeTo: isAction ? doc.parent : doc, + rollData: doc.getRollData?.(), + secrets: isAction ? doc.parent.isOwner : doc.isOwner + }); + } + /* -------------------------------------------- */ /* Application Clicks Actions */ /* -------------------------------------------- */ /** * Create an embedded document. - * @param {PointerEvent} event - The originating click event - * @param {HTMLElement} button - The capturing HTML element which defines the [data-action="removeAction"] + * @type {ApplicationClickAction} */ - static async #createDoc(event, button) { - const { documentClass, type } = button.dataset; - const parent = this.document; + static async #createDoc(event, target) { + const { documentClass, type, inVault, disabled } = target.dataset; + const parentIsItem = this.document.documentName === 'Item'; + const parent = parentIsItem && documentClass === 'Item' ? null : this.document; - const cls = getDocumentClass(documentClass); - return await cls.createDocuments( - [ - { - name: cls.defaultName({ type, parent }), - type + if (type === 'action') { + const { type: actionType } = await foundry.applications.api.DialogV2.input({ + window: { title: 'Select Action Type' }, + content: await foundry.applications.handlebars.renderTemplate( + 'systems/daggerheart/templates/actionTypes/actionType.hbs', + { types: CONFIG.DH.ACTIONS.actionTypes } + ), + ok: { + label: game.i18n.format('DOCUMENT.Create', { + type: game.i18n.localize('DAGGERHEART.GENERAL.Action.single') + }), } - ], - { parent, renderSheet: !event.shiftKey } - ); + }) ?? {}; + if (!actionType) return; + const cls = game.system.api.models.actions.actionsTypes[actionType] + const action = new cls({ + _id: foundry.utils.randomID(), + type: actionType, + name: game.i18n.localize(CONFIG.DH.ACTIONS.actionTypes[actionType].name), + ...cls.getSourceConfig(this.document) + }, + { + parent: this.document + } + ); + await this.document.update({ 'system.actions': [...this.document.system.actions, action] }); + await new DHActionConfig(this.document.system.actions[this.document.system.actions.length - 1]).render({ + force: true + }); + return action; + + } else { + const cls = getDocumentClass(documentClass); + const data = { + name: cls.defaultName({ type, parent }), + type, + } + if (inVault) data["system.inVault"] = true; + if (disabled) data.disabled = true; + + const doc = await cls.create(data, { parent, renderSheet: !event.shiftKey }); + if (parentIsItem && type === 'feature') { + await this.document.update({ + 'system.features': this.document.system.toObject().features.concat(doc.uuid) + }); + } + return doc; + } + } /** * Renders an embedded document. - * @param {PointerEvent} event - The originating click event - * @param {HTMLElement} button - The capturing HTML element which defines the [data-action="removeAction"] + * @type {ApplicationClickAction} */ - static #editDoc(_event, button) { - const { type, docId } = button.dataset; - this.document.getEmbeddedDocument(type, docId, { strict: true }).sheet.render({ force: true }); + static #editDoc(_event, target) { + const doc = getDocFromElement(target); + if (doc) return doc.sheet.render({ force: true }); + + // TODO: REDO this + const { actionId } = target.closest('[data-action-id]').dataset; + const { actions, attack } = this.document.system; + const action = attack?.id === actionId ? attack : actions?.find(a => a.id === actionId); + new DHActionConfig(action).render({ force: true }) } /** * Delete an embedded document. - * @param {PointerEvent} _event - The originating click event - * @param {HTMLElement} button - The capturing HTML element which defines the [data-action="removeAction"] + * @type {ApplicationClickAction} */ - static async #deleteDoc(_event, button) { - const { type, docId } = button.dataset; - const document = this.document.getEmbeddedDocument(type, docId, { strict: true }); - const typeName = game.i18n.localize( - document.type === 'base' ? `DOCUMENT.${type}` : `TYPES.${type}.${document.type}` - ); + static async #deleteDoc(event, target) { + const doc = getDocFromElement(target); - const confirmed = await foundry.applications.api.DialogV2.confirm({ - window: { - title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', { - type: typeName, - name: document.name - }) - }, - content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { name: document.name }) + if (doc) { + if (event.shiftKey) return doc.delete() + else return await doc.deleteDialog() + } + + // TODO: REDO this + const { actionId } = target.closest('[data-action-id]').dataset; + const { actions, attack } = this.document.system; + if (attack?.id === actionId) return; + const action = actions.find(a => a.id === actionId); + + if (!event.shiftKey) { + const confirmed = await foundry.applications.api.DialogV2.confirm({ + window: { + title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', { + type: game.i18n.localize(`DAGGERHEART.GENERAL.Action.single`), + name: action.name + }) + }, + content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { name: action.name }) + }); + if (!confirmed) return; + } + + return await this.document.update({ + 'system.actions': actions.filter((a) => a.id !== action.id) }); - if (!confirmed) return; - - await document.delete(); } + + /** + * Send item to Chat + * @type {ApplicationClickAction} + */ + static async #toChat(_event, target) { + let doc = getDocFromElement(target); + + // TODO: REDO this + if (!doc) { + const { actionId } = target.closest('[data-action-id]').dataset; + const { actions, attack } = this.document.system; + doc = attack?.id === actionId ? attack : actions?.find(a => a.id === actionId); + } + return doc.toChat(this.document.id); + } + + /** + * Use a item + * @type {ApplicationClickAction} + */ + static async #useItem(event, target) { + let doc = getDocFromElement(target); + // TODO: REDO this + if (!doc) { + const { actionId } = target.closest('[data-action-id]').dataset; + const { actions, attack } = this.document.system; + doc = attack?.id === actionId ? attack : actions?.find(a => a.id === actionId); + if(this.document instanceof foundry.documents.Item && !this.document.parent) return; + } + + await doc.use(event); + } + + /** + * Use a item + * @type {ApplicationClickAction} + */ + static async #useAction(event, target) { + const doc = getDocFromElement(target); + const { actionId } = target.closest('[data-action-id]').dataset; + const { actions, attack } = doc.system; + const action = attack?.id === actionId ? attack : actions?.find(a => a.id === actionId); + await action.use(event); + } + + + /** + * Toggle a ActiveEffect + * @type {ApplicationClickAction} + */ + static async #toggleEffect(_, target) { + const doc = getDocFromElement(target); + await doc.update({ disabled: !doc.disabled }); + } + + /** + * Trigger the context menu. + * @type {ApplicationClickAction} + */ + static #triggerContextMenu(event, _) { + return CONFIG.ux.ContextMenu.triggerContextMenu(event); + } + + /** + * Toggle the 'extended' class on the .extensible element inside inventory-item-content + * @type {ApplicationClickAction} + * @this {DHSheetV2} + */ + static async #toggleExtended(_, target) { + const container = target.closest('.inventory-item'); + const extensible = container?.querySelector('.extensible'); + const t = extensible?.classList.toggle('extended'); + + const descriptionElement = extensible?.querySelector('.invetory-description'); + if (t && !!descriptionElement) this.#prepareInventoryDescription(extensible, descriptionElement); + } + } return DHSheetV2; diff --git a/module/applications/sheets/api/base-actor.mjs b/module/applications/sheets/api/base-actor.mjs index 78df0aac..56f00e9b 100644 --- a/module/applications/sheets/api/base-actor.mjs +++ b/module/applications/sheets/api/base-actor.mjs @@ -21,11 +21,24 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { submitOnChange: true }, actions: { - openSettings: DHBaseActorSheet.#openSettings + openSettings: DHBaseActorSheet.#openSettings, + sendExpToChat: DHBaseActorSheet.#sendExpToChat, }, + contextMenus: [ + { + handler: DHBaseActorSheet.#getFeatureContextOptions, + selector: '[data-item-uuid][data-type="feature"]', + options: { + parentClassHooks: false, + fixed: true + } + } + ], dragDrop: [{ dragSelector: '.inventory-item[data-type="attack"]', dropSelector: null }] }; + /* -------------------------------------------- */ + /**@type {typeof DHBaseActorSettings}*/ #settingSheet; @@ -35,6 +48,10 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { return (this.#settingSheet ??= SheetClass ? new SheetClass({ document: this.document }) : null); } + /* -------------------------------------------- */ + /* Prepare Context */ + /* -------------------------------------------- */ + /**@inheritdoc */ async _prepareContext(_options) { const context = await super._prepareContext(_options); @@ -42,6 +59,56 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { return context; } + + /**@inheritdoc */ + async _preparePartContext(partId, context, options) { + context = await super._preparePartContext(partId, context, options); + switch (partId) { + case 'effects': + await this._prepareEffectsContext(context, options); + break; + } + return context; + } + + /** + * Prepare render context for the Effect part. + * @param {ApplicationRenderContext} context + * @param {ApplicationRenderOptions} options + * @returns {Promise} + * @protected + */ + async _prepareEffectsContext(context, _options) { + context.effects = { + actives: [], + inactives: [], + }; + + for (const effect of this.actor.allApplicableEffects()) { + const list = effect.active ? context.effects.actives : context.effects.inactives; + list.push(effect); + } + } + + /* -------------------------------------------- */ + /* Context Menu */ + /* -------------------------------------------- */ + + /** + * Get the set of ContextMenu options for Features. + * @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance + * @this {DHSheetV2} + * @protected + */ + static #getFeatureContextOptions() { + return this._getContextMenuCommonOptions.call(this, { usable: true, toChat: true }); + } + + + /* -------------------------------------------- */ + /* Application Clicks Actions */ + /* -------------------------------------------- */ + /** * Open the Actor Setting Sheet * @type {ApplicationClickAction} @@ -50,6 +117,29 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { await this.settingSheet.render({ force: true }); } + /** + * Send Experience to Chat + * @type {ApplicationClickAction} + */ + static async #sendExpToChat(_, button) { + const experience = this.document.system.experiences[button.dataset.id]; + + const systemData = { + name: game.i18n.localize('DAGGERHEART.GENERAL.Experience.single'), + description: `${experience.name} ${experience.value.signedString()}` + }; + + foundry.documents.ChatMessage.implementation.create({ + type: 'abilityUse', + user: game.user.id, + system: systemData, + content: await foundry.applications.handlebars.renderTemplate( + 'systems/daggerheart/templates/ui/chat/ability-use.hbs', + systemData + ) + }); + } + /* -------------------------------------------- */ /* Application Drag/Drop */ /* -------------------------------------------- */ diff --git a/module/applications/sheets/api/base-item.mjs b/module/applications/sheets/api/base-item.mjs index 723b4802..766b443c 100644 --- a/module/applications/sheets/api/base-item.mjs +++ b/module/applications/sheets/api/base-item.mjs @@ -1,4 +1,4 @@ -import DHActionConfig from '../../sheets-configs/action-config.mjs'; +import { getDocFromElement } from '../../../helpers/utils.mjs'; import DHApplicationMixin from './application-mixin.mjs'; const { ItemSheetV2 } = foundry.applications.sheets; @@ -15,16 +15,14 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { static DEFAULT_OPTIONS = { classes: ['item'], position: { width: 600 }, + window: { resizable: true }, form: { submitOnChange: true }, actions: { - addAction: DHBaseItemSheet.#addAction, - editAction: DHBaseItemSheet.#editAction, removeAction: DHBaseItemSheet.#removeAction, addFeature: DHBaseItemSheet.#addFeature, - editFeature: DHBaseItemSheet.#editFeature, - removeFeature: DHBaseItemSheet.#removeFeature, + deleteFeature: DHBaseItemSheet.#deleteFeature, addResource: DHBaseItemSheet.#addResource, removeResource: DHBaseItemSheet.#removeResource }, @@ -32,6 +30,16 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { { dragSelector: null, dropSelector: '.tab.features .drop-section' }, { dragSelector: '.feature-item', dropSelector: null }, { dragSelector: '.action-item', dropSelector: null } + ], + contextMenus: [ + { + handler: DHBaseItemSheet.#getFeatureContextOptions, + selector: '[data-item-uuid][data-type="feature"]', + options: { + parentClassHooks: false, + fixed: true + } + } ] }; @@ -40,7 +48,7 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { /** @inheritdoc */ static TABS = { primary: { - tabs: [{ id: 'description' }, { id: 'settings' }, { id: 'actions' }], + tabs: [{ id: 'description' }, { id: 'settings' }, { id: 'actions' }, { id: 'effects' }], initial: 'description', labelPrefix: 'DAGGERHEART.GENERAL.Tabs' } @@ -51,8 +59,8 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { /* -------------------------------------------- */ /**@inheritdoc */ - async _preparePartContext(partId, context) { - await super._preparePartContext(partId, context); + async _preparePartContext(partId, context, options) { + await super._preparePartContext(partId, context, options); const { TextEditor } = foundry.applications.ux; switch (partId) { @@ -64,77 +72,78 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { secrets: this.item.isOwner }); break; + case "effects": + await this._prepareEffectsContext(context, options) + break; + case "features": + context.isGM = game.user.isGM; + break; } return context; } - /* -------------------------------------------- */ - /* Application Clicks Actions */ - /* -------------------------------------------- */ - /** - * Render a dialog prompting the user to select an action type. - * - * @returns {Promise} An object containing the selected action type. + * Prepare render context for the Effect part. + * @param {ApplicationRenderContext} context + * @param {ApplicationRenderOptions} options + * @returns {Promise} + * @protected */ - static async selectActionType() { - const content = await foundry.applications.handlebars.renderTemplate( - 'systems/daggerheart/templates/actionTypes/actionType.hbs', - { types: CONFIG.DH.ACTIONS.actionTypes } - ), - title = 'Select Action Type'; + async _prepareEffectsContext(context, _options) { + context.effects = { + actives: [], + inactives: [], + }; - return foundry.applications.api.DialogV2.prompt({ - window: { title }, - content, - ok: { - label: title, - callback: (event, button, dialog) => button.form.elements.type.value - } - }); - } - - /** - * Add a new action to the item, prompting the user for its type. - * @type {ApplicationClickAction} - */ - static async #addAction(_event, _button) { - const actionType = await DHBaseItemSheet.selectActionType(); - if (!actionType) return; - try { - const cls = - game.system.api.models.actions.actionsTypes[actionType] ?? - game.system.api.models.actions.actionsTypes.attack, - action = new cls( - { - _id: foundry.utils.randomID(), - type: actionType, - name: game.i18n.localize(CONFIG.DH.ACTIONS.actionTypes[actionType].name), - ...cls.getSourceConfig(this.document) - }, - { - parent: this.document - } - ); - await this.document.update({ 'system.actions': [...this.document.system.actions, action] }); - await new DHActionConfig(this.document.system.actions[this.document.system.actions.length - 1]).render({ - force: true - }); - } catch (error) { - console.log(error); + for (const effect of this.item.effects) { + const list = effect.active ? context.effects.actives : context.effects.inactives; + list.push(effect); } } + /* -------------------------------------------- */ + /* Context Menu */ + /* -------------------------------------------- */ + /** - * Edit an existing action on the item - * @type {ApplicationClickAction} + * Get the set of ContextMenu options for Features. + * @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance + * @this {DHSheetV2} + * @protected */ - static async #editAction(_event, button) { - const action = this.document.system.actions[button.dataset.index]; - await new DHActionConfig(action).render({ force: true }); + static #getFeatureContextOptions() { + const options = this._getContextMenuCommonOptions({ usable: true, toChat: true, deletable: false }) + options.push( + { + name: 'CONTROLS.CommonDelete', + icon: '', + callback: async (target) => { + const feature = getDocFromElement(target); + if (!feature) return; + const confirmed = await foundry.applications.api.DialogV2.confirm({ + window: { + title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', { + type: game.i18n.localize(`TYPES.Item.feature`), + name: feature.name + }) + }, + content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { name: feature.name }) + }); + if (!confirmed) return; + await this.document.update({ + 'system.features': this.document.system.toObject().features.filter(uuid => uuid !== feature.uuid) + }); + }, + } + ) + return options; } + /* -------------------------------------------- */ + /* Application Clicks Actions */ + /* -------------------------------------------- */ + /** * Remove an action from the item. * @type {ApplicationClickAction} @@ -144,16 +153,19 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { const actionIndex = button.closest('[data-index]').dataset.index; const action = this.document.system.actions[actionIndex]; - const confirmed = await foundry.applications.api.DialogV2.confirm({ - window: { - title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', { - type: game.i18n.localize(`DAGGERHEART.GENERAL.Action.single`), - name: action.name - }) - }, - content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { name: action.name }) - }); - if (!confirmed) return; + if(!event.shiftKey) { + const confirmed = await foundry.applications.api.DialogV2.confirm({ + window: { + title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', { + type: game.i18n.localize(`DAGGERHEART.GENERAL.Action.single`), + name: action.name + }) + }, + content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { name: action.name }) + }); + if (!confirmed) return; + } + await this.document.update({ 'system.actions': this.document.system.actions.filter((_, index) => index !== Number.parseInt(actionIndex)) @@ -164,57 +176,31 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { * Add a new feature to the item, prompting the user for its type. * @type {ApplicationClickAction} */ - static async #addFeature(_event, _button) { - const feature = await game.items.documentClass.create({ + static async #addFeature(_, target) { + const { type } = target.dataset; + const cls = foundry.documents.Item.implementation; + const feature = await cls.create({ type: 'feature', - name: game.i18n.format('DOCUMENT.New', { type: game.i18n.localize('TYPES.Item.feature') }) + name: cls.defaultName({ type: 'feature' }), + "system.subType": CONFIG.DH.ITEM.featureSubTypes[type] }); await this.document.update({ - 'system.features': [...this.document.system.features.filter(x => x).map(x => x.uuid), feature.uuid] + 'system.features': [...this.document.system.features, feature].map(f => f.uuid) }); } - /** - * Edit an existing feature on the item - * @type {ApplicationClickAction} - */ - static async #editFeature(_event, button) { - const target = button.closest('.feature-item'); - const feature = this.document.system.features.find(x => x?.id === target.id); - if (!feature) { - ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureIsMissing')); - return; - } - - feature.sheet.render(true); - } - /** * Remove a feature from the item. * @type {ApplicationClickAction} */ - static async #removeFeature(event, button) { - event.stopPropagation(); - const target = button.closest('.feature-item'); - const feature = this.document.system.features.find(x => x && x.id === target.id); - - if (feature) { - const confirmed = await foundry.applications.api.DialogV2.confirm({ - window: { - title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', { - type: game.i18n.localize(`TYPES.Item.feature`), - name: feature.name - }) - }, - content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { name: feature.name }) - }); - if (!confirmed) return; - } - + static async #deleteFeature(_, target) { + const feature = getDocFromElement(target); + if (!feature) return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureIsMissing')); + await feature.update({ 'system.subType': null }); await this.document.update({ 'system.features': this.document.system.features - .filter(feature => feature && feature.id !== target.id) .map(x => x.uuid) + .filter(uuid => uuid !== feature.uuid) }); } diff --git a/module/applications/sheets/items/ancestry.mjs b/module/applications/sheets/items/ancestry.mjs index de55301d..fc0b8a52 100644 --- a/module/applications/sheets/items/ancestry.mjs +++ b/module/applications/sheets/items/ancestry.mjs @@ -5,7 +5,6 @@ export default class AncestrySheet extends DHHeritageSheet { static DEFAULT_OPTIONS = { classes: ['ancestry'], actions: { - addFeature: AncestrySheet.#addFeature, editFeature: AncestrySheet.#editFeature, removeFeature: AncestrySheet.#removeFeature }, @@ -23,23 +22,6 @@ export default class AncestrySheet extends DHHeritageSheet { /* Application Clicks Actions */ /* -------------------------------------------- */ - /** - * Add a new feature to the item, prompting the user for its type. - * @type {ApplicationClickAction} - */ - static async #addFeature(_event, button) { - const feature = await game.items.documentClass.create({ - type: 'feature', - name: game.i18n.format('DOCUMENT.New', { type: game.i18n.localize('TYPES.Item.feature') }), - system: { - subType: button.dataset.type - } - }); - await this.document.update({ - 'system.features': [...this.document.system.features.map(x => x.uuid), feature.uuid] - }); - } - /** * Edit an existing feature on the item * @type {ApplicationClickAction} diff --git a/module/applications/sheets/items/armor.mjs b/module/applications/sheets/items/armor.mjs index f3a38068..80bbe139 100644 --- a/module/applications/sheets/items/armor.mjs +++ b/module/applications/sheets/items/armor.mjs @@ -27,7 +27,11 @@ export default class ArmorSheet extends ItemAttachmentSheet(DHBaseItemSheet) { template: 'systems/daggerheart/templates/sheets/items/armor/settings.hbs', scrollable: ['.settings'] }, - ...super.PARTS + effects: { + template: 'systems/daggerheart/templates/sheets/global/tabs/tab-effects.hbs', + scrollable: ['.effects'] + }, + ...super.PARTS, }; /**@inheritdoc */ diff --git a/module/applications/sheets/items/beastform.mjs b/module/applications/sheets/items/beastform.mjs index 194f3ab1..db769e94 100644 --- a/module/applications/sheets/items/beastform.mjs +++ b/module/applications/sheets/items/beastform.mjs @@ -28,20 +28,4 @@ export default class BeastformSheet extends DHBaseItemSheet { labelPrefix: 'DAGGERHEART.GENERAL.Tabs' } }; - - /**@inheritdoc */ - async _prepareContext(_options) { - const context = await super._prepareContext(_options); - - context.document = context.document.toObject(); - context.document.effects = this.document.effects.map(effect => { - const data = effect.toObject(); - data.id = effect.id; - if (effect.type === 'beastform') data.mandatory = true; - - return data; - }); - - return context; - } } diff --git a/module/applications/sheets/items/class.mjs b/module/applications/sheets/items/class.mjs index 3a34bcec..11feb1b2 100644 --- a/module/applications/sheets/items/class.mjs +++ b/module/applications/sheets/items/class.mjs @@ -10,10 +10,6 @@ export default class ClassSheet extends DHBaseItemSheet { actions: { removeItemFromCollection: ClassSheet.#removeItemFromCollection, removeSuggestedItem: ClassSheet.#removeSuggestedItem, - viewDoc: ClassSheet.#viewDoc, - addFeature: this.addFeature, - editFeature: this.editFeature, - deleteFeature: this.deleteFeature }, tagifyConfigs: [ { @@ -46,13 +42,17 @@ export default class ClassSheet extends DHBaseItemSheet { settings: { template: 'systems/daggerheart/templates/sheets/items/class/settings.hbs', scrollable: ['.settings'] + }, + effects: { + template: 'systems/daggerheart/templates/sheets/global/tabs/tab-effects.hbs', + scrollable: ['.effects'] } }; /** @inheritdoc */ static TABS = { primary: { - tabs: [{ id: 'description' }, { id: 'features' }, { id: 'settings' }], + tabs: [{ id: 'description' }, { id: 'features' }, { id: 'settings' }, { id: 'effects' }], initial: 'description', labelPrefix: 'DAGGERHEART.GENERAL.Tabs' } @@ -179,59 +179,4 @@ export default class ClassSheet extends DHBaseItemSheet { const { target } = element.dataset; await this.document.update({ [`system.characterGuide.${target}`]: null }); } - - /** - * Open the sheet of a item by UUID. - * @param {PointerEvent} _event - - * @param {HTMLElement} button - */ - static async #viewDoc(_event, button) { - const doc = await fromUuid(button.dataset.uuid); - doc.sheet.render({ force: true }); - } - - static async addFeature(_, target) { - const feature = await game.items.documentClass.create({ - type: 'feature', - name: game.i18n.format('DOCUMENT.New', { type: game.i18n.localize('TYPES.Item.feature') }), - system: { - subType: - target.dataset.type === 'hope' - ? CONFIG.DH.ITEM.featureSubTypes.hope - : CONFIG.DH.ITEM.featureSubTypes.class - } - }); - await this.document.update({ - [`system.features`]: [...this.document.system.features.filter(x => x).map(x => x.uuid), feature.uuid] - }); - } - - static async editFeature(_, button) { - const target = button.closest('.feature-item'); - const feature = this.document.system.features.find(x => x?.id === target.dataset.featureId); - if (!feature) { - ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureIsMissing')); - return; - } - - feature.sheet.render(true); - } - - static async deleteFeature(event, button) { - event.stopPropagation(); - const target = button.closest('.feature-item'); - - const feature = this.document.system.features.find( - feature => feature && feature.id === target.dataset.featureId - ); - if (feature) { - await feature.update({ 'system.subType': null }); - } - - await this.document.update({ - [`system.features`]: this.document.system.features - .filter(feature => feature && feature.id !== target.dataset.featureId) - .map(x => x.uuid) - }); - } } diff --git a/module/applications/sheets/items/community.mjs b/module/applications/sheets/items/community.mjs index 387556d9..3650dec6 100644 --- a/module/applications/sheets/items/community.mjs +++ b/module/applications/sheets/items/community.mjs @@ -10,7 +10,7 @@ export default class CommunitySheet extends DHHeritageSheet { static PARTS = { header: { template: 'systems/daggerheart/templates/sheets/items/community/header.hbs' }, ...super.PARTS, - feature: { + features: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-features.hbs', scrollable: ['.feature'] } diff --git a/module/applications/sheets/items/consumable.mjs b/module/applications/sheets/items/consumable.mjs index f6fb1ba5..de09744c 100644 --- a/module/applications/sheets/items/consumable.mjs +++ b/module/applications/sheets/items/consumable.mjs @@ -19,6 +19,10 @@ export default class ConsumableSheet extends DHBaseItemSheet { settings: { template: 'systems/daggerheart/templates/sheets/items/consumable/settings.hbs', scrollable: ['.settings'] + }, + effects: { + template: 'systems/daggerheart/templates/sheets/global/tabs/tab-effects.hbs', + scrollable: ['.effects'] } }; } diff --git a/module/applications/sheets/items/feature.mjs b/module/applications/sheets/items/feature.mjs index ca375fc7..fcbec97f 100644 --- a/module/applications/sheets/items/feature.mjs +++ b/module/applications/sheets/items/feature.mjs @@ -1,5 +1,3 @@ -import { actionsTypes } from '../../../data/action/_module.mjs'; -import DHActionConfig from '../../sheets-configs/action-config.mjs'; import DHBaseItemSheet from '../api/base-item.mjs'; export default class FeatureSheet extends DHBaseItemSheet { @@ -7,13 +5,7 @@ export default class FeatureSheet extends DHBaseItemSheet { static DEFAULT_OPTIONS = { id: 'daggerheart-feature', classes: ['feature'], - position: { height: 600 }, - window: { resizable: true }, - actions: { - addAction: FeatureSheet.#addAction, - editAction: FeatureSheet.#editAction, - removeAction: FeatureSheet.#removeAction - } + actions: {} }; /**@override */ @@ -40,96 +32,4 @@ export default class FeatureSheet extends DHBaseItemSheet { labelPrefix: 'DAGGERHEART.GENERAL.Tabs' } }; - - /* -------------------------------------------- */ - - /**@inheritdoc */ - async _prepareContext(_options) { - const context = await super._prepareContext(_options); - - return context; - } - - /* -------------------------------------------- */ - /* Application Clicks Actions */ - /* -------------------------------------------- */ - - /** - * Render a dialog prompting the user to select an action type. - * - * @returns {Promise} An object containing the selected action type. - */ - static async selectActionType() { - const content = await foundry.applications.handlebars.renderTemplate( - 'systems/daggerheart/templates/actionTypes/actionType.hbs', - { - types: CONFIG.DH.ACTIONS.actionTypes, - itemName: game.i18n.localize('DAGGERHEART.CONFIG.SelectAction.selectType') - } - ), - title = game.i18n.localize('DAGGERHEART.CONFIG.SelectAction.selectType'); - - return foundry.applications.api.DialogV2.prompt({ - window: { title }, - classes: ['daggerheart', 'dh-style'], - content, - ok: { - label: title, - callback: (event, button, dialog) => button.form.elements.type.value - } - }); - } - - /** - * Add a new action to the item, prompting the user for its type. - * @param {PointerEvent} _event - The originating click event - * @param {HTMLElement} _button - The capturing HTML element which defines the [data-action="addAction"] - */ - static async #addAction(_event, _button) { - const actionType = await FeatureSheet.selectActionType(); - if (!actionType) return; - try { - const cls = actionsTypes[actionType] ?? actionsTypes.attack, - action = new cls( - { - _id: foundry.utils.randomID(), - type: actionType, - name: game.i18n.localize(CONFIG.DH.ACTIONS.actionTypes[actionType].name), - ...cls.getSourceConfig(this.document) - }, - { - parent: this.document - } - ); - await this.document.update({ 'system.actions': [...this.document.system.actions, action] }); - await new DHActionConfig(this.document.system.actions[this.document.system.actions.length - 1]).render({ - force: true - }); - } catch (error) { - console.log(error); - } - } - - /** - * Edit an existing action on the item - * @param {PointerEvent} _event - The originating click event - * @param {HTMLElement} button - The capturing HTML element which defines the [data-action="editAction"] - */ - static async #editAction(_event, button) { - const action = this.document.system.actions[button.dataset.index]; - await new DHActionConfig(action).render({ force: true }); - } - - /** - * Remove an action from the item. - * @param {PointerEvent} event - The originating click event - * @param {HTMLElement} button - The capturing HTML element which defines the [data-action="removeAction"] - */ - static async #removeAction(event, button) { - event.stopPropagation(); - const actionIndex = button.closest('[data-index]').dataset.index; - await this.document.update({ - 'system.actions': this.document.system.actions.filter((_, index) => index !== Number.parseInt(actionIndex)) - }); - } } diff --git a/module/applications/sheets/items/miscellaneous.mjs b/module/applications/sheets/items/miscellaneous.mjs index 62df4e8c..0b4b3139 100644 --- a/module/applications/sheets/items/miscellaneous.mjs +++ b/module/applications/sheets/items/miscellaneous.mjs @@ -19,6 +19,10 @@ export default class MiscellaneousSheet extends DHBaseItemSheet { settings: { template: 'systems/daggerheart/templates/sheets/items/miscellaneous/settings.hbs', scrollable: ['.settings'] + }, + effects: { + template: 'systems/daggerheart/templates/sheets/global/tabs/tab-effects.hbs', + scrollable: ['.effects'] } }; } diff --git a/module/applications/sheets/items/subclass.mjs b/module/applications/sheets/items/subclass.mjs index fcf62f69..585bd6cb 100644 --- a/module/applications/sheets/items/subclass.mjs +++ b/module/applications/sheets/items/subclass.mjs @@ -6,11 +6,7 @@ export default class SubclassSheet extends DHBaseItemSheet { classes: ['subclass'], position: { width: 600 }, window: { resizable: false }, - actions: { - addFeature: this.addFeature, - editFeature: this.editFeature, - deleteFeature: this.deleteFeature - } + actions: {} }; /**@override */ @@ -25,64 +21,22 @@ export default class SubclassSheet extends DHBaseItemSheet { settings: { template: 'systems/daggerheart/templates/sheets/items/subclass/settings.hbs', scrollable: ['.settings'] + }, + effects: { + template: 'systems/daggerheart/templates/sheets/global/tabs/tab-effects.hbs', + scrollable: ['.effects'] } }; /** @inheritdoc */ static TABS = { primary: { - tabs: [{ id: 'description' }, { id: 'features' }, { id: 'settings' }], + tabs: [{ id: 'description' }, { id: 'features' }, { id: 'settings' }, { id: 'effects' }], initial: 'description', labelPrefix: 'DAGGERHEART.GENERAL.Tabs' } }; - static async addFeature(_, target) { - const feature = await game.items.documentClass.create({ - type: 'feature', - name: game.i18n.format('DOCUMENT.New', { type: game.i18n.localize('TYPES.Item.feature') }), - system: { - subType: - target.dataset.type === 'foundation' - ? CONFIG.DH.ITEM.featureSubTypes.foundation - : target.dataset.type === 'specialization' - ? CONFIG.DH.ITEM.featureSubTypes.specialization - : CONFIG.DH.ITEM.featureSubTypes.mastery - } - }); - await this.document.update({ - [`system.features`]: [...this.document.system.features.map(x => x.uuid), feature.uuid] - }); - } - - static async editFeature(_, button) { - const feature = this.document.system.features.find(x => x.id === button.dataset.feature); - if (!feature) { - ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureIsMissing')); - return; - } - - if (feature) { - await feature.update({ 'system.subType': null }); - } - - feature.sheet.render(true); - } - - static async deleteFeature(event, target) { - event.stopPropagation(); - const feature = this.document.system.features.find(feature => feature.id === target.dataset.feature); - if (feature) { - await feature.update({ 'system.subType': null }); - } - - await this.document.update({ - [`system.features`]: this.document.system.features - .filter(feature => feature && feature.id !== target.dataset.feature) - .map(x => x.uuid) - }); - } - async _onDragStart(event) { const featureItem = event.currentTarget.closest('.drop-section'); diff --git a/module/applications/sheets/items/weapon.mjs b/module/applications/sheets/items/weapon.mjs index c601f926..3fb725d7 100644 --- a/module/applications/sheets/items/weapon.mjs +++ b/module/applications/sheets/items/weapon.mjs @@ -27,7 +27,11 @@ export default class WeaponSheet extends ItemAttachmentSheet(DHBaseItemSheet) { template: 'systems/daggerheart/templates/sheets/items/weapon/settings.hbs', scrollable: ['.settings'] }, - ...super.PARTS + effects: { + template: 'systems/daggerheart/templates/sheets/global/tabs/tab-effects.hbs', + scrollable: ['.effects'] + }, + ...super.PARTS, }; /**@inheritdoc */ diff --git a/module/applications/ux/contextMenu.mjs b/module/applications/ux/contextMenu.mjs index a913c450..09454848 100644 --- a/module/applications/ux/contextMenu.mjs +++ b/module/applications/ux/contextMenu.mjs @@ -100,7 +100,7 @@ export default class DHContextMenu extends foundry.applications.ux.ContextMenu { event.preventDefault(); event.stopPropagation(); const { clientX, clientY } = event; - const selector = '[data-item-id]'; + const selector = '[data-item-uuid]'; const target = event.target.closest(selector) ?? event.currentTarget.closest(selector); target?.dispatchEvent( new PointerEvent('contextmenu', { diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index 292eedb7..c484465a 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -67,7 +67,7 @@ export default class DhCharacter extends BaseDataActor { scars: new fields.TypedObjectField( new fields.SchemaField({ name: new fields.StringField({}), - description: new fields.HTMLField() + description: new fields.StringField() }) ), biography: new fields.SchemaField({ diff --git a/module/data/actor/companion.mjs b/module/data/actor/companion.mjs index fc8195d8..b7774284 100644 --- a/module/data/actor/companion.mjs +++ b/module/data/actor/companion.mjs @@ -13,6 +13,7 @@ export default class DhCompanion extends BaseDataActor { return foundry.utils.mergeObject(super.metadata, { label: 'TYPES.Actor.companion', type: 'companion', + isNPC: false, settingSheet: DHCompanionSettings }); } diff --git a/module/helpers/handlebarsHelper.mjs b/module/helpers/handlebarsHelper.mjs index 17326dc2..0a57a6a6 100644 --- a/module/helpers/handlebarsHelper.mjs +++ b/module/helpers/handlebarsHelper.mjs @@ -8,10 +8,10 @@ export default class RegisterHandlebarsHelpers { times: this.times, damageFormula: this.damageFormula, damageSymbols: this.damageSymbols, - rollParsed: this.rollParsed + rollParsed: this.rollParsed, + hasProperty: foundry.utils.hasProperty, }); } - static add(a, b) { const aNum = Number.parseInt(a); const bNum = Number.parseInt(b); diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index 91671e36..2aacd5c7 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -300,6 +300,18 @@ export const updateActorTokens = async (actor, update) => { } }; +/** + * Retrieves a Foundry document associated with the nearest ancestor element + * that has a `data-item-uuid` attribute. + * @param {HTMLElement} element - The DOM element to start the search from. + * @returns {foundry.abstract.Document|null} The resolved document, or null if not found or invalid. + */ +export function getDocFromElement(element) { + const target = element.closest('[data-item-uuid]'); + return foundry.utils.fromUuidSync(target.dataset.itemUuid) ?? null; +} + + export const itemAbleRollParse = (value, actor, item) => { if (!value) return value; diff --git a/module/systemRegistration/handlebars.mjs b/module/systemRegistration/handlebars.mjs index 0c455750..ec5f2df1 100644 --- a/module/systemRegistration/handlebars.mjs +++ b/module/systemRegistration/handlebars.mjs @@ -1,10 +1,12 @@ export const preloadHandlebarsTemplates = async function () { + foundry.applications.handlebars.loadTemplates({ + 'daggerheart.inventory-items': 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs', + 'daggerheart.inventory-item': 'systems/daggerheart/templates/sheets/global/partials/inventory-item-V2.hbs', + }) return foundry.applications.handlebars.loadTemplates([ 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs', - 'systems/daggerheart/templates/sheets/global/partials/inventory-item.hbs', 'systems/daggerheart/templates/sheets/global/partials/action-item.hbs', 'systems/daggerheart/templates/sheets/global/partials/domain-card-item.hbs', - 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs', 'systems/daggerheart/templates/sheets/global/partials/item-resource.hbs', 'systems/daggerheart/templates/sheets/global/partials/resource-section.hbs', 'systems/daggerheart/templates/components/card-preview.hbs', diff --git a/styles/less/global/inventory-item.less b/styles/less/global/inventory-item.less index 30fe920c..d758085b 100644 --- a/styles/less/global/inventory-item.less +++ b/styles/less/global/inventory-item.less @@ -12,87 +12,155 @@ } } +.appTheme({}, { + .inventory-item-header .img-portait .roll-img { + filter: invert(1); + } +}); + .application.daggerheart.dh-style { .inventory-item { - display: grid; - grid-template-columns: 40px 1fr 60px; - gap: 10px; width: 100%; + list-style-type: none; - .item-img { - height: 40px; - width: 40px; - border-radius: 3px; - border: none; - cursor: pointer; - object-fit: cover; - - &.actor-img { - border-radius: 50%; - } - } - - .item-label-wrapper { - display: grid; - grid-template-columns: 1fr 60px; - } - - .item-label { - font-family: @font-body; - align-self: center; - - &.fullWidth { - grid-column: span 2; - } - - .item-name { - font-size: 14px; - } - - .item-tags, - .item-labels { - display: flex; - gap: 10px; - - .tag { - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; - padding: 3px 5px; - font-size: 12px; - - background: light-dark(@dark-15, @beige-15); - border: 1px solid light-dark(@dark, @beige); - border-radius: 3px; + &:hover { + .inventory-item-header .img-portait { + .roll-img { + opacity: 1; } - - .label { - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; - font-size: 12px; - gap: 4px; + .item-img { + opacity: 0; } } } - .controls { + .inventory-item-header { display: flex; align-items: center; - justify-content: end; - gap: 8px; + gap: 10px; + cursor: pointer; - a { - text-align: center; + .img-portait { + flex: 0 0 40px; + height: 40px; + cursor: pointer; + position: relative; - &.unequipped { - opacity: 0.4; + .item-img, + .roll-img { + position: absolute; + transition: opacity 300ms ease-in; + } + + .roll-img { + opacity: 0; + } + } + + .item-label { + flex: 1; + font-family: @font-body; + align-self: center; + + .item-name { + font-size: 14px; + } + + .item-tags, + .item-labels { + display: flex; + gap: 10px; + + .tag, + .label { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + font-size: 12px; + } + + .tag { + padding: 3px 5px; + background: light-dark(@dark-15, @beige-15); + border: 1px solid light-dark(@dark, @beige); + border-radius: 3px; + } + + .label { + gap: 4px; + } + } + } + + .item-resource { + flex: 0 0 60px; + display: flex; + align-items: center; + gap: 4px; + } + .controls { + flex: 0 0 auto; + display: flex; + align-items: center; + justify-content: end; + gap: 8px; + } + } + + .inventory-item-content { + > *:not(:last-child) { + margin-bottom: 5px; + } + &.extensible { + max-height: 0; + overflow: hidden; + transition: max-height 0.5s ease-in-out; + &.extended { + max-height: 500px; + overflow: auto; + } + } + .item-resources { + display: flex; + align-items: center; + gap: 4px; + .item-dice-resource { + position: relative; + display: flex; + align-items: center; + justify-content: center; + width: 26px; + + label, + i { + position: absolute; + z-index: 2; + cursor: pointer; + } + + label { + color: light-dark(white, black); + filter: drop-shadow(0 0 1px light-dark(@dark-blue, @golden)); + font-size: 18px; + } + + img { + filter: brightness(0) saturate(100%) invert(97%) sepia(7%) saturate(580%) hue-rotate(332deg) + brightness(96%) contrast(95%); + } + + i { + text-shadow: 0 0 3px white; + filter: drop-shadow(0 1px white); + color: black; + font-size: 26px; + } } } } } + .card-item { position: relative; height: 120px; @@ -212,6 +280,7 @@ input { flex: 1; + text-align: center; } .item-dice-resource { diff --git a/system.json b/system.json index 39b1c37f..bfa25df3 100644 --- a/system.json +++ b/system.json @@ -35,6 +35,12 @@ }, { "name": "Po0lp" + }, + { + "name": "JoaquinP", + "url": "https://github.com/joaquinpereyra98", + "email": "joaquinpereyra98@gmail.com", + "discord": "joaquinp98" } ], "esmodules": ["build/daggerheart.js"], @@ -178,14 +184,14 @@ "documentTypes": { "Actor": { "character": { - "htmlFields": ["story", "description", "scars.*.description"] + "htmlFields": ["biography.background", "biography.connections"] }, "companion": {}, "adversary": { - "htmlFields": ["description", "motivesAndTactics"] + "htmlFields": ["notes", "description"] }, "environment": { - "htmlFields": ["description", "impulses"] + "htmlFields": ["notes", "description"] } }, "Item": { diff --git a/templates/sheets-settings/adversary-settings/features.hbs b/templates/sheets-settings/adversary-settings/features.hbs index f232dae9..a287c2a8 100644 --- a/templates/sheets-settings/adversary-settings/features.hbs +++ b/templates/sheets-settings/adversary-settings/features.hbs @@ -16,8 +16,8 @@ {{feature.name}}
- - + +
{{/each}} diff --git a/templates/sheets-settings/environment-settings/adversaries.hbs b/templates/sheets-settings/environment-settings/adversaries.hbs index 4d8fa066..4a9ef08d 100644 --- a/templates/sheets-settings/environment-settings/adversaries.hbs +++ b/templates/sheets-settings/environment-settings/adversaries.hbs @@ -1,30 +1,33 @@ -
+
- {{#each document.system.potentialAdversaries as |category id|}} -
- {{category.label}} -
- - - - + {{#each document.system.potentialAdversaries as |category categoryId|}} +
+ {{category.label}} +
+ + + + +
+
+ {{#each category.adversaries as |adversary|}} +
+ {{> 'daggerheart.inventory-item' + item=adversary + type='adversary' + isActor=true + categoryAdversary=categoryId + }}
-
- {{#each category.adversaries as |adversary|}} -
- {{> 'systems/daggerheart/templates/sheets/global/partials/inventory-item.hbs' item=adversary type='adversary' isActor=true categoryAdversary=@../key}} -
- {{/each}} -
-
- Drop Actors here -
-
+ {{/each}} +
+
+ Drop Actors here +
+
{{/each}}
\ No newline at end of file diff --git a/templates/sheets-settings/environment-settings/features.hbs b/templates/sheets-settings/environment-settings/features.hbs index f232dae9..aab68309 100644 --- a/templates/sheets-settings/environment-settings/features.hbs +++ b/templates/sheets-settings/environment-settings/features.hbs @@ -16,8 +16,8 @@ {{feature.name}}
- - + +
{{/each}} diff --git a/templates/sheets/actors/adversary/effects.hbs b/templates/sheets/actors/adversary/effects.hbs index 3d378802..325610e6 100644 --- a/templates/sheets/actors/adversary/effects.hbs +++ b/templates/sheets/actors/adversary/effects.hbs @@ -1,8 +1,20 @@ -
- {{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize 'DAGGERHEART.GENERAL.activeEffects') type='effect'}} - {{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize 'DAGGERHEART.GENERAL.inactiveEffects') type='effect'}} +
+ {{> 'daggerheart.inventory-items' + title='DAGGERHEART.GENERAL.activeEffects' + type='effect' + isGlassy=true + collection=effects.actives + canCreate=true + hideResources=true + }} + + {{> 'daggerheart.inventory-items' + title='DAGGERHEART.GENERAL.inactiveEffects' + type='effect' + isGlassy=true + collection=effects.inactives + canCreate=true + hideResources=true + }}
\ No newline at end of file diff --git a/templates/sheets/actors/adversary/features.hbs b/templates/sheets/actors/adversary/features.hbs index 1a8d918e..e5a304ef 100644 --- a/templates/sheets/actors/adversary/features.hbs +++ b/templates/sheets/actors/adversary/features.hbs @@ -1,9 +1,13 @@ -
+
- {{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize tabs.features.label) type='feature' values=document.system.features hideControls=true}} + {{> 'daggerheart.inventory-items' + title=tabs.features.label + type='feature' + collection=document.system.features + hideControls=true + canCreate=true + showActions=true + }}
\ No newline at end of file diff --git a/templates/sheets/actors/adversary/header.hbs b/templates/sheets/actors/adversary/header.hbs index 28c0d002..fd6389aa 100644 --- a/templates/sheets/actors/adversary/header.hbs +++ b/templates/sheets/actors/adversary/header.hbs @@ -27,7 +27,7 @@
- {{{source.system.description}}} + {{{description}}}
{{localize 'DAGGERHEART.ACTORS.Adversary.FIELDS.motivesAndTactics.label'}}: {{{source.system.motivesAndTactics}}} diff --git a/templates/sheets/actors/adversary/notes.hbs b/templates/sheets/actors/adversary/notes.hbs index effa7240..a2378516 100644 --- a/templates/sheets/actors/adversary/notes.hbs +++ b/templates/sheets/actors/adversary/notes.hbs @@ -5,6 +5,6 @@ >
{{localize tabs.notes.label}} - {{formInput systemFields.notes value=document.system.notes enriched=document.system.notes localize=true toggled=true}} + {{formInput notes.field value=notes.value enriched=notes.enriched toggled=true}}
\ No newline at end of file diff --git a/templates/sheets/actors/adversary/sidebar.hbs b/templates/sheets/actors/adversary/sidebar.hbs index 5384504e..8eb4dcb0 100644 --- a/templates/sheets/actors/adversary/sidebar.hbs +++ b/templates/sheets/actors/adversary/sidebar.hbs @@ -1,111 +1,118 @@ \ No newline at end of file diff --git a/templates/sheets/actors/character/biography.hbs b/templates/sheets/actors/character/biography.hbs index 34313def..6913f279 100644 --- a/templates/sheets/actors/character/biography.hbs +++ b/templates/sheets/actors/character/biography.hbs @@ -26,11 +26,11 @@
{{localize 'DAGGERHEART.ACTORS.Character.story.backgroundTitle'}} - {{formInput systemFields.biography.fields.background value=source.system.biography.background enriched=source.system.biography.background localize=true toggled=true}} + {{formInput background.field value=background.value enriched=background.enriched toggled=true}}
{{localize 'DAGGERHEART.ACTORS.Character.story.connectionsTitle'}} - {{formInput systemFields.biography.fields.connections value=source.system.biography.connections enriched=source.system.biography.connections localize=true toggled=true}} + {{formInput connections.field value=connections.value enriched=connections.enriched toggled=true}}
\ No newline at end of file diff --git a/templates/sheets/actors/character/effects.hbs b/templates/sheets/actors/character/effects.hbs index 3d378802..4c4fca27 100644 --- a/templates/sheets/actors/character/effects.hbs +++ b/templates/sheets/actors/character/effects.hbs @@ -1,8 +1,21 @@ -
- {{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize 'DAGGERHEART.GENERAL.activeEffects') type='effect'}} - {{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize 'DAGGERHEART.GENERAL.inactiveEffects') type='effect'}} +
+ + {{> 'daggerheart.inventory-items' + title='DAGGERHEART.GENERAL.activeEffects' + type='effect' + isGlassy=true + collection=effects.actives + canCreate=true + hideResources=true + }} + + {{> 'daggerheart.inventory-items' + title='DAGGERHEART.GENERAL.inactiveEffects' + type='effect' + isGlassy=true + collection=effects.inactives + canCreate=true + hideResources=true + }}
\ No newline at end of file diff --git a/templates/sheets/actors/character/features.hbs b/templates/sheets/actors/character/features.hbs index 7fab5263..acabd37e 100644 --- a/templates/sheets/actors/character/features.hbs +++ b/templates/sheets/actors/character/features.hbs @@ -1,13 +1,25 @@ -
+
- {{#each document.system.sheetLists}} - {{#if this.values}} - {{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=this.title values=this.values}} - {{/if}} + {{#each document.system.sheetLists as |category|}} + {{#if (eq category.type 'feature' )}} + {{> 'daggerheart.inventory-items' + title=category.title + type='feature' + collection=category.values + canCreate=true + showActions=true + }} + {{else if category.values}} + {{> 'daggerheart.inventory-items' + title=category.title + type='feature' + collection=category.values + canCreate=false + showActions=true + }} + + {{/if}} {{/each}}
\ No newline at end of file diff --git a/templates/sheets/actors/character/header.hbs b/templates/sheets/actors/character/header.hbs index 18e791d1..9e930fd6 100644 --- a/templates/sheets/actors/character/header.hbs +++ b/templates/sheets/actors/character/header.hbs @@ -29,25 +29,25 @@
{{#if document.system.class.value}} - {{document.system.class.value.name}} + {{document.system.class.value.name}} {{else}} {{localize 'TYPES.Item.class'}} {{/if}} {{#if document.system.class.subclass}} - {{document.system.class.subclass.name}} + {{document.system.class.subclass.name}} {{else}} {{localize 'TYPES.Item.subclass'}} {{/if}} {{#if document.system.community}} - {{document.system.community.name}} + {{document.system.community.name}} {{else}} {{localize 'TYPES.Item.community'}} {{/if}} {{#if document.system.ancestry}} - {{document.system.ancestry.name}} + {{document.system.ancestry.name}} {{else}} {{localize 'TYPES.Item.ancestry'}} {{/if}} @@ -56,13 +56,13 @@ {{#if document.system.multiclass.value}}
{{#if document.system.multiclass.value}} - {{document.system.multiclass.value.name}} + {{document.system.multiclass.value.name}} {{else}} {{localize 'DAGGERHEART.GENERAL.multiclass'}} {{/if}} {{#if document.system.multiclass.subclass}} - {{document.system.multiclass.subclass.name}} + {{document.system.multiclass.subclass.name}} {{else}} {{localize 'TYPES.Item.subclass'}} {{/if}} diff --git a/templates/sheets/actors/character/inventory.hbs b/templates/sheets/actors/character/inventory.hbs index be8bb251..9610d8e2 100644 --- a/templates/sheets/actors/character/inventory.hbs +++ b/templates/sheets/actors/character/inventory.hbs @@ -1,8 +1,5 @@ -
+
\ No newline at end of file diff --git a/templates/sheets/actors/character/loadout.hbs b/templates/sheets/actors/character/loadout.hbs index 5a1d675e..0319d56f 100644 --- a/templates/sheets/actors/character/loadout.hbs +++ b/templates/sheets/actors/character/loadout.hbs @@ -10,28 +10,33 @@ -
- {{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' - title=(localize 'DAGGERHEART.GENERAL.Tabs.loadout') + {{> 'daggerheart.inventory-items' + title='DAGGERHEART.GENERAL.Tabs.loadout' type='domainCard' isGlassy=true - cardView=(ifThen listView "list" "card")}} - - {{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' - title=(localize 'DAGGERHEART.GENERAL.Tabs.vault') + cardView=cardView + collection=document.system.domainCards.loadout + canCreate=true + }} + {{> 'daggerheart.inventory-items' + title='DAGGERHEART.GENERAL.Tabs.vault' type='domainCard' - isVault=true isGlassy=true - cardView=(ifThen listView "list" "card")}} + cardView=cardView + collection=document.system.domainCards.vault + canCreate=true + inVault=true + }}
\ No newline at end of file diff --git a/templates/sheets/actors/character/sidebar.hbs b/templates/sheets/actors/character/sidebar.hbs index 51816443..553ba246 100644 --- a/templates/sheets/actors/character/sidebar.hbs +++ b/templates/sheets/actors/character/sidebar.hbs @@ -1,14 +1,16 @@
\ No newline at end of file diff --git a/templates/sheets/actors/companion/effects.hbs b/templates/sheets/actors/companion/effects.hbs index 3d378802..325610e6 100644 --- a/templates/sheets/actors/companion/effects.hbs +++ b/templates/sheets/actors/companion/effects.hbs @@ -1,8 +1,20 @@ -
- {{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize 'DAGGERHEART.GENERAL.activeEffects') type='effect'}} - {{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize 'DAGGERHEART.GENERAL.inactiveEffects') type='effect'}} +
+ {{> 'daggerheart.inventory-items' + title='DAGGERHEART.GENERAL.activeEffects' + type='effect' + isGlassy=true + collection=effects.actives + canCreate=true + hideResources=true + }} + + {{> 'daggerheart.inventory-items' + title='DAGGERHEART.GENERAL.inactiveEffects' + type='effect' + isGlassy=true + collection=effects.inactives + canCreate=true + hideResources=true + }}
\ No newline at end of file diff --git a/templates/sheets/actors/environment/features.hbs b/templates/sheets/actors/environment/features.hbs index 6697e42c..d7f10d56 100644 --- a/templates/sheets/actors/environment/features.hbs +++ b/templates/sheets/actors/environment/features.hbs @@ -4,6 +4,13 @@ data-group='{{tabs.features.group}}' >
- {{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize tabs.features.label) type='feature' values=document.system.features hideControls=true }} + {{> 'daggerheart.inventory-items' + title=tabs.features.label + type='feature' + collection=document.system.features + hideControls=true + canCreate=true + showActions=true + }}
\ No newline at end of file diff --git a/templates/sheets/actors/environment/header.hbs b/templates/sheets/actors/environment/header.hbs index e9921bae..fff55144 100644 --- a/templates/sheets/actors/environment/header.hbs +++ b/templates/sheets/actors/environment/header.hbs @@ -34,7 +34,7 @@
- {{{source.system.description}}} + {{{description}}}
{{localize 'DAGGERHEART.ACTORS.Environment.FIELDS.impulses.label'}}: {{{source.system.impulses}}} diff --git a/templates/sheets/actors/environment/notes.hbs b/templates/sheets/actors/environment/notes.hbs index effa7240..663a484a 100644 --- a/templates/sheets/actors/environment/notes.hbs +++ b/templates/sheets/actors/environment/notes.hbs @@ -5,6 +5,6 @@ >
{{localize tabs.notes.label}} - {{formInput systemFields.notes value=document.system.notes enriched=document.system.notes localize=true toggled=true}} + {{formInput notes.field value=notes.value enriched=notes.value toggled=true}}
\ No newline at end of file diff --git a/templates/sheets/actors/environment/potentialAdversaries.hbs b/templates/sheets/actors/environment/potentialAdversaries.hbs index f39a1adf..cc246312 100644 --- a/templates/sheets/actors/environment/potentialAdversaries.hbs +++ b/templates/sheets/actors/environment/potentialAdversaries.hbs @@ -4,8 +4,17 @@ data-group='{{tabs.potentialAdversaries.group}}' >
- {{#each document.system.potentialAdversaries}} - {{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=this.label type='adversary' isGlassy=true adversaries=this.adversaries}} + {{#each document.system.potentialAdversaries as |category categoryId|}} + {{> 'daggerheart.inventory-items' + title=category.label + type='adversary' + isGlassy=true + isActor=true + categoryAdversary=categoryId + hideControls=true + collection=category.adversaries + hideResources=true + }} {{/each}}
\ No newline at end of file diff --git a/templates/sheets/global/partials/domain-card-item.hbs b/templates/sheets/global/partials/domain-card-item.hbs index 0ff7e8f5..37d6a0e2 100644 --- a/templates/sheets/global/partials/domain-card-item.hbs +++ b/templates/sheets/global/partials/domain-card-item.hbs @@ -1,34 +1,22 @@ -
  • +
  • -
  • +
  • {{feature.name}}

    {{#unless hideContrals}} - + {{/unless}}
  • \ No newline at end of file diff --git a/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs b/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs new file mode 100644 index 00000000..aa58aad8 --- /dev/null +++ b/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs @@ -0,0 +1,70 @@ +{{!-- +Inventory/Domain Card Section + +{{> 'daggerheart.inventory-items' }} + +Parameters: +- title {string} : Localization key used for the legend. +- collection {array} : Array of items to render. +- type {string} : The type of items in the list: +- isGlassy {boolean} : If true, applies the 'glassy' class to the fieldset. +- cardView {boolean} : If true and type is 'domainCard', renders using domain card layout. +- isActor {boolean} : Passed through to inventory-item partials. +- canCreate {boolean} : If true, show createDoc anchor on legend +- inVault {boolean} : If true, the domainCard is created with inVault=true +- disabled {boolean}: If true, the ActiveEffect is created with disabled=true; +- categoryAdversary {string} : Category adversary id. +- showLabels {boolean} : If true, show label-tags else show simple tags. +- hideTooltip {boolean} : If true, disables the tooltip on the item image. +- hideControls {boolean} : If true, hides the controls inside inventory-item partials. +- hideDescription {boolean} : If true, hides the item's description. +- hideResources {boolean} : If true, hides the item's resources. +- showActions {boolean} : If true show feature's actions. +--}} + +
    + + {{localize title}} + {{#if canCreate}} + + + + {{/if }} + + {{#if (and cardView (eq type 'domainCard'))}} +
      + {{#each collection as |item|}} + + {{> 'systems/daggerheart/templates/sheets/global/partials/domain-card-item.hbs' + item=item + type='domainCard' + }} + + {{/each}} +
    + {{else}} +
      + {{#each collection as |item|}} + + {{> 'daggerheart.inventory-item' + item=item + type=../type + hideControls=../hideControls + isActor=../isActor + categoryAdversary=../categoryAdversary + hideTooltip=../hideTooltip + showLabels=../showLabels + isAction=../isAction + hideResources=../hideResources + showActions=../showActions + }} + + {{/each}} +
    + {{/if}} +
    \ No newline at end of file diff --git a/templates/sheets/global/partials/inventory-fieldset-items.hbs b/templates/sheets/global/partials/inventory-fieldset-items.hbs deleted file mode 100644 index 65c52736..00000000 --- a/templates/sheets/global/partials/inventory-fieldset-items.hbs +++ /dev/null @@ -1,44 +0,0 @@ -
    - {{title}} -
      - {{#unless (eq cardView 'card') }} - {{#if (or (eq type 'domainCard') (eq type 'armor') (eq type 'consumable') (eq type 'miscellaneous') (eq type 'weapon'))}} - {{#each document.items as |item|}} - {{#if (eq item.type ../type)}} - {{#unless (and (eq ../type 'domainCard') (or (and item.system.inVault (not ../isVault)) (and (not item.system.inVault) ../isVault)))}} - {{> 'systems/daggerheart/templates/sheets/global/partials/inventory-item.hbs' item=item type=../type}} - {{/unless}} - {{/if}} - {{/each}} - {{else}} - {{#if (eq type 'effect')}} - {{#each document.effects as |effect|}} - {{> 'systems/daggerheart/templates/sheets/global/partials/inventory-item.hbs' item=effect type=../type}} - {{/each}} - {{else}} - {{#each values}} - {{> 'systems/daggerheart/templates/sheets/global/partials/inventory-item.hbs' item=this type=../type hideControls=../hideControls featureType=true }} - {{/each}} - - {{#each adversaries as |adversary|}} - {{> 'systems/daggerheart/templates/sheets/global/partials/inventory-item.hbs' item=adversary type='adversary' hideControls=true isActor=true categoryAdversary=@../key}} - {{/each}} - {{/if}} - {{/if}} - {{/unless}} - -
    - {{#if (and (eq cardView 'card') (eq type 'domainCard'))}} -
      - {{#if isVault}} - {{#each document.system.domainCards.vault as |card|}} - {{> 'systems/daggerheart/templates/sheets/global/partials/domain-card-item.hbs' item=card type=../type}} - {{/each}} - {{else}} - {{#each document.system.domainCards.loadout as |card|}} - {{> 'systems/daggerheart/templates/sheets/global/partials/domain-card-item.hbs' item=card type=../type}} - {{/each}} - {{/if}} -
    - {{/if}} -
    \ No newline at end of file diff --git a/templates/sheets/global/partials/inventory-item-V2.hbs b/templates/sheets/global/partials/inventory-item-V2.hbs new file mode 100644 index 00000000..5329e7b5 --- /dev/null +++ b/templates/sheets/global/partials/inventory-item-V2.hbs @@ -0,0 +1,239 @@ +{{!-- +{{> 'daggerheart.inventory-item' }} + +Parameters: +- type {string} : The type of items in the list +- isActor {boolean} : Passed through to inventory-item partials. +- categoryAdversary {string} : Category adversary id. +- noExtensible {boolean} : If true, the inventory-item-content would be collapsable/extendible else it always be showed +- hideLabels {boolean} : If true, hide label-tags else show label-tags. +- hideTags {boolean} : If true, hide simple-tags else show simple-tags. +- hideTooltip {boolean} : If true, disables the tooltip on the item image. +- hideControls {boolean} : If true, hides the controls inside inventory-item partials. +- hideDescription {boolean} : If true, hides the item's description. +- hideResources {boolean} : If true, hides the item's resources. +- showActions {boolean} : If true show feature's actions. +--}} + +
  • +
    + {{!-- Image --}} +
    + + d20 +
    + + {{!-- Name & Tags --}} +
    + + {{!-- Item Name --}} +
    {{item.name}}
    + + {{!-- Weapon Block Start --}} + {{#if (eq type 'weapon')}} + {{#if (not hideTags)}} +
    +
    + {{localize (concat 'DAGGERHEART.CONFIG.Traits.' item.system.attack.roll.trait '.name')}} +
    +
    + {{localize (concat 'DAGGERHEART.CONFIG.Range.' item.system.attack.range '.name')}} +
    +
    + {{item.system.attack.damage.parts.0.value.dice}} + {{#if item.system.attack.damage.parts.0.value.bonus}} + + {{item.system.attack.damage.parts.0.value.bonus}}{{/if}} + ( + {{#each item.system.attack.damage.parts.0.type as |type|}} + + {{localize (concat 'DAGGERHEART.CONFIG.DamageType.' type '.abbreviation')}} + {{#unless @last}}|{{/unless}} + {{/each}} + ) + +
    +
    + {{localize (concat 'DAGGERHEART.CONFIG.Burden.' item.system.burden)}} +
    +
    + {{else if (not hideLabels)}} +
    +
    + {{localize (concat 'DAGGERHEART.CONFIG.Traits.' item.system.attack.roll.trait '.short')}} + {{localize (concat 'DAGGERHEART.CONFIG.Range.' item.system.attack.range '.short')}} + - + {{item.system.attack.damage.parts.0.value.dice}} + {{#if item.system.attack.damage.parts.0.value.bonus}} + + {{item.system.attack.damage.parts.0.value.bonus}} + {{/if}} + {{#with (lookup @root.config.GENERAL.damageTypes item.system.attack.damage.parts.0.type)}} + {{#each icon}}{{/each}} + {{/with}} +
    +
    + {{/if}} + {{/if}} + {{!-- Weapon Block End --}} + + {{!-- Armor Block Start --}} + {{#if (eq type 'armor')}} + {{#if (not hideTags)}} +
    +
    {{localize "DAGGERHEART.ITEMS.Armor.baseScore"}}: {{item.system.baseScore}}
    +
    + {{localize "DAGGERHEART.ITEMS.Armor.baseThresholds.base"}}: + {{item.system.baseThresholds.major}} / {{item.system.baseThresholds.severe}} +
    +
    + {{else if (not hideLabels)}} +
    +
    + {{localize "DAGGERHEART.ITEMS.Armor.baseScore"}}: {{item.system.baseScore}} +
    +
    + {{/if}} + {{/if}} + {{!-- Armor Block End --}} + + {{!-- Domain Card Block Start --}} + {{#if (eq type 'domainCard')}} + {{#if (not hideTags)}} +
    +
    {{localize (concat 'DAGGERHEART.CONFIG.DomainCardTypes.' item.system.type)}}
    +
    {{localize (concat 'DAGGERHEART.GENERAL.Domain.' item.system.domain '.label')}}
    +
    + {{localize "DAGGERHEART.ITEMS.DomainCard.recallCost"}}: + {{item.system.recallCost}} +
    +
    + {{else if (not hideLabels)}} +
    +
    + {{localize (concat 'DAGGERHEART.CONFIG.DomainCardTypes.' item.system.type)}} - + {{localize (concat 'DAGGERHEART.GENERAL.Domain.' item.system.domain '.label')}} - + {{item.system.recallCost}} + +
    +
    + {{/if}} + {{/if}} + {{!-- Domain Card Block End --}} + + {{!-- Effect Block Start --}} + {{#if (eq type 'effect')}} + {{#if (not hideTags)}} +
    +
    + {{localize item.parent.system.metadata.label}}: {{item.parent.name}} +
    +
    + {{#if item.duration.duration}} + {{localize 'DAGGERHEART.EFFECTS.Duration.temporary'}} + {{else}} + {{localize 'DAGGERHEART.EFFECTS.Duration.passive'}} + {{/if}} +
    + {{#each item.statuses as |status|}} +
    {{localize (concat 'DAGGERHEART.CONFIG.Condition.' status '.name')}}
    + {{/each}} +
    + {{else if (not hideLabels)}} + {{!-- Empty --}} + {{/if}} + {{/if}} + {{!-- Effect Block End --}} + + {{!-- Action Block Start --}} + {{#if (eq type 'action')}} + {{#if (not hideTags)}} +
    +
    {{localize (concat 'DAGGERHEART.ACTIONS.TYPES.' item.type '.name')}}
    +
    {{localize (concat 'DAGGERHEART.CONFIG.ActionType.' item.actionType)}}
    +
    + {{else if (not hideLabels)}} + {{!-- Empty --}} + {{/if}} + {{/if}} + {{!-- Action Block End --}} +
    + + {{!-- Simple Resource --}} + {{#if (and (not hideResources) (eq item.system.resource.type 'simple'))}} + {{> "systems/daggerheart/templates/sheets/global/partials/item-resource.hbs"}} + {{/if}} + {{#if (and (not hideResources) item.system.quantity)}} +
    + +
    + {{/if}} + + {{!-- Controls --}} + {{#unless hideControls}} +
    + {{#if isActor}} + + + + {{#if (eq type 'adversary')}} + + + + {{/if}} + {{else}} + {{#if (eq type 'weapon')}} + + + + {{else if (eq type 'armor')}} + + + + {{else if (eq type 'domainCard')}} + + + + {{else if (eq type 'effect')}} + + + + {{/if}} + {{#if (hasProperty item "toChat")}} + + + + {{/if}} + + + + {{/if}} +
    + {{/unless}} +
    +
    + {{!-- Description --}} + {{#unless hideDescription}} +
    + {{/unless}} + {{!-- Dice Resource --}} + {{#if (and (not hideResources) (eq item.system.resource.type 'diceValue'))}} + {{> "systems/daggerheart/templates/sheets/global/partials/item-resource.hbs"}} + {{/if}} + {{!-- Actions Buttons --}} + {{#if (and showActions (eq item.type 'feature'))}} +
    + {{#each item.system.actions as | action |}} + + {{/each}} +
    + {{/if}} +
    +
  • \ No newline at end of file diff --git a/templates/sheets/global/partials/inventory-item.hbs b/templates/sheets/global/partials/inventory-item.hbs deleted file mode 100644 index a6373c08..00000000 --- a/templates/sheets/global/partials/inventory-item.hbs +++ /dev/null @@ -1,197 +0,0 @@ -
  • - -
    -
    - {{#if isCompanion}} - {{item.name}} - {{else}} -
    {{item.name}}
    - {{/if}} - {{#if (eq type 'weapon')}} -
    - {{#if isSidebar}} -
    -
    - {{localize (concat 'DAGGERHEART.CONFIG.Traits.' item.system.attack.roll.trait '.short')}} - {{localize (concat 'DAGGERHEART.CONFIG.Range.' item.system.attack.range '.short')}} - - - {{item.system.attack.damage.parts.0.value.dice}}{{#if item.system.attack.damage.parts.0.value.bonus}} + {{item.system.attack.damage.parts.0.value.bonus}}{{/if}} - {{#each item.system.attack.damage.parts.0.type as | type | }} - {{#with (lookup @root.config.GENERAL.damageTypes type)}} - - {{/with}} - {{/each}} -
    -
    - {{else}} -
    - {{localize (concat 'DAGGERHEART.CONFIG.Traits.' item.system.attack.roll.trait '.name')}} -
    -
    - {{localize (concat 'DAGGERHEART.CONFIG.Range.' item.system.attack.range '.name')}} -
    -
    - {{item.system.attack.damage.parts.0.value.dice}}{{#if item.system.attack.damage.parts.0.value.bonus}} + {{item.system.attack.damage.parts.0.value.bonus}}{{/if}} - ( - {{#each item.system.attack.damage.parts.0.type}} - {{localize (concat 'DAGGERHEART.CONFIG.DamageType.' this '.abbreviation')}} - {{/each}} - ) -
    -
    - {{localize (concat 'DAGGERHEART.CONFIG.Burden.' item.system.burden)}} -
    - {{/if}} -
    - {{/if}} - {{#if (eq type 'armor')}} - {{#if isSidebar}} -
    -
    - {{localize "DAGGERHEART.ITEMS.Armor.baseScore"}}: - {{item.system.baseScore}} -
    -
    - {{else}} -
    -
    - {{localize "DAGGERHEART.ITEMS.Armor.baseScore"}}: - {{item.system.baseScore}} -
    -
    - {{localize "DAGGERHEART.ITEMS.Armor.baseThresholds.base"}}: - {{item.system.baseThresholds.major}} - / - {{item.system.baseThresholds.severe}} -
    -
    - {{/if}} - {{/if}} - {{#if (eq type 'domainCard')}} - {{#if isSidebar}} -
    -
    - {{localize (concat 'DAGGERHEART.CONFIG.DomainCardTypes.' item.system.type)}} - - - {{localize (concat 'DAGGERHEART.GENERAL.Domain.' item.system.domain '.label')}} - - - {{item.system.recallCost}} - -
    -
    - {{else}} -
    -
    - {{localize (concat 'DAGGERHEART.CONFIG.DomainCardTypes.' item.system.type)}} -
    -
    - {{localize (concat 'DAGGERHEART.GENERAL.Domain.' item.system.domain '.label')}} -
    -
    - {{localize "DAGGERHEART.ITEMS.DomainCard.recallCost"}}: - {{item.system.recallCost}} -
    -
    - {{/if}} - {{/if}} - {{#if (eq type 'effect')}} -
    -
    - {{localize (concat 'TYPES.Item.' item.parent.type)}} - : - {{item.parent.name}} -
    -
    - {{#if item.duration.duration}} - {{localize 'DAGGERHEART.EFFECTS.Duration.temporary'}} - {{else}} - {{localize 'DAGGERHEART.EFFECTS.Duration.passive'}} - {{/if}} -
    - {{#each item.statuses as |status|}} -
    - {{localize (concat 'DAGGERHEART.CONFIG.Condition.' status '.name')}} -
    - {{/each}} -
    - {{/if}} - {{#if (eq type 'action')}} -
    -
    - {{localize (concat 'DAGGERHEART.ACTIONS.TYPES.' item.type '.name')}} -
    -
    - {{localize (concat 'DAGGERHEART.CONFIG.ActionType.' item.actionType)}} -
    -
    - {{/if}} -
    - {{#if (and (not isSidebar) (eq item.system.resource.type 'simple'))}} - {{> "systems/daggerheart/templates/sheets/global/partials/item-resource.hbs"}} - {{/if}} - {{#if (and (not isSidebar) item.system.quantity)}} -
    - -
    - {{/if}} -
    - {{#unless hideControls}} - {{#if isActor}} -
    - {{#if (eq type 'actor')}} - - - - {{/if}} - {{#if (eq type 'adversary')}} - - - - - - - {{/if}} -
    - {{else}} -
    - {{#if (eq type 'weapon')}} - - - - {{/if}} - {{#if (eq type 'armor')}} - - - - {{/if}} - {{#if (eq type 'domainCard')}} - {{#unless item.system.inVault}} - - - - {{else}} - - - - {{/unless}} - - {{/if}} - - -
    - {{/if}} - {{else}} - - {{/unless}} -
    {{#unless isSidebar}}{{{item.system.description}}}{{/unless}}
    - {{#if (and (not isSidebar) (eq item.system.resource.type 'diceValue'))}} - {{> "systems/daggerheart/templates/sheets/global/partials/item-resource.hbs"}} - {{/if}} - {{#if featureType}} -
    - {{#each item.system.actions as | action |}} - - {{/each}} -
    - {{/if}} -
  • \ No newline at end of file diff --git a/templates/sheets/global/tabs/tab-actions.hbs b/templates/sheets/global/tabs/tab-actions.hbs index ed71c45f..584cf782 100644 --- a/templates/sheets/global/tabs/tab-actions.hbs +++ b/templates/sheets/global/tabs/tab-actions.hbs @@ -3,22 +3,11 @@ data-tab='{{tabs.actions.id}}' data-group='{{tabs.actions.group}}' > -
    - {{localize "DAGGERHEART.GENERAL.Action.plural"}} -
    - {{#each document.system.actions as |action index|}} -
    - - {{action.name}} -
    - -
    -
    - {{/each}} -
    -
    + +{{> 'daggerheart.inventory-items' + title='DAGGERHEART.GENERAL.Action.plural' + collection=document.system.actions + type='action' + canCreate=true +}} \ No newline at end of file diff --git a/templates/sheets/global/tabs/tab-effects.hbs b/templates/sheets/global/tabs/tab-effects.hbs index a75f1b0b..c89c6ff4 100644 --- a/templates/sheets/global/tabs/tab-effects.hbs +++ b/templates/sheets/global/tabs/tab-effects.hbs @@ -1,26 +1,21 @@ -
    -
    - - {{localize "DAGGERHEART.GENERAL.Effect.plural"}} - - - - -
    - {{#each document.effects as |effect|}} -
    - - {{effect.name}} -
    - - -
    -
    - {{/each}} -
    -
    +
    + + {{> 'daggerheart.inventory-items' + title='DAGGERHEART.GENERAL.activeEffects' + type='effect' + isGlassy=true + collection=effects.actives + canCreate=true + hideResources=true + }} + + {{> 'daggerheart.inventory-items' + title='DAGGERHEART.GENERAL.inactiveEffects' + type='effect' + isGlassy=true + collection=effects.inactives + canCreate=true + hideResources=true + }}
    \ No newline at end of file diff --git a/templates/sheets/global/tabs/tab-feature-section.hbs b/templates/sheets/global/tabs/tab-feature-section.hbs deleted file mode 100644 index ca6af184..00000000 --- a/templates/sheets/global/tabs/tab-feature-section.hbs +++ /dev/null @@ -1,14 +0,0 @@ -
    -
    - {{localize "DAGGERHEART.GENERAL.Tabs.features"}} -
    - {{#each source.system.abilities as |feature key|}} - {{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' feature=feature}} - {{/each}} -
    -
    -
    \ No newline at end of file diff --git a/templates/sheets/global/tabs/tab-features.hbs b/templates/sheets/global/tabs/tab-features.hbs index eced83d7..b70b33d3 100644 --- a/templates/sheets/global/tabs/tab-features.hbs +++ b/templates/sheets/global/tabs/tab-features.hbs @@ -1,23 +1,11 @@ -
    -
    - {{localize "DAGGERHEART.GENERAL.features"}} -
    - {{#each document.system.features as |feature|}} -
    - - {{feature.name}} -
    - -
    -
    - {{/each}} -
    -
    +
    + {{> 'daggerheart.inventory-items' + title='DAGGERHEART.GENERAL.features' + type='feature' + isGlassy=true + collection=document.system.features + canCreate=(or document.parent isGM) + showActions=false + }}
    \ No newline at end of file diff --git a/templates/sheets/items/class/settings.hbs b/templates/sheets/items/class/settings.hbs index b110bf4c..756687b4 100644 --- a/templates/sheets/items/class/settings.hbs +++ b/templates/sheets/items/class/settings.hbs @@ -38,7 +38,7 @@ {{localize "DAGGERHEART.ITEMS.Class.guide.suggestedPrimaryWeaponTitle"}}
    {{#if document.system.characterGuide.suggestedPrimaryWeapon}} -
    +
    {{document.system.characterGuide.suggestedPrimaryWeapon.name}}
    @@ -53,7 +53,7 @@ {{localize "DAGGERHEART.ITEMS.Class.guide.suggestedSecondaryWeaponTitle"}}
    {{#if document.system.characterGuide.suggestedSecondaryWeapon}} -
    +
    {{document.system.characterGuide.suggestedSecondaryWeapon.name}}
    @@ -68,7 +68,7 @@ {{localize "DAGGERHEART.ITEMS.Class.guide.suggestedArmorTitle"}}
    {{#if document.system.characterGuide.suggestedArmor}} -
    +
    {{document.system.characterGuide.suggestedArmor.name}}
    @@ -86,7 +86,7 @@ {{localize "DAGGERHEART.GENERAL.take"}}
    {{#each source.system.inventory.take}} -
    +
    {{this.name}}
    @@ -101,7 +101,7 @@ {{localize "DAGGERHEART.ITEMS.Class.guide.inventory.thenChoose"}}
    {{#each source.system.inventory.choiceA}} -
    +
    {{this.name}}
    @@ -116,7 +116,7 @@ {{localize "DAGGERHEART.ITEMS.Class.guide.inventory.andEither"}}
    {{#each source.system.inventory.choiceB}} -
    +
    {{this.name}}