From 2a4777f1a05d3d8505f23c40e41f377375e7c069 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sat, 26 Jul 2025 00:37:30 +0200 Subject: [PATCH] [Fix] Itemlink Redux Revengeance (#399) * Small random fixes * Added use of ItemLinkFields * Multiclass levelup fixes * Fixed our onCreate methods unintentionally being run on all clients * Remade apps handling * Added for all class items and subclass * Restored foreignDocumentUuidField * Improved * PR fxies * Fixed tooltip enrichment * . * Reverted silly change --- daggerheart.mjs | 2 +- .../characterCreation/characterCreation.mjs | 67 +++++- .../dialogs/actionSelectionDialog.mjs | 12 +- module/applications/dialogs/d20RollDialog.mjs | 9 +- .../applications/levelup/characterLevelup.mjs | 7 +- module/applications/levelup/levelup.mjs | 24 +- .../sheets/api/application-mixin.mjs | 17 +- module/applications/sheets/api/base-item.mjs | 29 ++- module/applications/sheets/items/ancestry.mjs | 62 +----- .../applications/sheets/items/beastform.mjs | 5 + module/applications/sheets/items/class.mjs | 81 +++---- .../applications/sheets/items/community.mjs | 5 + module/applications/sheets/items/subclass.mjs | 63 +----- module/applications/ui/chatLog.mjs | 2 +- module/data/action/baseAction.mjs | 10 +- module/data/action/effectAction.mjs | 2 +- module/data/activeEffect/beastformEffect.mjs | 4 +- module/data/actor/character.mjs | 3 +- module/data/chat-message/adversaryRoll.mjs | 4 +- module/data/chat-message/applyEffects.mjs | 4 +- module/data/chat-message/damageRoll.mjs | 4 +- module/data/fields/action/_module.mjs | 2 +- module/data/fields/action/beastformField.mjs | 4 +- module/data/fields/action/costField.mjs | 6 +- module/data/fields/action/damageField.mjs | 4 +- module/data/fields/action/effectsField.mjs | 4 +- module/data/fields/action/healingField.mjs | 4 +- module/data/fields/action/rangeField.mjs | 4 +- module/data/fields/action/rollField.mjs | 2 +- module/data/fields/action/saveField.mjs | 4 +- module/data/fields/action/targetField.mjs | 8 +- module/data/fields/action/usesField.mjs | 7 +- module/data/fields/itemLinkFields.mjs | 19 ++ module/data/fields/mappingField.mjs | 208 +++++++++--------- module/data/item/ancestry.mjs | 16 +- module/data/item/base.mjs | 30 ++- module/data/item/beastform.mjs | 4 +- module/data/item/class.mjs | 46 +++- module/data/item/subclass.mjs | 12 +- module/dice/damageRoll.mjs | 2 +- module/documents/actor.mjs | 8 +- module/documents/item.mjs | 2 +- module/documents/tooltipManager.mjs | 45 +++- module/helpers/utils.mjs | 44 ++++ styles/less/dialog/actions/action-list.less | 10 +- .../dialog/level-up/selections-container.less | 1 - .../less/sheets/actors/character/effects.less | 38 ++-- .../characterCreation/tabs/equipment.hbs | 78 +++---- .../global/partials/feature-section-item.hbs | 2 +- templates/sheets/items/ancestry/features.hbs | 14 +- templates/sheets/items/class/features.hbs | 8 +- templates/sheets/items/class/settings.hbs | 50 +++-- templates/sheets/items/weapon/settings.hbs | 2 +- templates/ui/tooltip/armor.hbs | 2 +- templates/ui/tooltip/consumable.hbs | 2 +- templates/ui/tooltip/domainCard.hbs | 2 +- templates/ui/tooltip/effect.hbs | 5 + templates/ui/tooltip/feature.hbs | 2 +- templates/ui/tooltip/miscellaneous.hbs | 2 +- templates/ui/tooltip/parts/tooltipTags.hbs | 16 +- templates/ui/tooltip/weapon.hbs | 2 +- 61 files changed, 648 insertions(+), 489 deletions(-) create mode 100644 module/data/fields/itemLinkFields.mjs create mode 100644 templates/ui/tooltip/effect.hbs diff --git a/daggerheart.mjs b/daggerheart.mjs index 3f7dec77..387c1a74 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -3,7 +3,7 @@ import * as applications from './module/applications/_module.mjs'; import * as models from './module/data/_module.mjs'; import * as documents from './module/documents/_module.mjs'; import * as dice from './module/dice/_module.mjs'; -import * as fields from './module/data/fields/_module.mjs' +import * as fields from './module/data/fields/_module.mjs'; import RegisterHandlebarsHelpers from './module/helpers/handlebarsHelper.mjs'; import { enricherConfig, enricherRenderSetup } from './module/enrichers/_module.mjs'; import { getCommandTarget, rollCommandToJSON } from './module/helpers/utils.mjs'; diff --git a/module/applications/characterCreation/characterCreation.mjs b/module/applications/characterCreation/characterCreation.mjs index b8759cc5..fc819e66 100644 --- a/module/applications/characterCreation/characterCreation.mjs +++ b/module/applications/characterCreation/characterCreation.mjs @@ -205,7 +205,11 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl _getSetupTabs(tabs) { for (const v of Object.values(tabs)) { - v.active = this.tabGroups[v.group] ? this.tabGroups[v.group] === v.id : v.active; + v.active = this.tabGroups[v.group] + ? this.tabGroups[v.group] === v.id + : this.tabGroups.primary !== 'equipment' + ? v.active + : false; v.cssClass = v.active ? 'active' : ''; switch (v.id) { @@ -242,6 +246,16 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl marker.classList.remove('active'); } } + + if (tab === 'equipment') { + this.tabGroups.setup = null; + this.element.querySelector('section[data-group="setup"].active')?.classList?.remove?.('active'); + } else { + this.tabGroups.setup = 'domainCards'; + this.element + .querySelector('section[data-group="setup"][data-tab="domainCards"]') + ?.classList?.add?.('active'); + } } } @@ -256,6 +270,11 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl }); } + async _preFirstRender(_context, _options) { + this.tabGroups.primary = 'setup'; + this.tabGroups.setup = 'ancestry'; + } + async _prepareContext(_options) { const context = await super._prepareContext(_options); context.tabs = this._getTabs(this.constructor.TABS); @@ -266,7 +285,7 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl async _preparePartContext(partId, context) { switch (partId) { case 'footer': - context.isLastTab = this.tabGroups.setup === 'domainCards'; + context.isLastTab = this.tabGroups.setup === 'domainCards' || this.tabGroups.primary !== 'setup'; switch (this.tabGroups.setup) { case null: case 'ancestry': @@ -321,10 +340,7 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl }) }; context.traits.nrTotal = Object.keys(context.traits.values).length; - context.traits.nrSelected = Object.values(context.traits.values).reduce( - (acc, trait) => acc + (trait.value !== null ? 1 : 0), - 0 - ); + context.traits.nrSelected = this.getNrSelectedTrait(); context.experience = { values: this.setup.experiences, @@ -378,6 +394,10 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl choiceA: { suggestions: suggestions.inventory.choiceA, compendium: 'consumables' }, choiceB: { suggestions: suggestions.inventory.choiceB, compendium: 'general-items' } }; + context.noInventoryChoices = + suggestions.inventory.take.length === 0 && + suggestions.inventory.choiceA?.length === 0 && + suggestions.inventory.choiceB?.length === 0; break; } @@ -408,7 +428,7 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl case 5: return Object.values(this.setup.experiences).every(x => x.name) ? 6 : 5; case 4: - return Object.values(this.setup.traits).every(x => x.value !== null) ? 5 : 4; + return this.getNrSelectedTrait() === 6 ? 5 : 4; case 3: return this.setup.class.uuid && this.setup.subclass.uuid ? 4 : 3; case 2: @@ -418,6 +438,18 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl } } + getNrSelectedTrait() { + const traitCompareArray = [ + ...game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).traitArray + ]; + return Object.values(this.setup.traits).reduce((acc, x) => { + const index = traitCompareArray.indexOf(x.value); + traitCompareArray.splice(index, 1); + acc += index !== -1; + return acc; + }, 0); + } + async getEquipmentSuggestions(choiceA, choiceB) { if (!this.setup.class.uuid) return { inventory: { take: [] } }; @@ -429,10 +461,15 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl ? { ...characterGuide.suggestedSecondaryWeapon, uuid: characterGuide.suggestedSecondaryWeapon.uuid } : null, inventory: { - take: inventory.take ?? [], + take: inventory.take?.filter(x => x) ?? [], choiceA: - inventory.choiceA?.map(x => ({ ...x, uuid: x.uuid, selected: x.uuid === choiceA?.uuid })) ?? [], - choiceB: inventory.choiceB?.map(x => ({ ...x, uuid: x.uuid, selected: x.uuid === choiceB?.uuid })) ?? [] + inventory.choiceA + ?.filter(x => x) + .map(x => ({ ...x, uuid: x.uuid, selected: x.uuid === choiceA?.uuid })) ?? [], + choiceB: + inventory.choiceB + ?.filter(x => x) + .map(x => ({ ...x, uuid: x.uuid, selected: x.uuid === choiceB?.uuid })) ?? [] } }; } @@ -506,7 +543,10 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl name: this.setup.ancestryName ?? this.setup.primaryAncestry.name, system: { ...this.setup.primaryAncestry.system, - features: [primaryAncestryFeature.uuid, secondaryAncestryFeature.uuid] + features: [ + { type: 'primary', item: primaryAncestryFeature.uuid }, + { type: 'secondary', item: secondaryAncestryFeature.uuid } + ] } }; @@ -535,7 +575,10 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl await this.character.createEmbeddedDocuments('Item', [this.equipment.inventory.choiceA]); if (this.equipment.inventory.choiceB.uuid) await this.character.createEmbeddedDocuments('Item', [this.equipment.inventory.choiceB]); - await this.character.createEmbeddedDocuments('Item', this.setup.class.system.inventory.take); + await this.character.createEmbeddedDocuments( + 'Item', + this.setup.class.system.inventory.take.filter(x => x) + ); await this.character.update({ system: { diff --git a/module/applications/dialogs/actionSelectionDialog.mjs b/module/applications/dialogs/actionSelectionDialog.mjs index c029be24..786b5329 100644 --- a/module/applications/dialogs/actionSelectionDialog.mjs +++ b/module/applications/dialogs/actionSelectionDialog.mjs @@ -1,7 +1,7 @@ const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api; export default class ActionSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) { - constructor(item, event, options={}) { + constructor(item, event, options = {}) { super(options); this.#item = item; this.#event = event; @@ -24,7 +24,7 @@ export default class ActionSelectionDialog extends HandlebarsApplicationMixin(Ap static PARTS = { actions: { - template: "systems/daggerheart/templates/dialogs/actionSelect.hbs" + template: 'systems/daggerheart/templates/dialogs/actionSelect.hbs' } }; @@ -60,10 +60,10 @@ export default class ActionSelectionDialog extends HandlebarsApplicationMixin(Ap const actions = this.#item.system.actionsList, itemName = this.#item.name; return { - ...await super._prepareContext(options), + ...(await super._prepareContext(options)), actions, itemName - } + }; } static async #onChooseAction(event, button) { @@ -80,8 +80,8 @@ export default class ActionSelectionDialog extends HandlebarsApplicationMixin(Ap static create(item, event, options) { return new Promise(resolve => { const dialog = new this(item, event, options); - dialog.addEventListener("close", () => resolve(dialog.action), { once: true }); + dialog.addEventListener('close', () => resolve(dialog.action), { once: true }); dialog.render({ force: true }); }); } -} \ No newline at end of file +} diff --git a/module/applications/dialogs/d20RollDialog.mjs b/module/applications/dialogs/d20RollDialog.mjs index 5cd80c7d..05ca1505 100644 --- a/module/applications/dialogs/d20RollDialog.mjs +++ b/module/applications/dialogs/d20RollDialog.mjs @@ -68,7 +68,10 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio })); if (this.config.costs?.length) { - const updatedCosts = game.system.api.fields.ActionFields.CostField.calcCosts.call(this.action, this.config.costs); + const updatedCosts = game.system.api.fields.ActionFields.CostField.calcCosts.call( + this.action, + this.config.costs + ); context.costs = updatedCosts.map(x => ({ ...x, label: x.keyIsID @@ -80,7 +83,9 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio } if (this.config.uses?.max) { context.uses = game.system.api.fields.ActionFields.UsesField.calcUses.call(this.action, this.config.uses); - context.canRoll = context.canRoll && game.system.api.fields.ActionFields.UsesField.hasUses.call(this.action, context.uses); + context.canRoll = + context.canRoll && + game.system.api.fields.ActionFields.UsesField.hasUses.call(this.action, context.uses); } if (this.roll) { context.roll = this.roll; diff --git a/module/applications/levelup/characterLevelup.mjs b/module/applications/levelup/characterLevelup.mjs index d6bbe2db..8e17a907 100644 --- a/module/applications/levelup/characterLevelup.mjs +++ b/module/applications/levelup/characterLevelup.mjs @@ -1,7 +1,7 @@ import LevelUpBase from './levelup.mjs'; import { DhLevelup } from '../../data/levelup.mjs'; import { domains } from '../../config/domainConfig.mjs'; -import { abilities } from '../../config/actorConfig.mjs'; +import { abilities, subclassFeatureLabels } from '../../config/actorConfig.mjs'; export default class DhCharacterLevelUp extends LevelUpBase { constructor(actor) { @@ -166,6 +166,7 @@ export default class DhCharacterLevelUp extends LevelUpBase { context.multiclass = { ...data, ...(multiclass.toObject?.() ?? multiclass), + type: 'multiclass', uuid: multiclass.uuid, domains: multiclass?.system?.domains.map(key => { @@ -349,8 +350,8 @@ export default class DhCharacterLevelUp extends LevelUpBase { if (!acc) acc = {}; acc[traitKey] = { label: game.i18n.localize(abilities[traitKey].label), - old: this.actor.system.traits[traitKey].max, - new: this.actor.system.traits[traitKey].max + advancement.trait[traitKey] + old: this.actor.system.traits[traitKey].value, + new: this.actor.system.traits[traitKey].value + advancement.trait[traitKey] }; } return acc; diff --git a/module/applications/levelup/levelup.mjs b/module/applications/levelup/levelup.mjs index 141a0a06..7820c267 100644 --- a/module/applications/levelup/levelup.mjs +++ b/module/applications/levelup/levelup.mjs @@ -452,6 +452,12 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2) return; } + const secondaryData = Object.keys( + foundry.utils.getProperty(this.levelup, `${target.dataset.path}.secondaryData`) + ).reduce((acc, key) => { + acc[`-=${key}`] = null; + return acc; + }, {}); await this.levelup.updateSource({ multiclass: { class: item.uuid, @@ -464,7 +470,8 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2) amount: target.dataset.amount ? Number(target.dataset.amount) : null, value: target.dataset.value, type: target.dataset.type, - data: item.uuid + data: item.uuid, + secondaryData: secondaryData } }); this.render(); @@ -538,10 +545,21 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2) static async selectDomain(_, button) { const option = foundry.utils.getProperty(this.levelup, button.dataset.path); const domain = option.secondaryData.domain ? null : button.dataset.domain; + const update = { [`${button.dataset.path}.secondaryData.domain`]: domain }; - await this.levelup.updateSource({ - [`${button.dataset.path}.secondaryData.domain`]: domain + const domainCards = this.levelup.levels[this.levelup.currentLevel].achievements.domainCards; + const illegalDomainCards = option.secondaryData.domain + ? Object.keys(domainCards) + .map(key => ({ ...domainCards[key], key })) + .filter( + x => x.uuid && foundry.utils.fromUuidSync(x.uuid).system.domain === option.secondaryData.domain + ) + : []; + illegalDomainCards.forEach(card => { + update[`levels.${this.levelup.currentLevel}.achievements.domainCards.${card.key}.uuid`] = null; }); + + await this.levelup.updateSource(update); this.render(); } diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index 544ab575..7267ff2b 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -107,6 +107,13 @@ export default function DHApplicationMixin(Base) { tagifyConfigs: [] }; + /** + * Related documents that should cause a rerender of this application when updated. + */ + get relatedDocs() { + return []; + } + /* -------------------------------------------- */ /**@inheritdoc */ @@ -117,9 +124,17 @@ export default function DHApplicationMixin(Base) { /**@inheritdoc */ async _onFirstRender(context, options) { await super._onFirstRender(context, options); + this.relatedDocs.filter(doc => doc).map(doc => (doc.apps[this.id] = this)); + if (!!this.options.contextMenus.length) this._createContextMenus(); } + /** @inheritDoc */ + _onClose(options) { + super._onClose(options); + this.relatedDocs.filter(doc => doc).map(doc => delete doc.apps[this.id]); + } + /**@inheritdoc */ async _onRender(context, options) { await super._onRender(context, options); @@ -285,7 +300,7 @@ export default function DHApplicationMixin(Base) { icon: 'fa-solid fa-pen-to-square', condition: target => { const doc = getDocFromElement(target); - return !doc.hasOwnProperty('systemPath') || doc.inCollection + return !doc.hasOwnProperty('systemPath') || doc.inCollection; }, callback: target => getDocFromElement(target).sheet.render({ force: true }) } diff --git a/module/applications/sheets/api/base-item.mjs b/module/applications/sheets/api/base-item.mjs index 52b63fd9..5fe4d681 100644 --- a/module/applications/sheets/api/base-item.mjs +++ b/module/applications/sheets/api/base-item.mjs @@ -150,13 +150,15 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { static async #addFeature(_, target) { const { type } = target.dataset; const cls = foundry.documents.Item.implementation; - const feature = await cls.create({ - 'type': 'feature', - 'name': cls.defaultName({ type: 'feature' }), - 'system.subType': CONFIG.DH.ITEM.featureSubTypes[type] + const item = await cls.create({ + type: 'feature', + name: cls.defaultName({ type: 'feature' }) }); await this.document.update({ - 'system.features': [...this.document.system.features, feature].map(f => f.uuid) + 'system.features': [...this.document.system.features, { type, item }].map(x => ({ + ...x, + item: x.item?.uuid + })) }); } @@ -164,12 +166,14 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { * Remove a feature from the item. * @type {ApplicationClickAction} */ - static async #deleteFeature(_, target) { + static async #deleteFeature(_, element) { + const target = element.closest('[data-item-uuid]'); 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.map(x => x.uuid).filter(uuid => uuid !== feature.uuid) + 'system.features': this.document.system.features + .filter(x => target.dataset.type !== x.type || x.item.uuid !== feature.uuid) + .map(x => ({ ...x, item: x.item.uuid })) }); } @@ -242,10 +246,15 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event); if (data.fromInternal) return; + const target = event.target.closest('fieldset.drop-section'); const item = await fromUuid(data.uuid); if (item?.type === 'feature') { - const current = this.document.system.features.map(x => x.uuid); - await this.document.update({ 'system.features': [...current, item.uuid] }); + await this.document.update({ + 'system.features': [...this.document.system.features, { type: target.dataset.type, item }].map(x => ({ + ...x, + item: x.item?.uuid + })) + }); } } } diff --git a/module/applications/sheets/items/ancestry.mjs b/module/applications/sheets/items/ancestry.mjs index fc0b8a52..c7796f9a 100644 --- a/module/applications/sheets/items/ancestry.mjs +++ b/module/applications/sheets/items/ancestry.mjs @@ -3,12 +3,7 @@ import DHHeritageSheet from '../api/heritage-sheet.mjs'; export default class AncestrySheet extends DHHeritageSheet { /**@inheritdoc */ static DEFAULT_OPTIONS = { - classes: ['ancestry'], - actions: { - editFeature: AncestrySheet.#editFeature, - removeFeature: AncestrySheet.#removeFeature - }, - dragDrop: [{ dragSelector: null, dropSelector: '.tab.features .drop-section' }] + classes: ['ancestry'] }; /**@inheritdoc */ @@ -18,38 +13,9 @@ export default class AncestrySheet extends DHHeritageSheet { features: { template: 'systems/daggerheart/templates/sheets/items/ancestry/features.hbs' } }; - /* -------------------------------------------- */ - /* Application Clicks Actions */ - /* -------------------------------------------- */ - - /** - * 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[`${target.dataset.type}Feature`]; - if (!feature || Object.keys(feature).length === 0) { - 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[`${target.dataset.type}Feature`]; - - if (feature) await feature.update({ 'system.subType': null }); - await this.document.update({ - 'system.features': this.document.system.features.filter(x => x && x.uuid !== feature.uuid).map(x => x.uuid) - }); + /**@inheritdoc */ + get relatedDocs() { + return this.document.system.features.map(x => x.item); } /* -------------------------------------------- */ @@ -61,22 +27,12 @@ export default class AncestrySheet extends DHHeritageSheet { * @param {DragEvent} event - The drag event */ async _onDrop(event) { - event.stopPropagation(); - const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event); + const target = event.target.closest('fieldset.drop-section'); + const typeField = + this.document.system[target.dataset.type === 'primary' ? 'primaryFeature' : 'secondaryFeature']; - const item = await fromUuid(data.uuid); - if (item?.type === 'feature') { - const subType = event.target.closest('.primary-feature') ? 'primary' : 'secondary'; - if (item.system.subType && item.system.subType !== CONFIG.DH.ITEM.featureSubTypes[subType]) { - const error = subType === 'primary' ? 'featureNotPrimary' : 'featureNotSecondary'; - ui.notifications.warn(game.i18n.localize(`DAGGERHEART.UI.Notifications.${error}`)); - return; - } - - await item.update({ 'system.subType': subType }); - await this.document.update({ - 'system.features': [...this.document.system.features.map(x => x.uuid), item.uuid] - }); + if (!typeField) { + super._onDrop(event); } } } diff --git a/module/applications/sheets/items/beastform.mjs b/module/applications/sheets/items/beastform.mjs index 8894b694..1d6adcd3 100644 --- a/module/applications/sheets/items/beastform.mjs +++ b/module/applications/sheets/items/beastform.mjs @@ -31,6 +31,11 @@ export default class BeastformSheet extends DHBaseItemSheet { } }; + /**@inheritdoc */ + get relatedDocs() { + return this.document.system.features; + } + _attachPartListeners(partId, htmlElement, options) { super._attachPartListeners(partId, htmlElement, options); diff --git a/module/applications/sheets/items/class.mjs b/module/applications/sheets/items/class.mjs index 1520844b..37db5b8d 100644 --- a/module/applications/sheets/items/class.mjs +++ b/module/applications/sheets/items/class.mjs @@ -58,6 +58,31 @@ export default class ClassSheet extends DHBaseItemSheet { } }; + /**@inheritdoc */ + get relatedDocs() { + return this.document.system.features.map(x => x.item); + } + + /**@inheritdoc */ + async _onFirstRender(context, options) { + await super._onFirstRender(context, options); + + const paths = [ + 'subclasses', + 'characterGuide.suggestedPrimaryWeapon', + 'characterGuide.suggestedSecondaryWeapon', + 'characterGuide.suggestedArmor', + 'inventory.take', + 'inventory.choiceA', + 'inventory.choiceB' + ]; + + paths.forEach(path => { + const docs = [].concat(foundry.utils.getProperty(this.document, `system.${path}`) ?? []); + docs.forEach(doc => (doc.apps[this.id] = this)); + }); + } + /**@inheritdoc */ async _prepareContext(_options) { const context = await super._prepareContext(_options); @@ -87,69 +112,45 @@ export default class ClassSheet extends DHBaseItemSheet { 'system.subclasses': [...this.document.system.subclasses.map(x => x.uuid), item.uuid] }); } else if (item.type === 'feature') { - if (target.classList.contains('hope-feature')) { - if (item.system.subType && item.system.subType !== CONFIG.DH.ITEM.featureSubTypes.hope) { - ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureNotHope')); - return; - } - - await item.update({ 'system.subType': CONFIG.DH.ITEM.featureSubTypes.hope }); - await this.document.update({ - 'system.features': [...this.document.system.features.map(x => x.uuid), item.uuid] - }); - } else if (target.classList.contains('class-feature')) { - if (item.system.subType && item.system.subType !== CONFIG.DH.ITEM.featureSubTypes.class) { - ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureNotClass')); - return; - } - - await item.update({ 'system.subType': CONFIG.DH.ITEM.featureSubTypes.class }); - await this.document.update({ - 'system.features': [...this.document.system.features.map(x => x.uuid), item.uuid] - }); - } + super._onDrop(event); } else if (item.type === 'weapon') { if (target.classList.contains('primary-weapon-section')) { - if (!this.document.system.characterGuide.suggestedPrimaryWeapon && !item.system.secondary) + if (!item.system.secondary) await this.document.update({ 'system.characterGuide.suggestedPrimaryWeapon': item.uuid }); } else if (target.classList.contains('secondary-weapon-section')) { - if (!this.document.system.characterGuide.suggestedSecondaryWeapon && item.system.secondary) + if (item.system.secondary) await this.document.update({ 'system.characterGuide.suggestedSecondaryWeapon': item.uuid }); } } else if (item.type === 'armor') { if (target.classList.contains('armor-section')) { - if (!this.document.system.characterGuide.suggestedArmor) - await this.document.update({ - 'system.characterGuide.suggestedArmor': item.uuid - }); + await this.document.update({ + 'system.characterGuide.suggestedArmor': item.uuid + }); } } else if (target.classList.contains('choice-a-section')) { if (item.type === 'miscellaneous' || item.type === 'consumable') { - if (this.document.system.inventory.choiceA.length < 2) + const filteredChoiceA = this.document.system.inventory.choiceA; + if (filteredChoiceA.length < 2) await this.document.update({ - 'system.inventory.choiceA': [ - ...this.document.system.inventory.choiceA.map(x => x.uuid), - item.uuid - ] + 'system.inventory.choiceA': [...filteredChoiceA.map(x => x.uuid), item.uuid] }); } } else if (item.type === 'miscellaneous') { if (target.classList.contains('take-section')) { - if (this.document.system.inventory.take.length < 3) + const filteredTake = this.document.system.inventory.take.filter(x => x); + if (filteredTake.length < 3) await this.document.update({ - 'system.inventory.take': [...this.document.system.inventory.take.map(x => x.uuid), item.uuid] + 'system.inventory.take': [...filteredTake.map(x => x.uuid), item.uuid] }); } else if (target.classList.contains('choice-b-section')) { - if (this.document.system.inventory.choiceB.length < 2) + const filteredChoiceB = this.document.system.inventory.choiceB.filter(x => x); + if (filteredChoiceB.length < 2) await this.document.update({ - 'system.inventory.choiceB': [ - ...this.document.system.inventory.choiceB.map(x => x.uuid), - item.uuid - ] + 'system.inventory.choiceB': [...filteredChoiceB.map(x => x.uuid), item.uuid] }); } } @@ -167,7 +168,7 @@ export default class ClassSheet extends DHBaseItemSheet { static async #removeItemFromCollection(_event, element) { const { uuid, target } = element.dataset; const prop = foundry.utils.getProperty(this.document.system, target); - await this.document.update({ [`system.${target}`]: prop.filter(i => i.uuid !== uuid) }); + await this.document.update({ [`system.${target}`]: prop.filter(i => i.uuid !== uuid).map(x => x.uuid) }); } /** diff --git a/module/applications/sheets/items/community.mjs b/module/applications/sheets/items/community.mjs index 3650dec6..40af1eb3 100644 --- a/module/applications/sheets/items/community.mjs +++ b/module/applications/sheets/items/community.mjs @@ -15,4 +15,9 @@ export default class CommunitySheet extends DHHeritageSheet { scrollable: ['.feature'] } }; + + /**@inheritdoc */ + get relatedDocs() { + return this.document.system.features; + } } diff --git a/module/applications/sheets/items/subclass.mjs b/module/applications/sheets/items/subclass.mjs index 585bd6cb..54250f51 100644 --- a/module/applications/sheets/items/subclass.mjs +++ b/module/applications/sheets/items/subclass.mjs @@ -5,8 +5,7 @@ export default class SubclassSheet extends DHBaseItemSheet { static DEFAULT_OPTIONS = { classes: ['subclass'], position: { width: 600 }, - window: { resizable: false }, - actions: {} + window: { resizable: false } }; /**@override */ @@ -37,62 +36,8 @@ export default class SubclassSheet extends DHBaseItemSheet { } }; - async _onDragStart(event) { - const featureItem = event.currentTarget.closest('.drop-section'); - - if (featureItem) { - const feature = this.document.system[featureItem.dataset.type]; - if (!feature) { - ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureIsMissing')); - return; - } - - const featureData = { type: 'Item', data: { ...feature.toObject(), _id: null }, fromInternal: true }; - event.dataTransfer.setData('text/plain', JSON.stringify(featureData)); - event.dataTransfer.setDragImage(featureItem.querySelector('img'), 60, 0); - } - } - - async _onDrop(event) { - event.stopPropagation(); - - const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event); - if (data.fromInternal) return; - - const item = await fromUuid(data.uuid); - const target = event.target.closest('fieldset.drop-section'); - if (item.type === 'feature') { - if (target.dataset.type === 'foundation') { - if (item.system.subType && item.system.subType !== CONFIG.DH.ITEM.featureSubTypes.foundation) { - ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureNotFoundation')); - return; - } - - await item.update({ 'system.subType': CONFIG.DH.ITEM.featureSubTypes.foundation }); - await this.document.update({ - 'system.features': [...this.document.system.features.map(x => x.uuid), item.uuid] - }); - } else if (target.dataset.type === 'specialization') { - if (item.system.subType && item.system.subType !== CONFIG.DH.ITEM.featureSubTypes.specialization) { - ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureNotSpecialization')); - return; - } - - await item.update({ 'system.subType': CONFIG.DH.ITEM.featureSubTypes.specialization }); - await this.document.update({ - 'system.features': [...this.document.system.features.map(x => x.uuid), item.uuid] - }); - } else if (target.dataset.type === 'mastery') { - if (item.system.subType && item.system.subType !== CONFIG.DH.ITEM.featureSubTypes.mastery) { - ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureNotMastery')); - return; - } - - await item.update({ 'system.subType': CONFIG.DH.ITEM.featureSubTypes.mastery }); - await this.document.update({ - 'system.features': [...this.document.system.features.map(x => x.uuid), item.uuid] - }); - } - } + /**@inheritdoc */ + get relatedDocs() { + return this.document.system.features.map(x => x.item); } } diff --git a/module/applications/ui/chatLog.mjs b/module/applications/ui/chatLog.mjs index 2b96015f..2547a47c 100644 --- a/module/applications/ui/chatLog.mjs +++ b/module/applications/ui/chatLog.mjs @@ -255,7 +255,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo const action = message.system.actions[Number.parseInt(event.currentTarget.dataset.index)]; const actor = game.actors.get(message.system.source.actor); await actor.use(action); - }; + } async actionUseButton(event, message) { const { moveIndex, actionIndex } = event.currentTarget.dataset; diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index 4d336465..89d0caeb 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -37,7 +37,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel this.extraSchemas.forEach(s => { let clsField; - if(clsField = this.getActionField(s)) schemaFields[s] = new clsField(); + if ((clsField = this.getActionField(s))) schemaFields[s] = new clsField(); }); return schemaFields; @@ -117,14 +117,14 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel async use(event, ...args) { if (!this.actor) throw new Error("An Action can't be used outside of an Actor context."); - if(this.chatDisplay) this.toChat(); + if (this.chatDisplay) this.toChat(); let config = this.prepareConfig(event); - for(let i = 0; i < this.constructor.extraSchemas.length; i++) { + for (let i = 0; i < this.constructor.extraSchemas.length; i++) { let clsField = this.constructor.getActionField(this.constructor.extraSchemas[i]); - if(clsField?.prepareConfig) { + if (clsField?.prepareConfig) { const keep = clsField.prepareConfig.call(this, config); - if(config.isFastForward && !keep) return; + if (config.isFastForward && !keep) return; } } diff --git a/module/data/action/effectAction.mjs b/module/data/action/effectAction.mjs index de84224c..505bc924 100644 --- a/module/data/action/effectAction.mjs +++ b/module/data/action/effectAction.mjs @@ -4,7 +4,7 @@ export default class DHEffectAction extends DHBaseAction { static extraSchemas = [...super.extraSchemas, 'effects', 'target']; async trigger(event, data) { - if(this.effects.length) { + if (this.effects.length) { const cls = getDocumentClass('ChatMessage'), msg = { type: 'applyEffect', diff --git a/module/data/activeEffect/beastformEffect.mjs b/module/data/activeEffect/beastformEffect.mjs index 4e6fa104..91b84614 100644 --- a/module/data/activeEffect/beastformEffect.mjs +++ b/module/data/activeEffect/beastformEffect.mjs @@ -26,7 +26,9 @@ export default class BeastformEffect extends foundry.abstract.TypeDataModel { }; } - async _onCreate() { + async _onCreate(_data, _options, userId) { + if (userId !== game.user.id) return; + if (this.parent.parent?.type === 'character') { this.parent.parent.system.primaryWeapon?.update?.({ 'system.equipped': false }); this.parent.parent.system.secondayWeapon?.update?.({ 'system.equipped': false }); diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index 5134c5e1..c0306afa 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -263,7 +263,8 @@ export default class DhCharacter extends BaseDataActor { } get tier() { - return this.levelData.level.current === 1 + const currentLevel = this.levelData.level.current; + return currentLevel === 1 ? 1 : Object.values(game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers).tiers).find( tier => currentLevel >= tier.levels.start && currentLevel <= tier.levels.end diff --git a/module/data/chat-message/adversaryRoll.mjs b/module/data/chat-message/adversaryRoll.mjs index 11368783..45f6fe39 100644 --- a/module/data/chat-message/adversaryRoll.mjs +++ b/module/data/chat-message/adversaryRoll.mjs @@ -42,7 +42,9 @@ export default class DHAdversaryRoll extends foundry.abstract.TypeDataModel { this.hasHitTarget = this.targets.filter(t => t.hit === true).length > 0; this.currentTargets = this.targetSelection !== true - ? Array.from(game.user.targets).map(t => game.system.api.fields.ActionFields.TargetField.formatTarget(t)) + ? Array.from(game.user.targets).map(t => + game.system.api.fields.ActionFields.TargetField.formatTarget(t) + ) : this.targets; } } diff --git a/module/data/chat-message/applyEffects.mjs b/module/data/chat-message/applyEffects.mjs index 045f779d..41b0aaf5 100644 --- a/module/data/chat-message/applyEffects.mjs +++ b/module/data/chat-message/applyEffects.mjs @@ -25,7 +25,9 @@ export default class DHApplyEffect extends foundry.abstract.TypeDataModel { this.hasHitTarget = this.targets.filter(t => t.hit === true).length > 0; this.currentTargets = this.targetSelection !== true - ? Array.from(game.user.targets).map(t => game.system.api.fields.ActionFields.TargetField.formatTarget(t)) + ? Array.from(game.user.targets).map(t => + game.system.api.fields.ActionFields.TargetField.formatTarget(t) + ) : this.targets; } diff --git a/module/data/chat-message/damageRoll.mjs b/module/data/chat-message/damageRoll.mjs index e8b821c8..210cc0fe 100644 --- a/module/data/chat-message/damageRoll.mjs +++ b/module/data/chat-message/damageRoll.mjs @@ -40,7 +40,9 @@ export default class DHDamageRoll extends foundry.abstract.TypeDataModel { this.hasHitTarget = this.targets.filter(t => t.hit === true).length > 0; this.currentTargets = this.targetSelection !== true - ? Array.from(game.user.targets).map(t => game.system.api.fields.ActionFields.TargetField.formatTarget(t)) + ? Array.from(game.user.targets).map(t => + game.system.api.fields.ActionFields.TargetField.formatTarget(t) + ) : this.targets; } } diff --git a/module/data/fields/action/_module.mjs b/module/data/fields/action/_module.mjs index f9a24d69..192341e7 100644 --- a/module/data/fields/action/_module.mjs +++ b/module/data/fields/action/_module.mjs @@ -7,4 +7,4 @@ export { default as SaveField } from './saveField.mjs'; export { default as BeastformField } from './beastformField.mjs'; export { default as DamageField } from './damageField.mjs'; export { default as HealingField } from './healingField.mjs'; -export { default as RollField } from './rollField.mjs'; \ No newline at end of file +export { default as RollField } from './rollField.mjs'; diff --git a/module/data/fields/action/beastformField.mjs b/module/data/fields/action/beastformField.mjs index 19836f33..25e3cf9c 100644 --- a/module/data/fields/action/beastformField.mjs +++ b/module/data/fields/action/beastformField.mjs @@ -1,7 +1,7 @@ const fields = foundry.data.fields; export default class BeastformField extends fields.SchemaField { - constructor(options={}, context={}) { + constructor(options = {}, context = {}) { const beastformFields = { tierAccess: new fields.SchemaField({ exact: new fields.NumberField({ integer: true, nullable: true, initial: null }) @@ -9,4 +9,4 @@ export default class BeastformField extends fields.SchemaField { }; super(beastformFields, options, context); } -} \ No newline at end of file +} diff --git a/module/data/fields/action/costField.mjs b/module/data/fields/action/costField.mjs index e96a88e1..f5e1999c 100644 --- a/module/data/fields/action/costField.mjs +++ b/module/data/fields/action/costField.mjs @@ -1,7 +1,7 @@ const fields = foundry.data.fields; export default class CostField extends fields.ArrayField { - constructor(options={}, context={}) { + constructor(options = {}, context = {}) { const element = new fields.SchemaField({ key: new fields.StringField({ nullable: false, @@ -20,7 +20,7 @@ export default class CostField extends fields.ArrayField { const costs = this.cost?.length ? foundry.utils.deepClone(this.cost) : []; config.costs = CostField.calcCosts.call(this, costs); const hasCost = CostField.hasCost.call(this, config.costs); - if(config.isFastForward && !hasCost) + if (config.isFastForward && !hasCost) return ui.notifications.warn("You don't have the resources to use that action."); return hasCost; } @@ -79,4 +79,4 @@ export default class CostField extends fields.ArrayField { const realCosts = costs?.length ? costs.filter(c => c.enabled) : []; return realCosts; } -} \ No newline at end of file +} diff --git a/module/data/fields/action/damageField.mjs b/module/data/fields/action/damageField.mjs index f08fcd5e..9e9e4b16 100644 --- a/module/data/fields/action/damageField.mjs +++ b/module/data/fields/action/damageField.mjs @@ -1,4 +1,4 @@ -import FormulaField from "../formulaField.mjs"; +import FormulaField from '../formulaField.mjs'; const fields = foundry.data.fields; @@ -81,4 +81,4 @@ export class DHDamageData extends DHResourceData { ) }; } -} \ No newline at end of file +} diff --git a/module/data/fields/action/effectsField.mjs b/module/data/fields/action/effectsField.mjs index 968d753c..ddc69d2d 100644 --- a/module/data/fields/action/effectsField.mjs +++ b/module/data/fields/action/effectsField.mjs @@ -1,11 +1,11 @@ const fields = foundry.data.fields; export default class EffectsField extends fields.ArrayField { - constructor(options={}, context={}) { + constructor(options = {}, context = {}) { const element = new fields.SchemaField({ _id: new fields.DocumentIdField(), onSave: new fields.BooleanField({ initial: false }) }); super(element, options, context); } -} \ No newline at end of file +} diff --git a/module/data/fields/action/healingField.mjs b/module/data/fields/action/healingField.mjs index 926663b9..3734e596 100644 --- a/module/data/fields/action/healingField.mjs +++ b/module/data/fields/action/healingField.mjs @@ -1,4 +1,4 @@ -import { DHDamageData } from "./damageField.mjs"; +import { DHDamageData } from './damageField.mjs'; const fields = foundry.data.fields; @@ -6,4 +6,4 @@ export default class HealingField extends fields.EmbeddedDataField { constructor(options, context = {}) { super(DHDamageData, options, context); } -} \ No newline at end of file +} diff --git a/module/data/fields/action/rangeField.mjs b/module/data/fields/action/rangeField.mjs index 30469efa..2c906edb 100644 --- a/module/data/fields/action/rangeField.mjs +++ b/module/data/fields/action/rangeField.mjs @@ -1,7 +1,7 @@ const fields = foundry.data.fields; export default class RangeField extends fields.StringField { - constructor(context={}) { + constructor(context = {}) { const options = { choices: CONFIG.DH.GENERAL.range, required: false, @@ -13,4 +13,4 @@ export default class RangeField extends fields.StringField { static prepareConfig(config) { return true; } -} \ No newline at end of file +} diff --git a/module/data/fields/action/rollField.mjs b/module/data/fields/action/rollField.mjs index 597e804b..0686f71f 100644 --- a/module/data/fields/action/rollField.mjs +++ b/module/data/fields/action/rollField.mjs @@ -55,4 +55,4 @@ export default class RollField extends fields.EmbeddedDataField { constructor(options, context = {}) { super(DHActionRollData, options, context); } -} \ No newline at end of file +} diff --git a/module/data/fields/action/saveField.mjs b/module/data/fields/action/saveField.mjs index c70ba3b1..c2d84157 100644 --- a/module/data/fields/action/saveField.mjs +++ b/module/data/fields/action/saveField.mjs @@ -1,7 +1,7 @@ const fields = foundry.data.fields; export default class SaveField extends fields.SchemaField { - constructor(options={}, context={}) { + constructor(options = {}, context = {}) { const saveFields = { trait: new fields.StringField({ nullable: true, @@ -16,4 +16,4 @@ export default class SaveField extends fields.SchemaField { }; super(saveFields, options, context); } -} \ No newline at end of file +} diff --git a/module/data/fields/action/targetField.mjs b/module/data/fields/action/targetField.mjs index 647cf51c..61f79196 100644 --- a/module/data/fields/action/targetField.mjs +++ b/module/data/fields/action/targetField.mjs @@ -1,7 +1,7 @@ const fields = foundry.data.fields; export default class TargetField extends fields.SchemaField { - constructor(options={}, context={}) { + constructor(options = {}, context = {}) { const targetFields = { type: new fields.StringField({ choices: CONFIG.DH.ACTIONS.targetTypes, @@ -26,13 +26,13 @@ export default class TargetField extends fields.SchemaField { } config.targets = targets.map(t => TargetField.formatTarget.call(this, t)); const hasTargets = TargetField.checkTargets.call(this, this.target.amount, config.targets); - if(config.isFastForward && !hasTargets) + if (config.isFastForward && !hasTargets) return ui.notifications.warn('Too many targets selected for that actions.'); return hasTargets; } static checkTargets(amount, targets) { - return true + return true; // return !amount || (targets.length > amount); } @@ -59,4 +59,4 @@ export default class TargetField extends fields.SchemaField { evasion: actor.actor.system.evasion }; } -} \ No newline at end of file +} diff --git a/module/data/fields/action/usesField.mjs b/module/data/fields/action/usesField.mjs index 5f05adcb..df6c5d0c 100644 --- a/module/data/fields/action/usesField.mjs +++ b/module/data/fields/action/usesField.mjs @@ -1,7 +1,7 @@ const fields = foundry.data.fields; export default class UsesField extends fields.SchemaField { - constructor(options={}, context={}) { + constructor(options = {}, context = {}) { const usesFields = { value: new fields.NumberField({ nullable: true, initial: null }), max: new fields.NumberField({ nullable: true, initial: null }), @@ -19,8 +19,7 @@ export default class UsesField extends fields.SchemaField { if (uses && !uses.value) uses.value = 0; config.uses = uses; const hasUses = UsesField.hasUses.call(this, config.uses); - if(config.isFastForward && !hasUses) - return ui.notifications.warn("That action doesn't have remaining uses."); + if (config.isFastForward && !hasUses) return ui.notifications.warn("That action doesn't have remaining uses."); return hasUses; } @@ -36,4 +35,4 @@ export default class UsesField extends fields.SchemaField { if (!uses) return true; return (uses.hasOwnProperty('enabled') && !uses.enabled) || uses.value + 1 <= uses.max; } -} \ No newline at end of file +} diff --git a/module/data/fields/itemLinkFields.mjs b/module/data/fields/itemLinkFields.mjs new file mode 100644 index 00000000..65b1cdd0 --- /dev/null +++ b/module/data/fields/itemLinkFields.mjs @@ -0,0 +1,19 @@ +import ForeignDocumentUUIDField from './foreignDocumentUUIDField.mjs'; + +export default class ItemLinkFields extends foundry.data.fields.ArrayField { + constructor(options, context) { + super(new ItemLinkField(), options, context); + } +} + +class ItemLinkField extends foundry.data.fields.SchemaField { + constructor(context) { + super( + { + type: new foundry.data.fields.StringField({ choices: CONFIG.DH.ITEM.featureSubTypes, nullable: true }), + item: new ForeignDocumentUUIDField({ type: 'Item' }) + }, + context + ); + } +} diff --git a/module/data/fields/mappingField.mjs b/module/data/fields/mappingField.mjs index 83504536..31d91c76 100644 --- a/module/data/fields/mappingField.mjs +++ b/module/data/fields/mappingField.mjs @@ -9,120 +9,120 @@ * by `options.initialKeys`? */ export default class MappingField extends foundry.data.fields.ObjectField { - constructor(model, options) { - if ( !(model instanceof foundry.data.fields.DataField) ) { - throw new Error("MappingField must have a DataField as its contained element"); + constructor(model, options) { + if (!(model instanceof foundry.data.fields.DataField)) { + throw new Error('MappingField must have a DataField as its contained element'); + } + super(options); + + /** + * The embedded DataField definition which is contained in this field. + * @type {DataField} + */ + this.model = model; + model.parent = this; } - super(options); + + /* -------------------------------------------- */ + + /** @inheritDoc */ + static get _defaults() { + return foundry.utils.mergeObject(super._defaults, { + initialKeys: null, + initialValue: null, + initialKeysOnly: false + }); + } + + /* -------------------------------------------- */ + + /** @inheritDoc */ + _cleanType(value, options) { + Object.entries(value).forEach(([k, v]) => { + if (k.startsWith('-=')) return; + value[k] = this.model.clean(v, options); + }); + return value; + } + + /* -------------------------------------------- */ + + /** @inheritDoc */ + getInitialValue(data) { + let keys = this.initialKeys; + const initial = super.getInitialValue(data); + if (!keys || !foundry.utils.isEmpty(initial)) return initial; + if (!(keys instanceof Array)) keys = Object.keys(keys); + for (const key of keys) initial[key] = this._getInitialValueForKey(key); + return initial; + } + + /* -------------------------------------------- */ /** - * The embedded DataField definition which is contained in this field. - * @type {DataField} + * Get the initial value for the provided key. + * @param {string} key Key within the object being built. + * @param {object} [object] Any existing mapping data. + * @returns {*} Initial value based on provided field type. */ - this.model = model; - model.parent = this; - } - - /* -------------------------------------------- */ - - /** @inheritDoc */ - static get _defaults() { - return foundry.utils.mergeObject(super._defaults, { - initialKeys: null, - initialValue: null, - initialKeysOnly: false - }); - } - - /* -------------------------------------------- */ - - /** @inheritDoc */ - _cleanType(value, options) { - Object.entries(value).forEach(([k, v]) => { - if ( k.startsWith("-=") ) return; - value[k] = this.model.clean(v, options); - }); - return value; - } - - /* -------------------------------------------- */ - - /** @inheritDoc */ - getInitialValue(data) { - let keys = this.initialKeys; - const initial = super.getInitialValue(data); - if ( !keys || !foundry.utils.isEmpty(initial) ) return initial; - if ( !(keys instanceof Array) ) keys = Object.keys(keys); - for ( const key of keys ) initial[key] = this._getInitialValueForKey(key); - return initial; - } - - /* -------------------------------------------- */ - - /** - * Get the initial value for the provided key. - * @param {string} key Key within the object being built. - * @param {object} [object] Any existing mapping data. - * @returns {*} Initial value based on provided field type. - */ - _getInitialValueForKey(key, object) { - const initial = this.model.getInitialValue(); - return this.initialValue?.(key, initial, object) ?? initial; - } - - /* -------------------------------------------- */ - - /** @override */ - _validateType(value, options={}) { - if ( foundry.utils.getType(value) !== "Object" ) throw new Error("must be an Object"); - const errors = this._validateValues(value, options); - if ( !foundry.utils.isEmpty(errors) ) { - const failure = new foundry.data.validation.DataModelValidationFailure(); - failure.elements = Object.entries(errors).map(([id, failure]) => ({ id, failure })); - throw failure.asError(); + _getInitialValueForKey(key, object) { + const initial = this.model.getInitialValue(); + return this.initialValue?.(key, initial, object) ?? initial; } - } - /* -------------------------------------------- */ + /* -------------------------------------------- */ - /** - * Validate each value of the object. - * @param {object} value The object to validate. - * @param {object} options Validation options. - * @returns {Record} An object of value-specific errors by key. - */ - _validateValues(value, options) { - const errors = {}; - for ( const [k, v] of Object.entries(value) ) { - if ( k.startsWith("-=") ) continue; - const error = this.model.validate(v, options); - if ( error ) errors[k] = error; + /** @override */ + _validateType(value, options = {}) { + if (foundry.utils.getType(value) !== 'Object') throw new Error('must be an Object'); + const errors = this._validateValues(value, options); + if (!foundry.utils.isEmpty(errors)) { + const failure = new foundry.data.validation.DataModelValidationFailure(); + failure.elements = Object.entries(errors).map(([id, failure]) => ({ id, failure })); + throw failure.asError(); + } } - return errors; - } - /* -------------------------------------------- */ + /* -------------------------------------------- */ - /** @override */ - initialize(value, model, options={}) { - if ( !value ) return value; - const obj = {}; - const initialKeys = (this.initialKeys instanceof Array) ? this.initialKeys : Object.keys(this.initialKeys ?? {}); - const keys = this.initialKeysOnly ? initialKeys : Object.keys(value); - for ( const key of keys ) { - const data = value[key] ?? this._getInitialValueForKey(key, value); - obj[key] = this.model.initialize(data, model, options); + /** + * Validate each value of the object. + * @param {object} value The object to validate. + * @param {object} options Validation options. + * @returns {Record} An object of value-specific errors by key. + */ + _validateValues(value, options) { + const errors = {}; + for (const [k, v] of Object.entries(value)) { + if (k.startsWith('-=')) continue; + const error = this.model.validate(v, options); + if (error) errors[k] = error; + } + return errors; } - return obj; - } - /* -------------------------------------------- */ + /* -------------------------------------------- */ - /** @inheritDoc */ - _getField(path) { - if ( path.length === 0 ) return this; - else if ( path.length === 1 ) return this.model; - path.shift(); - return this.model._getField(path); - } -} \ No newline at end of file + /** @override */ + initialize(value, model, options = {}) { + if (!value) return value; + const obj = {}; + const initialKeys = this.initialKeys instanceof Array ? this.initialKeys : Object.keys(this.initialKeys ?? {}); + const keys = this.initialKeysOnly ? initialKeys : Object.keys(value); + for (const key of keys) { + const data = value[key] ?? this._getInitialValueForKey(key, value); + obj[key] = this.model.initialize(data, model, options); + } + return obj; + } + + /* -------------------------------------------- */ + + /** @inheritDoc */ + _getField(path) { + if (path.length === 0) return this; + else if (path.length === 1) return this.model; + path.shift(); + return this.model._getField(path); + } +} diff --git a/module/data/item/ancestry.mjs b/module/data/item/ancestry.mjs index 71b7683d..ecffcb1b 100644 --- a/module/data/item/ancestry.mjs +++ b/module/data/item/ancestry.mjs @@ -1,5 +1,5 @@ -import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs'; import BaseDataItem from './base.mjs'; +import ItemLinkFields from '../../data/fields/itemLinkFields.mjs'; export default class DHAncestry extends BaseDataItem { /** @inheritDoc */ @@ -15,23 +15,15 @@ export default class DHAncestry extends BaseDataItem { static defineSchema() { return { ...super.defineSchema(), - features: new ForeignDocumentUUIDArrayField({ type: 'Item' }) + features: new ItemLinkFields() }; } get primaryFeature() { - return ( - this.features.find(x => x?.system?.subType === CONFIG.DH.ITEM.featureSubTypes.primary) ?? - (this.features.filter(x => !x).length > 0 ? {} : null) - ); + return this.features.find(x => x.type === CONFIG.DH.ITEM.featureSubTypes.primary)?.item; } get secondaryFeature() { - return ( - this.features.find(x => x?.system?.subType === CONFIG.DH.ITEM.featureSubTypes.secondary) ?? - (this.features.filter(x => !x || x.system.subType === CONFIG.DH.ITEM.featureSubTypes.primary).length > 1 - ? {} - : null) - ); + return this.features.find(x => x.type === CONFIG.DH.ITEM.featureSubTypes.secondary)?.item; } } diff --git a/module/data/item/base.mjs b/module/data/item/base.mjs index 0b2d8ddf..5b95a810 100644 --- a/module/data/item/base.mjs +++ b/module/data/item/base.mjs @@ -8,7 +8,8 @@ * @property {boolean} isInventoryItem- Indicates whether items of this type is a Inventory Item */ -import { ActionsField } from "../fields/actionField.mjs"; +import { addLinkedItemsDiff, updateLinkedItemApps } from '../../helpers/utils.mjs'; +import { ActionsField } from '../fields/actionField.mjs'; const fields = foundry.data.fields; @@ -72,8 +73,7 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { if (this.metadata.isQuantifiable) schema.quantity = new fields.NumberField({ integer: true, initial: 1, min: 0, required: true }); - if (this.metadata.hasActions) - schema.actions = new ActionsField() + if (this.metadata.hasActions) schema.actions = new ActionsField(); return schema; } @@ -124,18 +124,21 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { } } - _onCreate(data) { + _onCreate(data, _, userId) { + if (userId !== game.user.id) return; + if (!this.actor || this.actor.type !== 'character' || !this.features) return; this.actor.createEmbeddedDocuments( 'Item', this.features.map(feature => ({ - ...feature, + ...(feature.item ?? feature), system: { - ...feature.system, + ...(feature.item?.system ?? feature.system), originItemType: this.parent.type, originId: data._id, - identifier: feature.identifier + identifier: feature.identifier, + subType: feature.item ? feature.type : undefined } })) ); @@ -151,4 +154,17 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { items.map(x => x.id) ); } + + async _preUpdate(changed, options, userId) { + const allowed = await super._preUpdate(changed, options, userId); + if (allowed === false) return false; + + addLinkedItemsDiff(changed.system?.features, this.features, options); + } + + _onUpdate(changed, options, userId) { + super._onUpdate(changed, options, userId); + + updateLinkedItemApps(options, this.parent.sheet); + } } diff --git a/module/data/item/beastform.mjs b/module/data/item/beastform.mjs index 226504df..0dca8883 100644 --- a/module/data/item/beastform.mjs +++ b/module/data/item/beastform.mjs @@ -149,7 +149,9 @@ export default class DHBeastform extends BaseDataItem { return false; } - _onCreate() { + _onCreate(_data, _options, userId) { + if (userId !== game.user.id) return; + this.parent.createEmbeddedDocuments('ActiveEffect', [ { type: 'beastform', diff --git a/module/data/item/class.mjs b/module/data/item/class.mjs index 4016a4c7..d64c77cc 100644 --- a/module/data/item/class.mjs +++ b/module/data/item/class.mjs @@ -1,6 +1,8 @@ import BaseDataItem from './base.mjs'; import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs'; import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs'; +import ItemLinkFields from '../fields/itemLinkFields.mjs'; +import { addLinkedItemsDiff, updateLinkedItemApps } from '../../helpers/utils.mjs'; export default class DHClass extends BaseDataItem { /** @inheritDoc */ @@ -27,7 +29,7 @@ export default class DHClass extends BaseDataItem { label: 'DAGGERHEART.GENERAL.HitPoints.plural' }), evasion: new fields.NumberField({ initial: 0, integer: true, label: 'DAGGERHEART.GENERAL.evasion' }), - features: new ForeignDocumentUUIDArrayField({ type: 'Item' }), + features: new ItemLinkFields(), subclasses: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false }), inventory: new fields.SchemaField({ take: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false }), @@ -52,17 +54,11 @@ export default class DHClass extends BaseDataItem { } get hopeFeatures() { - return ( - this.features.filter(x => x?.system?.subType === CONFIG.DH.ITEM.featureSubTypes.hope) ?? - (this.features.filter(x => !x).length > 0 ? {} : null) - ); + return this.features.filter(x => x.type === CONFIG.DH.ITEM.featureSubTypes.hope).map(x => x.item); } get classFeatures() { - return ( - this.features.filter(x => x?.system?.subType === CONFIG.DH.ITEM.featureSubTypes.class) ?? - (this.features.filter(x => !x).length > 0 ? {} : null) - ); + return this.features.filter(x => x.type === CONFIG.DH.ITEM.featureSubTypes.class).map(x => x.item); } async _preCreate(data, options, user) { @@ -80,6 +76,9 @@ export default class DHClass extends BaseDataItem { _onCreate(data, options, userId) { super._onCreate(data, options, userId); + + if (userId !== game.user.id) return; + if (options.parent?.type === 'character') { const path = `system.${data.system.isMulticlass ? 'multiclass.value' : 'class.value'}`; options.parent.update({ [path]: `${options.parent.uuid}.Item.${data._id}` }); @@ -98,4 +97,33 @@ export default class DHClass extends BaseDataItem { foundry.utils.getProperty(options.parent, `${path}.subclass`)?.delete(); } } + + async _preUpdate(changed, options, userId) { + const allowed = await super._preUpdate(changed, options, userId); + if (allowed === false) return false; + + const paths = [ + 'subclasses', + 'characterGuide.suggestedPrimaryWeapon', + 'characterGuide.suggestedSecondaryWeapon', + 'characterGuide.suggestedArmor', + 'inventory.take', + 'inventory.choiceA', + 'inventory.choiceB' + ]; + + for (let path of paths) { + const currentItems = [].concat(foundry.utils.getProperty(this, path) ?? []); + const changedItems = [].concat(foundry.utils.getProperty(changed, `system.${path}`) ?? []); + if (!changedItems.length) continue; + + addLinkedItemsDiff(changedItems, currentItems, options); + } + } + + _onUpdate(changed, options, userId) { + super._onUpdate(changed, options, userId); + + updateLinkedItemApps(options, this.parent.sheet); + } } diff --git a/module/data/item/subclass.mjs b/module/data/item/subclass.mjs index e0a76092..ed6f8b45 100644 --- a/module/data/item/subclass.mjs +++ b/module/data/item/subclass.mjs @@ -1,4 +1,4 @@ -import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs'; +import ItemLinkFields from '../fields/itemLinkFields.mjs'; import BaseDataItem from './base.mjs'; export default class DHSubclass extends BaseDataItem { @@ -22,22 +22,22 @@ export default class DHSubclass extends BaseDataItem { nullable: true, initial: null }), - features: new ForeignDocumentUUIDArrayField({ type: 'Item' }), + features: new ItemLinkFields(), featureState: new fields.NumberField({ required: true, initial: 1, min: 1 }), isMulticlass: new fields.BooleanField({ initial: false }) }; } get foundationFeatures() { - return this.features.filter(x => x.system.subType === CONFIG.DH.ITEM.featureSubTypes.foundation); + return this.features.filter(x => x.type === CONFIG.DH.ITEM.featureSubTypes.foundation).map(x => x.item); } get specializationFeatures() { - return this.features.filter(x => x.system.subType === CONFIG.DH.ITEM.featureSubTypes.specialization); + return this.features.filter(x => x.type === CONFIG.DH.ITEM.featureSubTypes.specialization).map(x => x.item); } get masteryFeatures() { - return this.features.filter(x => x.system.subType === CONFIG.DH.ITEM.featureSubTypes.mastery); + return this.features.filter(x => x.type === CONFIG.DH.ITEM.featureSubTypes.mastery).map(x => x.item); } async _preCreate(data, options, user) { @@ -67,6 +67,8 @@ export default class DHSubclass extends BaseDataItem { _onCreate(data, options, userId) { super._onCreate(data, options, userId); + if (userId !== game.user.id) return; + if (options.parent?.type === 'character') { const path = `system.${data.system.isMulticlass ? 'multiclass.subclass' : 'class.subclass'}`; options.parent.update({ [path]: `${options.parent.uuid}.Item.${data._id}` }); diff --git a/module/dice/damageRoll.mjs b/module/dice/damageRoll.mjs index 4f5d9172..49aa3551 100644 --- a/module/dice/damageRoll.mjs +++ b/module/dice/damageRoll.mjs @@ -12,7 +12,7 @@ export default class DamageRoll extends DHRoll { static async buildEvaluate(roll, config = {}, message = {}) { if (config.evaluate !== false) { - if(config.dialog.configure === false) roll.constructFormula(config); + if (config.dialog.configure === false) roll.constructFormula(config); for (const roll of config.roll) await roll.roll.evaluate(); } roll._evaluated = true; diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 0540073c..67a5e0b3 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -26,15 +26,15 @@ export default class DhpActor extends Actor { /** @inheritDoc */ getEmbeddedDocument(embeddedName, id, options) { let doc; - switch ( embeddedName ) { - case "Action": + switch (embeddedName) { + case 'Action': doc = this.system.actions?.get(id); - if(!doc && this.system.attack?.id === id) doc = this.system.attack; + if (!doc && this.system.attack?.id === id) doc = this.system.attack; break; default: return super.getEmbeddedDocument(embeddedName, id, options); } - if ( options?.strict && !doc ) { + if (options?.strict && !doc) { throw new Error(`The key ${id} does not exist in the ${embeddedName} Collection`); } return doc; diff --git a/module/documents/item.mjs b/module/documents/item.mjs index 3f869a5d..a05a7ff0 100644 --- a/module/documents/item.mjs +++ b/module/documents/item.mjs @@ -1,4 +1,4 @@ -import ActionSelectionDialog from "../applications/dialogs/actionSelectionDialog.mjs"; +import ActionSelectionDialog from '../applications/dialogs/actionSelectionDialog.mjs'; /** * Override and extend the basic Item implementation. diff --git a/module/documents/tooltipManager.mjs b/module/documents/tooltipManager.mjs index 71dd71d2..f685c8a6 100644 --- a/module/documents/tooltipManager.mjs +++ b/module/documents/tooltipManager.mjs @@ -8,19 +8,15 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti const item = await foundry.utils.fromUuid(itemUuid); if (item) { const isAction = item instanceof game.system.api.models.actions.actionsTypes.base; - const description = await TextEditor.enrichHTML(isAction ? item.description : item.system.description); - if (item.system?.features) { - for (let feature of item.system.features) { - feature.system.enrichedDescription = await TextEditor.enrichHTML(feature.system.description); - } - } + const isEffect = item instanceof ActiveEffect; + await this.enrichText(item, isAction || isEffect); - const type = isAction ? 'action' : item.type; + const type = isAction ? 'action' : isEffect ? 'effect' : item.type; html = await foundry.applications.handlebars.renderTemplate( `systems/daggerheart/templates/ui/tooltip/${type}.hbs`, { item: item, - description: description, + description: item.system?.enrichedDescription ?? item.enrichedDescription, config: CONFIG.DH } ); @@ -126,4 +122,37 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti ]; } } + + async enrichText(item, flatStructure) { + const { TextEditor } = foundry.applications.ux; + const enrichPaths = [ + { path: flatStructure ? '' : 'system', name: 'description' }, + { path: 'system', name: 'features' }, + { path: 'system', name: 'actions' }, + { path: 'system', name: 'customActions' } + ]; + + for (let data of enrichPaths) { + const basePath = `${data.path ? `${data.path}.` : ''}${data.name}`; + const pathValue = foundry.utils.getProperty(item, basePath); + if (!pathValue) continue; + + if (Array.isArray(pathValue) || pathValue.size) { + for (const [index, itemValue] of pathValue.entries()) { + const itemIsAction = itemValue instanceof game.system.api.models.actions.actionsTypes.base; + const value = itemIsAction || !itemValue?.item ? itemValue : itemValue.item; + const enrichedValue = await TextEditor.enrichHTML(value.description); + if (itemIsAction) value.enrichedDescription = enrichedValue; + else foundry.utils.setProperty(item, `${basePath}.${index}.enrichedDescription`, enrichedValue); + } + } else { + const enrichedValue = await TextEditor.enrichHTML(pathValue); + foundry.utils.setProperty( + item, + `${data.path ? `${data.path}.` : ''}enriched${data.name.capitalize()}`, + enrichedValue + ); + } + } + } } diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index 5ee52018..f9349da9 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -241,6 +241,50 @@ export function getDocFromElement(element) { return foundry.utils.fromUuidSync(target.dataset.itemUuid) ?? null; } +/** + * Adds the update diff on a linkedItem property to update.options for use + * in _onUpdate via the updateLinkedItemApps function. + * @param {Array} changedItems The candidate changed list + * @param {Array} currentItems The current list + * @param {object} options Additional options which modify the update request + */ +export function addLinkedItemsDiff(changedItems, currentItems, options) { + if (changedItems) { + const prevItems = new Set(currentItems); + const newItems = new Set(changedItems); + options.toLink = Array.from( + newItems + .difference(prevItems) + .map(item => item?.item ?? item) + .filter(x => (typeof x === 'object' ? x.item : x)) + ); + + options.toUnlink = Array.from( + prevItems + .difference(newItems) + .map(item => item?.item?.uuid ?? item?.uuid ?? item) + .filter(x => (typeof x === 'object' ? x.item : x)) + ); + } +} + +/** + * Adds or removes the current Application from linked document apps + * depending on an update diff in the linked item list. + * @param {object} options Additional options which modify the update requests + * @param {object} sheet The application to add or remove from document apps + */ +export function updateLinkedItemApps(options, sheet) { + options.toLink?.forEach(featureUuid => { + const doc = foundry.utils.fromUuidSync(featureUuid); + doc.apps[sheet.id] = sheet; + }); + options.toUnlink?.forEach(featureUuid => { + const doc = foundry.utils.fromUuidSync(featureUuid); + delete doc.apps[sheet.id]; + }); +} + export const itemAbleRollParse = (value, actor, item) => { if (!value) return value; diff --git a/styles/less/dialog/actions/action-list.less b/styles/less/dialog/actions/action-list.less index f51e9a60..4011d124 100644 --- a/styles/less/dialog/actions/action-list.less +++ b/styles/less/dialog/actions/action-list.less @@ -1,8 +1,8 @@ @import '../../utils/fonts.less'; .application.daggerheart.dh-style { - - .actions-list, .action-buttons-list { + .actions-list, + .action-buttons-list { display: flex; flex-direction: column; @@ -36,11 +36,11 @@ .action-item { &:hover { - background-color: rgba(255,255,255,.05); + background-color: rgba(255, 255, 255, 0.05); } padding: 5px; border-radius: 5px; - transition: background-color .3s ease-in-out; + transition: background-color 0.3s ease-in-out; .label { display: flex; @@ -55,7 +55,7 @@ } } - input[type="radio"] { + input[type='radio'] { margin-left: auto; } } diff --git a/styles/less/dialog/level-up/selections-container.less b/styles/less/dialog/level-up/selections-container.less index 182e2962..10d61ed6 100644 --- a/styles/less/dialog/level-up/selections-container.less +++ b/styles/less/dialog/level-up/selections-container.less @@ -45,7 +45,6 @@ display: flex; flex-direction: column; align-items: center; - flex: 1; position: relative; cursor: pointer; diff --git a/styles/less/sheets/actors/character/effects.less b/styles/less/sheets/actors/character/effects.less index 1549c834..387b831b 100644 --- a/styles/less/sheets/actors/character/effects.less +++ b/styles/less/sheets/actors/character/effects.less @@ -1,19 +1,19 @@ -@import '../../../utils/colors.less'; -@import '../../../utils/fonts.less'; - -.application.sheet.daggerheart.actor.dh-style.character { - .tab.effects { - .effects-sections { - display: flex; - flex-direction: column; - gap: 10px; - overflow-y: auto; - mask-image: linear-gradient(0deg, transparent 0%, black 5%, black 95%, transparent 100%); - padding: 20px 0; - padding-top: 10px; - - scrollbar-width: thin; - scrollbar-color: light-dark(@dark-blue, @golden) transparent; - } - } -} +@import '../../../utils/colors.less'; +@import '../../../utils/fonts.less'; + +.application.sheet.daggerheart.actor.dh-style.character { + .tab.effects { + .effects-sections { + display: flex; + flex-direction: column; + gap: 10px; + overflow-y: auto; + mask-image: linear-gradient(0deg, transparent 0%, black 5%, black 95%, transparent 100%); + padding: 20px 0; + padding-top: 10px; + + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; + } + } +} diff --git a/templates/characterCreation/tabs/equipment.hbs b/templates/characterCreation/tabs/equipment.hbs index c66088a2..5c84d9d4 100644 --- a/templates/characterCreation/tabs/equipment.hbs +++ b/templates/characterCreation/tabs/equipment.hbs @@ -13,7 +13,7 @@ {{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.selectArmor"}} {{/"systems/daggerheart/templates/components/card-preview.hbs"}} - {{#if armor.suggestion}} + {{#if armor.suggestion.name}}
{{armor.suggestion.name}}
@@ -33,7 +33,7 @@ {{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.selectPrimaryWeapon"}} {{/"systems/daggerheart/templates/components/card-preview.hbs"}}
- {{#if primaryWeapon.suggestion}} + {{#if primaryWeapon.suggestion.name}}
{{primaryWeapon.suggestion.name}}
@@ -48,7 +48,7 @@ {{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.selectSecondaryWeapon"}} {{/"systems/daggerheart/templates/components/card-preview.hbs"}}
- {{#if secondaryWeapon.suggestion}} + {{#if secondaryWeapon.suggestion.name}}
{{secondaryWeapon.suggestion.name}}
-
-
- {{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.startingItems"}} + {{#unless noInventoryChoices}} +
+
+ {{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.startingItems"}} -
- {{#each inventory.take}} -
- - -
- {{/each}} -
-
+
+ {{#each inventory.take}} +
+ + +
+ {{/each}} +
+
-
- {{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.choice"}} +
+ {{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.choice"}} -
- {{#each inventory.choiceA.suggestions}} -
- - -
- {{/each}} -
-
+
+ {{#each inventory.choiceA.suggestions}} +
+ + +
+ {{/each}} +
+
-
- {{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.choice"}} +
+ {{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.choice"}} -
- {{#each inventory.choiceB.suggestions}} -
- - -
- {{/each}} -
-
-
+
+ {{#each inventory.choiceB.suggestions}} +
+ + +
+ {{/each}} +
+
+ + {{/unless}} \ No newline at end of file diff --git a/templates/sheets/global/partials/feature-section-item.hbs b/templates/sheets/global/partials/feature-section-item.hbs index 238031c5..a6cc3d5d 100644 --- a/templates/sheets/global/partials/feature-section-item.hbs +++ b/templates/sheets/global/partials/feature-section-item.hbs @@ -1,4 +1,4 @@ -
  • +
  • diff --git a/templates/sheets/items/ancestry/features.hbs b/templates/sheets/items/ancestry/features.hbs index 6bc108b1..f758f80a 100644 --- a/templates/sheets/items/ancestry/features.hbs +++ b/templates/sheets/items/ancestry/features.hbs @@ -3,7 +3,7 @@ data-tab='{{tabs.features.id}}' data-group='{{tabs.features.group}}' > -
    +
    {{localize "DAGGERHEART.ITEMS.Ancestry.primaryFeature"}} {{#unless document.system.primaryFeature}}{{/unless}} @@ -11,20 +11,21 @@
    {{#if document.system.primaryFeature}}
    {{document.system.primaryFeature.name}}
    - +
    {{/if}}
    -
    +
    {{localize "DAGGERHEART.ITEMS.Ancestry.secondaryFeature"}} {{#unless document.system.secondaryFeature}}{{/unless}} @@ -32,13 +33,14 @@
    {{#if document.system.secondaryFeature}}
    {{document.system.secondaryFeature.name}}
    - +
    {{/if}} diff --git a/templates/sheets/items/class/features.hbs b/templates/sheets/items/class/features.hbs index 5431e54b..368047c9 100644 --- a/templates/sheets/items/class/features.hbs +++ b/templates/sheets/items/class/features.hbs @@ -4,7 +4,7 @@ data-group='{{tabs.features.group}}' >
    -
    +
    {{localize "DAGGERHEART.ITEMS.Class.hopeFeatures"}}
    {{#each source.system.hopeFeatures as |feature|}} @@ -13,7 +13,7 @@
    -
    +
    {{localize "DAGGERHEART.ITEMS.Class.classFeatures"}}
    {{#each source.system.classFeatures as |feature|}} @@ -36,8 +36,8 @@ {{/if}} @@ -53,11 +53,11 @@ {{localize "DAGGERHEART.ITEMS.Class.guide.suggestedSecondaryWeaponTitle"}}
    {{#if document.system.characterGuide.suggestedSecondaryWeapon}} -
    +
    {{document.system.characterGuide.suggestedSecondaryWeapon.name}}
    - +
    {{/if}} @@ -72,7 +72,7 @@ {{document.system.characterGuide.suggestedArmor.name}}
    - +
    {{/if}} @@ -86,13 +86,15 @@ {{localize "DAGGERHEART.GENERAL.take"}}
    {{#each source.system.inventory.take}} -
    - - {{this.name}} -
    - + {{#if this}} +
    + + {{this.name}} +
    + +
    -
    + {{/if}} {{/each}}
    @@ -101,13 +103,15 @@ {{localize "DAGGERHEART.ITEMS.Class.guide.inventory.thenChoose"}}
    {{#each source.system.inventory.choiceA}} -
    - - {{this.name}} -
    - + {{#if this}} +
    + + {{this.name}} +
    + +
    -
    + {{/if}} {{/each}}
    @@ -116,13 +120,15 @@ {{localize "DAGGERHEART.ITEMS.Class.guide.inventory.andEither"}}
    {{#each source.system.inventory.choiceB}} -
    - - {{this.name}} -
    - + {{#if this}} +
    + + {{this.name}} +
    + +
    -
    + {{/if}} {{/each}}
    diff --git a/templates/sheets/items/weapon/settings.hbs b/templates/sheets/items/weapon/settings.hbs index d3d3a9ba..fd8f16d9 100644 --- a/templates/sheets/items/weapon/settings.hbs +++ b/templates/sheets/items/weapon/settings.hbs @@ -20,7 +20,7 @@
    {{#with systemFields.attack.fields.damage.fields.parts.element.fields as | fields | }} {{#with (lookup ../document.system.attack.damage.parts 0) as | source | }} - {{localize "DAGGERHEART.GENERAL.title"}} + {{localize "DAGGERHEART.GENERAL.damage"}} {{localize "DAGGERHEART.GENERAL.Dice.single"}} {{formInput fields.value.fields.dice value=source.value.dice name="system.attack.damage.parts.0.value.dice"}} {{localize "DAGGERHEART.GENERAL.bonus"}} diff --git a/templates/ui/tooltip/armor.hbs b/templates/ui/tooltip/armor.hbs index 6bdf9ce3..8163feff 100644 --- a/templates/ui/tooltip/armor.hbs +++ b/templates/ui/tooltip/armor.hbs @@ -33,5 +33,5 @@ {{/each}}
    - {{> "systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs" features=item.system.customActions label=(localize "DAGGERHEART.GENERAL.Action.plural")}} + {{> "systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs" features=item.system.customActions isAction=true label=(localize "DAGGERHEART.GENERAL.Action.plural")}}
    \ No newline at end of file diff --git a/templates/ui/tooltip/consumable.hbs b/templates/ui/tooltip/consumable.hbs index f4c4c700..e51ec255 100644 --- a/templates/ui/tooltip/consumable.hbs +++ b/templates/ui/tooltip/consumable.hbs @@ -10,5 +10,5 @@

    - {{> "systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs" features=item.system.actions label=(localize "DAGGERHEART.GENERAL.Action.plural") }} + {{> "systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs" features=item.system.actions isAction=true label=(localize "DAGGERHEART.GENERAL.Action.plural") }} \ No newline at end of file diff --git a/templates/ui/tooltip/domainCard.hbs b/templates/ui/tooltip/domainCard.hbs index ae2fe349..fabc1671 100644 --- a/templates/ui/tooltip/domainCard.hbs +++ b/templates/ui/tooltip/domainCard.hbs @@ -27,5 +27,5 @@ - {{> "systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs" features=item.system.actions label=(localize "DAGGERHEART.GENERAL.Action.plural") }} + {{> "systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs" features=item.system.actions isAction=true label=(localize "DAGGERHEART.GENERAL.Action.plural") }} \ No newline at end of file diff --git a/templates/ui/tooltip/effect.hbs b/templates/ui/tooltip/effect.hbs new file mode 100644 index 00000000..fb07d895 --- /dev/null +++ b/templates/ui/tooltip/effect.hbs @@ -0,0 +1,5 @@ +
    +

    {{item.name}}

    + +
    {{{description}}}
    +
    \ No newline at end of file diff --git a/templates/ui/tooltip/feature.hbs b/templates/ui/tooltip/feature.hbs index ce475664..3e8867c1 100644 --- a/templates/ui/tooltip/feature.hbs +++ b/templates/ui/tooltip/feature.hbs @@ -3,6 +3,6 @@
    {{{description}}}
    - {{> "systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs" features=item.system.actions label=(localize "DAGGERHEART.GENERAL.Action.plural") }} + {{> "systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs" features=item.system.actions isAction=true label=(localize "DAGGERHEART.GENERAL.Action.plural") }} {{> "systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs" features=item.effects label=(localize "DAGGERHEART.GENERAL.Effect.plural") }} \ No newline at end of file diff --git a/templates/ui/tooltip/miscellaneous.hbs b/templates/ui/tooltip/miscellaneous.hbs index b4912b4d..38e7cfb0 100644 --- a/templates/ui/tooltip/miscellaneous.hbs +++ b/templates/ui/tooltip/miscellaneous.hbs @@ -3,5 +3,5 @@
    {{{description}}}
    - {{> "systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs" features=item.system.actions label=(localize "DAGGERHEART.GENERAL.Action.plural") }} + {{> "systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs" features=item.system.actions isAction=true label=(localize "DAGGERHEART.GENERAL.Action.plural") }} \ No newline at end of file diff --git a/templates/ui/tooltip/parts/tooltipTags.hbs b/templates/ui/tooltip/parts/tooltipTags.hbs index 711945a0..b77c2215 100644 --- a/templates/ui/tooltip/parts/tooltipTags.hbs +++ b/templates/ui/tooltip/parts/tooltipTags.hbs @@ -1,12 +1,14 @@ {{#if (gt features.length 0)}}

    {{label}}

    {{/if}}
    {{#each features as | feature |}} -
    -
    -
    {{localize feature.name}}
    - {{#if feature.img}}{{/if}} -
    -
    {{{localize (ifThen feature.description feature.description (ifThen feature.system.enrichedDescription feature.system.enrichedDescription feature.system.description))}}}
    -
    + {{#with (ifThen ../isAction feature (ifThen feature.item feature.item feature))}} +
    +
    +
    {{localize this.name}}
    + {{#if this.img}}{{/if}} +
    +
    {{{localize (ifThen this.enrichedDescription this.enrichedDescription this.system.enrichedDescription)}}}
    +
    + {{/with}} {{/each}}
    \ No newline at end of file diff --git a/templates/ui/tooltip/weapon.hbs b/templates/ui/tooltip/weapon.hbs index 8b152f22..a8fe9c46 100644 --- a/templates/ui/tooltip/weapon.hbs +++ b/templates/ui/tooltip/weapon.hbs @@ -52,5 +52,5 @@ {{/each}} - {{> "systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs" features=item.system.customActions label=(localize "DAGGERHEART.GENERAL.Action.plural") }} + {{> "systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs" features=item.system.customActions isAction=true label=(localize "DAGGERHEART.GENERAL.Action.plural") }} \ No newline at end of file