From a27ee1578ed38981f4f137a84f30521ad3a59f31 Mon Sep 17 00:00:00 2001 From: joaquinpereyra98 <24190917+joaquinpereyra98@users.noreply.github.com> Date: Thu, 31 Jul 2025 23:33:26 -0300 Subject: [PATCH] Feature: add methods for generate tags and labels for documents and actions (#499) * FEAT: getTags and getLabels for weapons items * FEAT: add _gettags and _getLabels to armor, domainCard, weapons and ActiveEffect * define tags for actions --------- Co-authored-by: Joaquin Pereyra --- module/data/action/baseAction.mjs | 13 ++ module/data/item/armor.mjs | 22 ++ module/data/item/domainCard.mjs | 33 ++- module/data/item/weapon.mjs | 60 ++++++ module/documents/activeEffect.mjs | 48 ++++- module/documents/item.mjs | 34 +++- module/helpers/utils.mjs | 2 +- .../global/partials/inventory-item-V2.hbs | 190 ++++-------------- 8 files changed, 237 insertions(+), 165 deletions(-) diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index 5114fa61..8376447a 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -345,4 +345,17 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel }); } } + + /** + * Generates a list of localized tags for this action. + * @returns {string[]} An array of localized tag strings. + */ + _getTags() { + const tags = [ + game.i18n.localize(`DAGGERHEART.ACTIONS.TYPES.${this.type}.name`), + game.i18n.localize(`DAGGERHEART.CONFIG.ActionType.${this.actionType}`) + ]; + + return tags; + } } diff --git a/module/data/item/armor.mjs b/module/data/item/armor.mjs index 54598a3f..e8a6e35b 100644 --- a/module/data/item/armor.mjs +++ b/module/data/item/armor.mjs @@ -117,4 +117,26 @@ export default class DHArmor extends AttachableItem { } } } + + /** + * Generates a list of localized tags based on this item's type-specific properties. + * @returns {string[]} An array of localized tag strings. + */ + _getTags() { + const tags = [ + `${game.i18n.localize('DAGGERHEART.ITEMS.Armor.baseScore')}: ${this.baseScore}`, + `${game.i18n.localize('DAGGERHEART.ITEMS.Armor.baseThresholds.base')}: ${this.baseThresholds.major} / ${this.baseThresholds.severe}` + ]; + + return tags; + } + + /** + * Generate a localized label array for this item subtype. + * @returns {(string | { value: string, icons: string[] })[]} An array of localized strings and damage label objects. + */ + _getLabels() { + const labels = [`${game.i18n.localize('DAGGERHEART.ITEMS.Armor.baseScore')}: ${this.baseScore}`]; + return labels; + } } diff --git a/module/data/item/domainCard.mjs b/module/data/item/domainCard.mjs index d366b7a0..1dd89023 100644 --- a/module/data/item/domainCard.mjs +++ b/module/data/item/domainCard.mjs @@ -1,5 +1,4 @@ import BaseDataItem from './base.mjs'; -import { ActionField } from '../fields/actionField.mjs'; export default class DHDomainCard extends BaseDataItem { /** @inheritDoc */ @@ -34,6 +33,7 @@ export default class DHDomainCard extends BaseDataItem { }; } + /**@inheritdoc */ async _preCreate(data, options, user) { const allowed = await super._preCreate(data, options, user); if (allowed === false) return; @@ -55,4 +55,35 @@ export default class DHDomainCard extends BaseDataItem { } } } + + /** + * Generates a list of localized tags based on this item's type-specific properties. + * @returns {string[]} An array of localized tag strings. + */ + _getTags() { + const tags = [ + game.i18n.localize(`DAGGERHEART.CONFIG.DomainCardTypes.${this.type}`), + game.i18n.localize(`DAGGERHEART.GENERAL.Domain.${this.domain}.label`), + `${game.i18n.localize('DAGGERHEART.ITEMS.DomainCard.recallCost')}: ${this.recallCost}` + ]; + + return tags; + } + + /** + * Generate a localized label array for this item subtype. + * @returns {(string | { value: string, icons: string[] })[]} An array of localized strings and damage label objects. + */ + _getLabels() { + const labels = [ + game.i18n.localize(`DAGGERHEART.CONFIG.DomainCardTypes.${this.type}`), + game.i18n.localize(`DAGGERHEART.GENERAL.Domain.${this.domain}.label`), + { + value: `${this.recallCost}`, //converts the number to a string + icons: ['fa-bolt'] + } + ]; + + return labels; + } } diff --git a/module/data/item/weapon.mjs b/module/data/item/weapon.mjs index b7b70312..aab5a895 100644 --- a/module/data/item/weapon.mjs +++ b/module/data/item/weapon.mjs @@ -167,4 +167,64 @@ export default class DHWeapon extends AttachableItem { } } } + + /** + * Generates a list of localized tags based on this item's type-specific properties. + * @returns {string[]} An array of localized tag strings. + */ + _getTags() { + const { attack, burden } = this; + const tags = [ + game.i18n.localize(`DAGGERHEART.CONFIG.Traits.${attack.roll.trait}.name`), + game.i18n.localize(`DAGGERHEART.CONFIG.Range.${attack.range}.name`), + game.i18n.localize(`DAGGERHEART.CONFIG.Burden.${burden}`) + ]; + + for (const { value, type } of attack.damage.parts) { + const parts = [value.dice]; + if (value.bonus) parts.push(value.bonus.signedString()); + + if (type.size > 0) { + const typeTags = Array.from(type) + .map(t => game.i18n.localize(`DAGGERHEART.CONFIG.DamageType.${t}.abbreviation`)) + .join(' | '); + parts.push(` (${typeTags})`); // Add a space in front and put it inside a (). + } + + tags.push(parts.join('')); + } + + return tags; + } + + /** + * Generate a localized label array for this item subtype. + * @returns {(string | { value: string, icons: string[] })[]} An array of localized strings and damage label objects. + */ + _getLabels() { + const { roll, range, damage } = this.attack; + + const labels = [ + game.i18n.localize(`DAGGERHEART.CONFIG.Traits.${roll.trait}.short`), + game.i18n.localize(`DAGGERHEART.CONFIG.Range.${range}.short`) + ]; + + for (const { value, type } of damage.parts) { + const str = [value.dice]; + if (value.bonus) str.push(value.bonus.signedString()); + + const icons = Array.from(type) + .map(t => CONFIG.DH.GENERAL.damageTypes[t]?.icon) + .filter(Boolean); + + const labelValue = str.join(''); + if (icons.length === 0) { + labels.push(labelValue); + } else { + labels.push({ value: labelValue, icons }); + } + } + + return labels; + } } diff --git a/module/documents/activeEffect.mjs b/module/documents/activeEffect.mjs index 5d45e2e1..07dacb2f 100644 --- a/module/documents/activeEffect.mjs +++ b/module/documents/activeEffect.mjs @@ -1,6 +1,12 @@ import { itemAbleRollParse } from '../helpers/utils.mjs'; -export default class DhActiveEffect extends ActiveEffect { +export default class DhActiveEffect extends foundry.documents.ActiveEffect { + + /* -------------------------------------------- */ + /* Properties */ + /* -------------------------------------------- */ + + /**@override */ get isSuppressed() { // If this is a copied effect from an attachment, never suppress it // (These effects have attachmentSource metadata) @@ -41,14 +47,11 @@ export default class DhActiveEffect extends ActiveEffect { }); } - get localizedStatuses() { - const statusMap = new Map(foundry.CONFIG.statusEffects.map(status => [status.id, status.name])); - return this.statuses.map(x => ({ - key: x, - name: game.i18n.localize(statusMap.get(x)) - })); - } + /* -------------------------------------------- */ + /* Event Handlers */ + /* -------------------------------------------- */ + /**@inheritdoc*/ async _preCreate(data, options, user) { const update = {}; if (!data.img) { @@ -62,13 +65,22 @@ export default class DhActiveEffect extends ActiveEffect { await super._preCreate(data, options, user); } + /* -------------------------------------------- */ + /* Methods */ + /* -------------------------------------------- */ + + /**@inheritdoc*/ static applyField(model, change, field) { const evalValue = this.effectSafeEval(itemAbleRollParse(change.value, model, change.effect.parent)); change.value = evalValue ?? change.value; super.applyField(model, change, field); } - /* Altered Foundry safeEval to allow non-numeric returns */ + /** + * Altered Foundry safeEval to allow non-numeric return + * @param {string} expression + * @returns + */ static effectSafeEval(expression) { let result; try { @@ -82,6 +94,24 @@ export default class DhActiveEffect extends ActiveEffect { return result; } + /** + * Generates a list of localized tags based on this item's type-specific properties. + * @returns {string[]} An array of localized tag strings. + */ + _getTags() { + const tags = [ + `${game.i18n.localize(this.parent.system.metadata.label)}: ${this.parent.name}`, + game.i18n.localize(this.isTemporary ? 'DAGGERHEART.EFFECTS.Duration.temporary' : 'DAGGERHEART.EFFECTS.Duration.passive') + ]; + + for (const statusId of this.statuses) { + const status = CONFIG.statusEffects.find(s => s.id === statusId); + tags.push(game.i18n.localize(status.name)); + } + + return tags; + } + async toChat(origin) { const cls = getDocumentClass('ChatMessage'); const systemData = { diff --git a/module/documents/item.mjs b/module/documents/item.mjs index a05a7ff0..3e44e846 100644 --- a/module/documents/item.mjs +++ b/module/documents/item.mjs @@ -74,8 +74,8 @@ export default class DHItem extends foundry.documents.Item { isInventoryItem === true ? 'Inventory Items' //TODO localize : isInventoryItem === false - ? 'Character Items' //TODO localize - : 'Other'; //TODO localize + ? 'Character Items' //TODO localize + : 'Other'; //TODO localize return { value: type, label, group }; } @@ -96,6 +96,28 @@ export default class DHItem extends foundry.documents.Item { }); } + /* -------------------------------------------- */ + + /** + * Generate an array of localized tag. + * @returns {string[]} An array of localized tag strings. + */ + getTags() { + const tags = []; + if (this.system.getTags) tags.push(...this.system.getTags()); + return tags; + } + + /** + * Generate a localized label array for this item. + * @returns {(string | { value: string, icons: string[] })[]} An array of localized strings and damage label objects. + */ + getLabels() { + const labels = []; + if (this.system.getLabels) labels.push(...this.system.getLabels()); + return labels; + } + async use(event) { const actions = new Set(this.system.actionsList); if (actions?.size) { @@ -115,10 +137,10 @@ export default class DHItem extends foundry.documents.Item { this.type === 'ancestry' ? game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.ancestryTitle') : this.type === 'community' - ? game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.communityTitle') - : this.type === 'feature' - ? game.i18n.localize('TYPES.Item.feature') - : game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.subclassFeatureTitle'), + ? game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.communityTitle') + : this.type === 'feature' + ? game.i18n.localize('TYPES.Item.feature') + : game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.subclassFeatureTitle'), origin: origin, img: this.img, name: this.name, diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index 9d22906c..34de5a5c 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -240,7 +240,7 @@ 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. + * @returns {Promise} The resolved document, or null if not found or invalid. */ export async function getDocFromElement(element) { const target = element.closest('[data-item-uuid]'); diff --git a/templates/sheets/global/partials/inventory-item-V2.hbs b/templates/sheets/global/partials/inventory-item-V2.hbs index a9eb6e3b..b8e95280 100644 --- a/templates/sheets/global/partials/inventory-item-V2.hbs +++ b/templates/sheets/global/partials/inventory-item-V2.hbs @@ -15,169 +15,63 @@ Parameters: - showActions {boolean} : If true show feature's actions. --}} -
  • +
  • {{!-- Image --}} -
    +
    d20
    {{!-- Name & Tags --}} -
    +
    {{!-- Item Name --}}
    {{localize item.name}}
    - {{!-- Attack Block Start --}} - {{#if (eq type 'attack')}} -
    -
    - {{localize 'DAGGERHEART.GENERAL.unarmed'}} -
    -
    - {{localize 'DAGGERHEART.CONFIG.ActionType.action'}} -
    + {{!-- Attack Tags Start --}} + {{#if (eq type 'attack')}} +
    +
    + {{localize 'DAGGERHEART.GENERAL.unarmed'}} +
    +
    + {{localize 'DAGGERHEART.CONFIG.ActionType.action'}} +
    +
    + {{!-- Attack Tags End --}} + {{else}} + {{!-- Other elements Tags Start --}} + {{#with item}} + {{#if (not ../hideTags)}} +
    + {{#each this._getTags as |tag|}} +
    + {{tag}}
    - {{/if}} - {{!-- Attack Block End --}} - - {{!-- 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)}} +
    +
    + {{#each this._getLabels as |label|}} + {{ifThen label.value label.value label}} + {{#each label.icons as |icon|}} + + {{/each}} + {{#if (not @last)}} + - + {{/if}} + {{/each}} +
    -
    - {{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}} + {{/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.localizedStatuses as |status|}} -
    {{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 --}} + {{!-- Other elements Tags End --}}
    {{!-- Simple Resource --}} @@ -256,4 +150,4 @@ Parameters: {{/each}}
    {{/if}} -
  • + \ No newline at end of file