From ee89d5cb9e279f40ffd5803be93cab1dd6321ba8 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Tue, 27 Jan 2026 16:45:45 +0100 Subject: [PATCH 1/4] Fixed weapons --- .../applications/sheets/actors/character.mjs | 34 ++------ .../sheets/api/application-mixin.mjs | 22 +++-- module/data/actor/character.mjs | 22 ----- module/documents/actor.mjs | 87 +++++++++++++++++++ module/documents/item.mjs | 6 ++ 5 files changed, 115 insertions(+), 56 deletions(-) diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index 29e838ba..540a8392 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -763,32 +763,14 @@ export default class CharacterSheet extends DHBaseActorSheet { static async #toggleEquipItem(_event, button) { const item = await getDocFromElement(button); if (!item) return; - if (item.system.equipped) { - await item.update({ 'system.equipped': false }); - return; - } - switch (item.type) { - case 'armor': - const currentArmor = this.document.system.armor; - if (currentArmor) { - await currentArmor.update({ 'system.equipped': false }); - } - - await item.update({ 'system.equipped': true }); - break; - case 'weapon': - if (this.document.effects.find(x => !x.disabled && x.type === 'beastform')) { - return ui.notifications.warn( - game.i18n.localize('DAGGERHEART.UI.Notifications.beastformEquipWeapon') - ); - } - - await this.document.system.constructor.unequipBeforeEquip.bind(this.document.system)(item); - - await item.update({ 'system.equipped': true }); - break; - } + const changedData = await this.document.toggleEquipItem(item); + const removedData = changedData.filter(x => !x.add); + this.document.update({ + 'system.sidebarFavorites': [ + ...this.document.system.sidebarFavorites.filter(x => removedData.every(r => r.item.id !== x.id)) + ] + }); } /** @@ -1042,6 +1024,6 @@ export default class CharacterSheet extends DHBaseActorSheet { if (this.document.system.sidebarFavorites.some(x => x.id === item.id)) return; - this.document.update({ 'system.sidebarFavorites': [...this.document.system.sidebarFavorites, item] }); + this.document.setFavoriteItem(item, true); } } diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index 5c285bc9..6e1827d0 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -547,13 +547,20 @@ export default function DHApplicationMixin(Base) { name: 'Unfavorite', icon: 'fa-regular fa-star', condition: target => { - return this.document.type === 'character' && target.closest('.items-sidebar-list'); + const doc = getDocFromElementSync(target); + const isFavorited = this.document.system.sidebarFavorites.some(x => x.id === doc.id); + return this.document.type === 'character' && isFavorited; }, callback: async (target, _event) => { const doc = await getDocFromElement(target); - this.document.update({ - 'system.sidebarFavorites': this.document.system.sidebarFavorites.filter(x => x.id !== doc.id) - }); + if (doc.type === 'domainCard') { + } else { + this.document.update({ + 'system.sidebarFavorites': this.document.system.sidebarFavorites.filter( + x => x.id !== doc.id + ) + }); + } } }); @@ -562,17 +569,16 @@ export default function DHApplicationMixin(Base) { icon: 'fa-solid fa-star', condition: target => { const doc = getDocFromElementSync(target); + const isFavorited = this.document.system.sidebarFavorites.some(x => x.id === doc.id); return ( !(doc instanceof game.system.api.documents.DhActiveEffect) && this.document.type === 'character' && - !target.closest('.items-sidebar-list') + !isFavorited ); }, callback: async (target, _event) => { const doc = await getDocFromElement(target); - this.document.update({ - 'system.sidebarFavorites': [...this.document.system.sidebarFavorites, doc] - }); + this.document.setFavoriteItem(doc, true); } }); diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index b557492e..fc5cd144 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -578,28 +578,6 @@ export default class DhCharacter extends BaseDataActor { return diceTypes[attackDiceIndex]; } - static async unequipBeforeEquip(itemToEquip) { - const primary = this.primaryWeapon, - secondary = this.secondaryWeapon; - if (itemToEquip.system.secondary) { - if (primary && primary.burden === CONFIG.DH.GENERAL.burden.twoHanded.value) { - await primary.update({ 'system.equipped': false }); - } - - if (secondary) { - await secondary.update({ 'system.equipped': false }); - } - } else { - if (secondary && itemToEquip.system.burden === CONFIG.DH.GENERAL.burden.twoHanded.value) { - await secondary.update({ 'system.equipped': false }); - } - - if (primary) { - await primary.update({ 'system.equipped': false }); - } - } - } - prepareBaseData() { this.evasion += this.class.value?.system?.evasion ?? 0; diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index e8bea0bf..639938b3 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -992,4 +992,91 @@ export default class DhpActor extends Actor { return allTokens; } + + async unequipBeforeEquip(itemToEquip, options = { render: true }) { + const { render } = options; + + const primary = this.system.primaryWeapon, + secondary = this.system.secondaryWeapon; + let unequippedItems = []; + if (itemToEquip.system.secondary) { + if (primary && primary.system.burden === CONFIG.DH.GENERAL.burden.twoHanded.value) { + unequippedItems.push(primary); + } + + if (secondary) { + unequippedItems.push(secondary); + } + } else { + if (secondary && itemToEquip.system.burden === CONFIG.DH.GENERAL.burden.twoHanded.value) { + unequippedItems.push(secondary); + } + + if (primary) { + unequippedItems.push(primary); + } + } + + for (const item of unequippedItems) await item?.update({ 'system.equipped': false }, { render }); + + return unequippedItems; + } + + async toggleEquipItem(item, options = { render: true }) { + const { render } = options; + const changedItems = []; + const updateAndAddChangedItem = async (item, equip) => { + changedItems.push({ item, add: equip }); + await item.update({ 'system.equipped': equip }, { render }); + }; + + if (item.system.equipped) { + await updateAndAddChangedItem(item, false); + return changedItems; + } + + switch (item.type) { + case 'armor': + const currentArmor = this.system.armor; + if (currentArmor) { + await updateAndAddChangedItem(currentArmor, false); + } + + await updateAndAddChangedItem(item, true); + break; + case 'weapon': + if (this.effects.find(x => !x.disabled && x.type === 'beastform')) { + return ui.notifications.warn( + game.i18n.localize('DAGGERHEART.UI.Notifications.beastformEquipWeapon') + ); + } + + const unequippedItems = await this.unequipBeforeEquip(item, { render }); + changedItems.push(...unequippedItems.map(x => ({ item: x, add: false }))); + await updateAndAddChangedItem(item, true); + break; + } + + return changedItems; + } + + async setFavoriteItem(item, setFavorited) { + const favoritesToRemove = []; + const favoritesToAdd = []; + if (item.type === 'weapon') { + const changedData = await this.toggleEquipItem(item, { render: false }); + for (const data of changedData) { + if (data.add) favoritesToAdd.push(data.item); + else favoritesToRemove.push(data.item); + } + } else if (setFavorited) favoritesToAdd.push(item); + else favoritesToRemove.push(item); + + this.update({ + 'system.sidebarFavorites': [ + ...this.system.sidebarFavorites.filter(x => favoritesToRemove.every(r => r.id !== x.id)), + ...favoritesToAdd + ] + }); + } } diff --git a/module/documents/item.mjs b/module/documents/item.mjs index fe62c5bd..565080c7 100644 --- a/module/documents/item.mjs +++ b/module/documents/item.mjs @@ -226,5 +226,11 @@ export default class DHItem extends foundry.documents.Item { async _preDelete() { this.deleteTriggers(); + + if (this.parent?.type === 'character') { + const filteredFavorites = this.parent.system.sidebarFavorites.filter(x => x.id !== this.id); + if (this.parent.system.sidebarFavorites.length !== filteredFavorites.length) + this.parent.update({ 'system.sidebarFavorites': filteredFavorites }); + } } } From b1f2bdbee57cf53a50ce0b0c55c6e1ecff35d90f Mon Sep 17 00:00:00 2001 From: WBHarry Date: Tue, 27 Jan 2026 17:08:36 +0100 Subject: [PATCH 2/4] Fixed the rest --- .../applications/sheets/actors/character.mjs | 15 +++++------ .../sheets/api/application-mixin.mjs | 1 + module/data/item/domainCard.mjs | 20 -------------- module/documents/actor.mjs | 27 ++++++++++++++++--- 4 files changed, 31 insertions(+), 32 deletions(-) diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index 540a8392..2ec176ae 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -754,8 +754,6 @@ export default class CharacterSheet extends DHBaseActorSheet { await config.resourceUpdates.updateResources(); } - //TODO: redo toggleEquipItem method - /** * Toggles the equipped state of an item (armor or weapon). * @type {ApplicationClickAction} @@ -830,12 +828,13 @@ export default class CharacterSheet extends DHBaseActorSheet { */ static async #toggleVault(_event, button) { const doc = await getDocFromElement(button); - const { available } = this.document.system.loadoutSlot; - if (doc.system.inVault && !available && !doc.system.loadoutIgnore) { - return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.loadoutMaxReached')); - } - - await doc?.update({ 'system.inVault': !doc.system.inVault }); + const changedData = await this.document.toggleDomainCardVault(doc); + const removedData = changedData.filter(x => !x.add); + this.document.update({ + 'system.sidebarFavorites': [ + ...this.document.system.sidebarFavorites.filter(x => removedData.every(r => r.item.id !== x.id)) + ] + }); } /** diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index 6e1827d0..0c0de075 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -554,6 +554,7 @@ export default function DHApplicationMixin(Base) { callback: async (target, _event) => { const doc = await getDocFromElement(target); if (doc.type === 'domainCard') { + this.document.setFavoriteItem(doc, false); } else { this.document.update({ 'system.sidebarFavorites': this.document.system.sidebarFavorites.filter( diff --git a/module/data/item/domainCard.mjs b/module/data/item/domainCard.mjs index 2c272f75..327dafce 100644 --- a/module/data/item/domainCard.mjs +++ b/module/data/item/domainCard.mjs @@ -72,26 +72,6 @@ export default class DHDomainCard extends BaseDataItem { /* -------------------------------------------- */ - /**@inheritdoc */ - async _preUpdate(data, options, user) { - const allowed = await super._preUpdate(data, options, user); - if (allowed === false) return; - - if (this.parent.parent?.type === 'character') { - if ( - data.system?.inVault && - !this.inVault && - this.parent.parent.system.sidebarFavorites.find(x => x?.id === this.parent.id) - ) { - this.parent.parent.update({ - 'system.sidebarFavorites': this.parent.parent.system.sidebarFavorites.filter( - x => x.id !== this.parent.id - ) - }); - } - } - } - /**@inheritdoc */ async _preCreate(data, options, user) { const allowed = await super._preCreate(data, options, user); diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 639938b3..c04d886b 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -993,6 +993,18 @@ export default class DhpActor extends Actor { return allTokens; } + async toggleDomainCardVault(card, options = { render: true }) { + const { render } = options; + const { available } = this.system.loadoutSlot; + + if (card.system.inVault && !available && !card.system.loadoutIgnore) { + return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.loadoutMaxReached')); + } + + await card?.update({ 'system.inVault': !card.system.inVault }, { render }); + return [{ item: card, add: !card.system.inVault }]; + } + async unequipBeforeEquip(itemToEquip, options = { render: true }) { const { render } = options; @@ -1030,7 +1042,7 @@ export default class DhpActor extends Actor { await item.update({ 'system.equipped': equip }, { render }); }; - if (item.system.equipped) { + if (item.system.equipped && [undefined, false].includes(options.equip)) { await updateAndAddChangedItem(item, false); return changedItems; } @@ -1051,7 +1063,7 @@ export default class DhpActor extends Actor { ); } - const unequippedItems = await this.unequipBeforeEquip(item, { render }); + const unequippedItems = await this.unequipBeforeEquip(item, { render: false }); changedItems.push(...unequippedItems.map(x => ({ item: x, add: false }))); await updateAndAddChangedItem(item, true); break; @@ -1060,11 +1072,18 @@ export default class DhpActor extends Actor { return changedItems; } + /* This is very convoluted, and there is almost certainly a better way to do it. I couldn't get it working any better way atm though. */ async setFavoriteItem(item, setFavorited) { const favoritesToRemove = []; const favoritesToAdd = []; - if (item.type === 'weapon') { - const changedData = await this.toggleEquipItem(item, { render: false }); + if (['weapon', 'armor'].includes(item.type)) { + const changedData = await this.toggleEquipItem(item, { render: false, equip: setFavorited }); + for (const data of changedData) { + if (data.add) favoritesToAdd.push(data.item); + else favoritesToRemove.push(data.item); + } + } else if (item.type === 'domainCard') { + const changedData = await this.toggleDomainCardVault(item, { render: false }); for (const data of changedData) { if (data.add) favoritesToAdd.push(data.item); else favoritesToRemove.push(data.item); From 82b60859daa3471444e1c418bbf0bd9c46ed97f9 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Tue, 27 Jan 2026 17:08:50 +0100 Subject: [PATCH 3/4] Raised version --- system.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system.json b/system.json index 8624bab7..4a5eee0f 100644 --- a/system.json +++ b/system.json @@ -2,7 +2,7 @@ "id": "daggerheart", "title": "Daggerheart", "description": "An unofficial implementation of the Daggerheart system", - "version": "1.6.0", + "version": "1.6.1", "compatibility": { "minimum": "13.346", "verified": "13.351", From c95520c9d8ebbe74a48fe61501a0270efb84561f Mon Sep 17 00:00:00 2001 From: WBHarry Date: Tue, 27 Jan 2026 18:55:25 +0100 Subject: [PATCH 4/4] . --- .../sheets/api/application-mixin.mjs | 34 ++++++++----------- module/documents/actor.mjs | 7 ++-- 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index 0c0de075..d379906a 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -532,17 +532,6 @@ export default function DHApplicationMixin(Base) { callback: async target => (await getDocFromElement(target)).toChat(this.document.uuid) }); - if (deletable) - options.push({ - name: 'CONTROLS.CommonDelete', - icon: 'fa-solid fa-trash', - callback: async (target, event) => { - const doc = await getDocFromElement(target); - if (event.shiftKey) return doc.delete(); - else return doc.deleteDialog(); - } - }); - options.push({ name: 'Unfavorite', icon: 'fa-regular fa-star', @@ -553,15 +542,9 @@ export default function DHApplicationMixin(Base) { }, callback: async (target, _event) => { const doc = await getDocFromElement(target); - if (doc.type === 'domainCard') { - this.document.setFavoriteItem(doc, false); - } else { - this.document.update({ - 'system.sidebarFavorites': this.document.system.sidebarFavorites.filter( - x => x.id !== doc.id - ) - }); - } + this.document.update({ + 'system.sidebarFavorites': this.document.system.sidebarFavorites.filter(x => x.id !== doc.id) + }); } }); @@ -583,6 +566,17 @@ export default function DHApplicationMixin(Base) { } }); + if (deletable) + options.push({ + name: 'CONTROLS.CommonDelete', + icon: 'fa-solid fa-trash', + callback: async (target, event) => { + const doc = await getDocFromElement(target); + if (event.shiftKey) return doc.delete(); + else return doc.deleteDialog(); + } + }); + return options.map(option => ({ ...option, icon: `` diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index c04d886b..9d2de96b 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -1001,8 +1001,9 @@ export default class DhpActor extends Actor { return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.loadoutMaxReached')); } - await card?.update({ 'system.inVault': !card.system.inVault }, { render }); - return [{ item: card, add: !card.system.inVault }]; + const toVault = options.toVault ?? !card.system.inVault; + await card?.update({ 'system.inVault': toVault }, { render }); + return [{ item: card, add: !toVault }]; } async unequipBeforeEquip(itemToEquip, options = { render: true }) { @@ -1083,7 +1084,7 @@ export default class DhpActor extends Actor { else favoritesToRemove.push(data.item); } } else if (item.type === 'domainCard') { - const changedData = await this.toggleDomainCardVault(item, { render: false }); + const changedData = await this.toggleDomainCardVault(item, { render: false, toVault: !setFavorited }); for (const data of changedData) { if (data.add) favoritesToAdd.push(data.item); else favoritesToRemove.push(data.item);