From 23a497bab86755829b2cc8b7425092aaad7b5a67 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Mon, 12 Jan 2026 18:54:50 +0100 Subject: [PATCH 1/9] Initial --- lang/en.json | 4 + .../applications/sheets/actors/character.mjs | 102 +++++++++++++----- .../sheets/api/application-mixin.mjs | 33 ++++++ module/data/actor/character.mjs | 4 +- .../less/sheets/actors/character/sidebar.less | 19 ++++ templates/sheets/actors/character/sidebar.hbs | 60 ++++------- .../partials/inventory-item-compact.hbs | 5 - 7 files changed, 157 insertions(+), 70 deletions(-) diff --git a/lang/en.json b/lang/en.json index 6060a141..06ababf5 100755 --- a/lang/en.json +++ b/lang/en.json @@ -2135,6 +2135,10 @@ "plural": "Experiences" }, "failure": "Failure", + "favorite": { + "single": "Favorite", + "plural": "Favorites" + }, "fear": "Fear", "features": "Features", "formula": "Formula", diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index 594269be..e7733826 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -33,7 +33,7 @@ export default class CharacterSheet extends DHBaseActorSheet { advanceResourceDie: CharacterSheet.#advanceResourceDie, cancelBeastform: CharacterSheet.#cancelBeastform, useDowntime: this.useDowntime, - viewParty: CharacterSheet.#viewParty, + viewParty: CharacterSheet.#viewParty }, window: { resizable: true, @@ -49,6 +49,10 @@ export default class CharacterSheet extends DHBaseActorSheet { { dragSelector: '[data-item-id][draggable="true"], [data-item-id] [draggable="true"]', dropSelector: null + }, + { + dragSelector: null, + dropSelector: '.character-sidebar-sheet' } ], contextMenus: [ @@ -261,6 +265,24 @@ export default class CharacterSheet extends DHBaseActorSheet { */ async _prepareSidebarContext(context, _options) { context.isDeath = this.document.system.deathMoveViable; + context.sidebarFavoritesEmpty = this.document.system.sidebarFavorites.length === 0; + + const initialFavorites = this.document.system.usedUnarmed + ? { + equipment: { + label: 'DAGGERHEART.GENERAL.equipment', + items: [{ type: 'attack', value: this.document.system.usedUnarmed }] + } + } + : {}; + context.sidebarFavorites = this.document.system.sidebarFavorites.reduce((acc, item) => { + const type = item.type === 'domainCard' ? item.type : 'equipment'; + const label = type === 'domainCard' ? 'DAGGERHEART.GENERAL.loadout' : 'DAGGERHEART.GENERAL.equipment'; + if (!acc[type]) acc[type] = { label, items: [] }; + acc[type].items.push({ type: item.type, value: item }); + + return acc; + }, initialFavorites); } /** @@ -310,7 +332,9 @@ export default class CharacterSheet extends DHBaseActorSheet { icon: 'fa-solid fa-arrow-up', condition: target => { const doc = getDocFromElementSync(target); - return doc && doc.system.inVault; + const inCharacterSidebar = + this.document.type === 'character' && target.closest('.items-sidebar-list'); + return doc && doc.system.inVault && !inCharacterSidebar; }, callback: async target => { const doc = await getDocFromElement(target); @@ -324,7 +348,9 @@ export default class CharacterSheet extends DHBaseActorSheet { icon: 'fa-solid fa-bolt-lightning', condition: target => { const doc = getDocFromElementSync(target); - return doc && doc.system.inVault; + const inCharacterSidebar = + this.document.type === 'character' && target.closest('.items-sidebar-list'); + return doc && doc.system.inVault && !inCharacterSidebar; }, callback: async (target, event) => { const doc = await getDocFromElement(target); @@ -338,15 +364,20 @@ export default class CharacterSheet extends DHBaseActorSheet { } const type = 'effect'; const cls = game.system.api.models.actions.actionsTypes[type]; - const action = new cls({ - ...cls.getSourceConfig(doc.system), - type: type, - chatDisplay: false, - cost: [{ - key: 'stress', - value: doc.system.recallCost - }] - }, { parent: doc.system }); + const action = new cls( + { + ...cls.getSourceConfig(doc.system), + type: type, + chatDisplay: false, + cost: [ + { + key: 'stress', + value: doc.system.recallCost + } + ] + }, + { parent: doc.system } + ); const config = await action.use(event); if (config) { return doc.update({ 'system.inVault': false }); @@ -358,7 +389,9 @@ export default class CharacterSheet extends DHBaseActorSheet { icon: 'fa-solid fa-arrow-down', condition: target => { const doc = getDocFromElementSync(target); - return doc && !doc.system.inVault; + const inCharacterSidebar = + this.document.type === 'character' && target.closest('.items-sidebar-list'); + return doc && !doc.system.inVault && !inCharacterSidebar; }, callback: async target => (await getDocFromElement(target)).update({ 'system.inVault': true }) } @@ -900,32 +933,32 @@ export default class CharacterSheet extends DHBaseActorSheet { return; } - const buttons = parties.map((p) => { - const button = document.createElement("button"); - button.type = "button"; - button.classList.add("plain"); - const img = document.createElement("img"); + const buttons = parties.map(p => { + const button = document.createElement('button'); + button.type = 'button'; + button.classList.add('plain'); + const img = document.createElement('img'); img.src = p.img; button.append(img); - const name = document.createElement("span"); + const name = document.createElement('span'); name.textContent = p.name; button.append(name); - button.addEventListener("click", () => { + button.addEventListener('click', () => { p.sheet?.render({ force: true }); game.tooltip.dismissLockedTooltips(); }); return button; }); - const html = document.createElement("div"); - html.classList.add("party-list"); + const html = document.createElement('div'); + html.classList.add('party-list'); html.append(...buttons); - + game.tooltip.dismissLockedTooltips(); game.tooltip.activate(target, { html, - locked: true, - }) + locked: true + }); } /** @@ -948,6 +981,11 @@ export default class CharacterSheet extends DHBaseActorSheet { } async _onDropItem(event, item) { + const sidebarDrop = event.target.closest('.character-sidebar-sheet'); + if (sidebarDrop) { + return this._onSidebarDrop(event, item); + } + if (this.document.uuid === item.parent?.uuid) { return super._onDropItem(event, item); } @@ -986,4 +1024,18 @@ export default class CharacterSheet extends DHBaseActorSheet { itemData = itemData instanceof Array ? itemData : [itemData]; return this.document.createEmbeddedDocuments('Item', itemData); } + + async _onSidebarDrop(event, item) { + if (!item.testUserPermission(game.user, 'OWNER')) return; + + if (!(item instanceof game.system.api.documents.DHItem)) return; + if (!this.document.items.get(item.id)) return; + + const allowedItemTypes = ['domainCard', 'feature', 'weapon', 'armor', 'loot', 'consumable']; + if (!allowedItemTypes.includes(item.type)) return; + + if (this.document.system.sidebarFavorites.some(x => x.id === item.id)) return; + + this.document.update({ 'system.sidebarFavorites': [...this.document.system.sidebarFavorites, item] }); + } } diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index 903caa2a..2d83b54b 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -539,6 +539,39 @@ export default function DHApplicationMixin(Base) { } }); + options.push({ + name: 'Unfavorite', + icon: 'fa-regular fa-star', + condition: target => { + return this.document.type === 'character' && target.closest('.items-sidebar-list'); + }, + callback: async (target, _event) => { + const doc = await getDocFromElement(target); + this.document.update({ + 'system.sidebarFavorites': this.document.system.sidebarFavorites.filter(x => x.id !== doc.id) + }); + } + }); + + options.push({ + name: 'Favorite', + icon: 'fa-solid fa-star', + condition: target => { + const doc = getDocFromElementSync(target); + return ( + !(doc instanceof game.system.api.documents.DhActiveEffect) && + this.document.type === 'character' && + !target.closest('.items-sidebar-list') + ); + }, + callback: async (target, _event) => { + const doc = await getDocFromElement(target); + this.document.update({ + 'system.sidebarFavorites': [...this.document.system.sidebarFavorites, doc] + }); + } + }); + return options.map(option => ({ ...option, icon: `` diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index eba46f10..4bb47ebc 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -5,6 +5,7 @@ import BaseDataActor, { commonActorRules } from './base.mjs'; import { attributeField, resourceField, stressDamageReductionRule, bonusField } from '../fields/actorField.mjs'; import { ActionField } from '../fields/actionField.mjs'; import DHCharacterSettings from '../../applications/sheets-configs/character-settings.mjs'; +import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs'; export default class DhCharacter extends BaseDataActor { /**@override */ @@ -284,7 +285,8 @@ export default class DhCharacter extends BaseDataActor { burden: new fields.SchemaField({ ignore: new fields.BooleanField() }) - }) + }), + sidebarFavorites: new ForeignDocumentUUIDArrayField({ type: 'Item' }) }; } diff --git a/styles/less/sheets/actors/character/sidebar.less b/styles/less/sheets/actors/character/sidebar.less index 04baf2b9..976dffcd 100644 --- a/styles/less/sheets/actors/character/sidebar.less +++ b/styles/less/sheets/actors/character/sidebar.less @@ -561,6 +561,25 @@ overflow-y: auto; scrollbar-color: light-dark(@dark-blue, @golden) transparent; } + + .empty-favorites { + width: 100%; + display: flex; + align-items: center; + justify-content: center; + box-sizing: border-box; + height: 40px; + border: 1px dashed light-dark(@dark-blue-50, @beige-50); + border-radius: 3px; + color: light-dark(@dark-blue-50, @beige-50); + text-align: center; + margin-bottom: 10px; + margin-left: 4px; + + span { + width: 250px; + } + } } .equipment-section, diff --git a/templates/sheets/actors/character/sidebar.hbs b/templates/sheets/actors/character/sidebar.hbs index 0db2bf42..58827518 100644 --- a/templates/sheets/actors/character/sidebar.hbs +++ b/templates/sheets/actors/character/sidebar.hbs @@ -96,45 +96,27 @@
-
-
- -

{{localize "DAGGERHEART.GENERAL.equipment"}}

- -
-
    - {{#if document.system.usedUnarmed}} - {{> 'daggerheart.inventory-item-compact' - item=document.system.usedUnarmed - type='attack' - }} - {{/if}} - {{#each document.items as |item|}} - {{#if item.system.equipped}} - {{> 'daggerheart.inventory-item-compact' - item=item - type=item.type - }} - {{/if}} - {{/each}} -
-
-
-
- -

{{localize "DAGGERHEART.GENERAL.loadout"}}

- -
-
    - {{#each document.system.domainCards.loadout as |card|}} - {{> 'daggerheart.inventory-item-compact' - item=card - type='domainCard' - }} - - {{/each}} -
-
+ {{#if sidebarFavoritesEmpty}} +
{{localize "DAGGERHEART.GENERAL.favorite.plural"}}
+ {{else}} + {{#each sidebarFavorites as |category|}} +
+
+ +

{{localize category.label}}

+ +
+
    + {{#each category.items as |item|}} + {{> 'daggerheart.inventory-item-compact' + item=item.value + type=item.type + }} + {{/each}} +
+
+ {{/each}} + {{/if}}
diff --git a/templates/sheets/global/partials/inventory-item-compact.hbs b/templates/sheets/global/partials/inventory-item-compact.hbs index bbdf9116..6f7205fa 100644 --- a/templates/sheets/global/partials/inventory-item-compact.hbs +++ b/templates/sheets/global/partials/inventory-item-compact.hbs @@ -58,11 +58,6 @@ data-tooltip="DAGGERHEART.UI.Tooltip.{{ifThen item.system.equipped 'unequip' 'equip' }}"> - {{else if (eq type 'domainCard')}} - - - {{else if (eq type 'effect')}} From 2e4d3de3797bd8ed881c586b41344e58f13915b9 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Mon, 12 Jan 2026 20:52:50 +0100 Subject: [PATCH 2/9] . --- lang/en.json | 3 +- .../applications/sheets/actors/character.mjs | 31 ++++---- module/data/item/domainCard.mjs | 20 +++++ .../less/sheets/actors/character/sidebar.less | 4 +- templates/sheets/actors/character/sidebar.hbs | 76 ++++++++++++------- 5 files changed, 90 insertions(+), 44 deletions(-) diff --git a/lang/en.json b/lang/en.json index 06ababf5..cb297398 100755 --- a/lang/en.json +++ b/lang/en.json @@ -231,7 +231,8 @@ "viewParty": "View Party", "InvalidOldCharacterImportTitle": "Old Character Import", "InvalidOldCharacterImportText": "Character data exported prior to system version 1.1 will not generate a complete character. Do you wish to continue?", - "cancelBeastform": "Cancel Beastform" + "cancelBeastform": "Cancel Beastform", + "sidebarFavoritesHint": "Drag items, features and domain cards from the sheet to here" }, "Companion": { "FIELDS": { diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index e7733826..1be3c122 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -266,23 +266,24 @@ export default class CharacterSheet extends DHBaseActorSheet { async _prepareSidebarContext(context, _options) { context.isDeath = this.document.system.deathMoveViable; context.sidebarFavoritesEmpty = this.document.system.sidebarFavorites.length === 0; + context.showfavorites = !context.sidebarFavoritesEmpty || this.document.system.usedUnarmed; - const initialFavorites = this.document.system.usedUnarmed - ? { - equipment: { - label: 'DAGGERHEART.GENERAL.equipment', - items: [{ type: 'attack', value: this.document.system.usedUnarmed }] - } - } - : {}; - context.sidebarFavorites = this.document.system.sidebarFavorites.reduce((acc, item) => { - const type = item.type === 'domainCard' ? item.type : 'equipment'; - const label = type === 'domainCard' ? 'DAGGERHEART.GENERAL.loadout' : 'DAGGERHEART.GENERAL.equipment'; - if (!acc[type]) acc[type] = { label, items: [] }; - acc[type].items.push({ type: item.type, value: item }); + // const initialFavorites = this.document.system.usedUnarmed + // ? { + // equipment: { + // label: 'DAGGERHEART.GENERAL.equipment', + // items: [{ type: 'attack', value: this.document.system.usedUnarmed }] + // } + // } + // : {}; + // context.sidebarFavorites = this.document.system.sidebarFavorites.reduce((acc, item) => { + // const type = item.type === 'domainCard' ? item.type : 'equipment'; + // const label = type === 'domainCard' ? 'DAGGERHEART.GENERAL.loadout' : 'DAGGERHEART.GENERAL.equipment'; + // if (!acc[type]) acc[type] = { label, items: [] }; + // acc[type].items.push({ type: item.type, value: item }); - return acc; - }, initialFavorites); + // return acc; + // }, initialFavorites); } /** diff --git a/module/data/item/domainCard.mjs b/module/data/item/domainCard.mjs index 92d8828c..571d682c 100644 --- a/module/data/item/domainCard.mjs +++ b/module/data/item/domainCard.mjs @@ -45,6 +45,26 @@ 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/styles/less/sheets/actors/character/sidebar.less b/styles/less/sheets/actors/character/sidebar.less index 976dffcd..0f710055 100644 --- a/styles/less/sheets/actors/character/sidebar.less +++ b/styles/less/sheets/actors/character/sidebar.less @@ -552,7 +552,6 @@ .shortcut-items-section { overflow-y: hidden; padding-top: 10px; - padding-bottom: 20px; mask-image: linear-gradient(0deg, transparent 0%, black 5%); scrollbar-gutter: stable; scrollbar-width: thin; @@ -565,16 +564,17 @@ .empty-favorites { width: 100%; display: flex; + flex-direction: column; align-items: center; justify-content: center; box-sizing: border-box; - height: 40px; border: 1px dashed light-dark(@dark-blue-50, @beige-50); border-radius: 3px; color: light-dark(@dark-blue-50, @beige-50); text-align: center; margin-bottom: 10px; margin-left: 4px; + padding: 4px 0; span { width: 250px; diff --git a/templates/sheets/actors/character/sidebar.hbs b/templates/sheets/actors/character/sidebar.hbs index 58827518..e14daad2 100644 --- a/templates/sheets/actors/character/sidebar.hbs +++ b/templates/sheets/actors/character/sidebar.hbs @@ -96,10 +96,36 @@
- {{#if sidebarFavoritesEmpty}} -
{{localize "DAGGERHEART.GENERAL.favorite.plural"}}
- {{else}} - {{#each sidebarFavorites as |category|}} +
+
+ +

{{localize "DAGGERHEART.GENERAL.favorite.plural"}}

+ +
+
    + {{#if this.document.system.usedUnarmed}} + {{> 'daggerheart.inventory-item-compact' + item=this.document.system.usedUnarmed + type="attack" + }} + {{/if}} + {{#each this.document.system.sidebarFavorites as |item|}} + {{> 'daggerheart.inventory-item-compact' + item=item + type=item.type + }} + {{/each}} +
+ {{#if sidebarFavoritesEmpty}} +
+ {{localize "DAGGERHEART.ACTORS.Character.sidebarFavoritesHint"}} +
+ {{/if}} +
+
+ + + {{!-- {{#each sidebarFavorites as |category|}}
@@ -115,29 +141,27 @@ {{/each}}
- {{/each}} - {{/if}} -
-
- -

{{localize "DAGGERHEART.GENERAL.experience.single"}}

- -
-
- {{#each document.system.experiences as |experience id|}} -
- - +{{experience.value}} - - {{experience.name}} - + {{/each}} --}} +
+
+ +

{{localize "DAGGERHEART.GENERAL.experience.single"}}

+ +
+
+ {{#each document.system.experiences as |experience id|}} +
+ + +{{experience.value}} + + {{experience.name}} + - {{/each}} -
+
+ {{/each}}
\ No newline at end of file From ffdb73e1e94ee1f8b80e45552cf8a412c709f6c2 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Mon, 12 Jan 2026 22:45:12 +0100 Subject: [PATCH 3/9] . --- .../applications/sheets/actors/character.mjs | 74 +++++++++++++------ .../sheets/api/application-mixin.mjs | 13 +++- module/data/actor/character.mjs | 7 +- templates/sheets/actors/character/sidebar.hbs | 64 ++++++---------- 4 files changed, 92 insertions(+), 66 deletions(-) diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index 1be3c122..a6598b74 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -267,23 +267,7 @@ export default class CharacterSheet extends DHBaseActorSheet { context.isDeath = this.document.system.deathMoveViable; context.sidebarFavoritesEmpty = this.document.system.sidebarFavorites.length === 0; context.showfavorites = !context.sidebarFavoritesEmpty || this.document.system.usedUnarmed; - - // const initialFavorites = this.document.system.usedUnarmed - // ? { - // equipment: { - // label: 'DAGGERHEART.GENERAL.equipment', - // items: [{ type: 'attack', value: this.document.system.usedUnarmed }] - // } - // } - // : {}; - // context.sidebarFavorites = this.document.system.sidebarFavorites.reduce((acc, item) => { - // const type = item.type === 'domainCard' ? item.type : 'equipment'; - // const label = type === 'domainCard' ? 'DAGGERHEART.GENERAL.loadout' : 'DAGGERHEART.GENERAL.equipment'; - // if (!acc[type]) acc[type] = { label, items: [] }; - // acc[type].items.push({ type: item.type, value: item }); - - // return acc; - // }, initialFavorites); + context.sidebarFavorites = this.document.system.sidebarFavorites.sort((a, b) => a.sort - b.sort); } /** @@ -978,7 +962,12 @@ export default class CharacterSheet extends DHBaseActorSheet { if (inventoryItem) { event.dataTransfer.setDragImage(inventoryItem.querySelector('img'), 60, 0); } - super._onDragStart(event); + + await super._onDragStart(event); + const baseDragData = foundry.applications.ux.TextEditor.getDragEventData(event); + + const sidebarReorder = Boolean(event.target.closest('.items-sidebar-list')); + event.dataTransfer.setData('text/plain', JSON.stringify({ ...baseDragData, sidebarReorder })); } async _onDropItem(event, item) { @@ -1027,16 +1016,57 @@ export default class CharacterSheet extends DHBaseActorSheet { } async _onSidebarDrop(event, item) { - if (!item.testUserPermission(game.user, 'OWNER')) return; - if (!(item instanceof game.system.api.documents.DHItem)) return; if (!this.document.items.get(item.id)) return; + const data = foundry.applications.ux.TextEditor.getDragEventData(event); + if (data.sidebarReorder) { + return this.document.update({ 'system.sidebarFavorites': this.getDropSortedFavorites(event, item) }); + } + + if (!item.testUserPermission(game.user, 'OWNER')) return; + const allowedItemTypes = ['domainCard', 'feature', 'weapon', 'armor', 'loot', 'consumable']; if (!allowedItemTypes.includes(item.type)) return; - if (this.document.system.sidebarFavorites.some(x => x.id === item.id)) return; + if (this.document.system.sidebarFavorites.some(x => x.item.id === item.id)) return; - this.document.update({ 'system.sidebarFavorites': [...this.document.system.sidebarFavorites, item] }); + const nextSort = this.document.system.sidebarFavorites.length + ? this.document.system.sidebarFavorites.reduce((acc, curr) => Math.max(curr.sort, acc), 0) + 100000 + : 0; + this.document.update({ + 'system.sidebarFavorites': [ + ...this.document.system.sidebarFavorites.map(x => ({ ...x, item: x.item.uuid })), + { item, sort: nextSort } + ] + }); + } + + getDropSortedFavorites(event, item) { + const dropTarget = event.target.closest('[data-item-id]'); + if (!dropTarget) return; + + const siblings = []; + for (const element of dropTarget.parentElement.children) { + const siblingId = element.dataset.itemId; + if (siblingId && siblingId !== item.id && element.dataset.type !== 'attack') + siblings.push(this.document.system.sidebarFavorites.find(x => x.item.id === element.dataset.itemId)); + } + + const source = this.document.system.sidebarFavorites.find(x => x.item.id === item.id); + const target = this.document.system.sidebarFavorites.find(x => x.item.id === dropTarget.dataset.itemId); + const sortUpdates = foundry.utils.performIntegerSort(source, { target, siblings }); + const updates = sortUpdates.map(u => ({ + sort: u.update.sort, + item: u.target?.uuid ?? u.target.item.uuid + })); + + const test = this.document.system.sidebarFavorites.reduce((acc, curr) => { + if (acc.some(x => x.item === curr.item.uuid)) return acc; + acc.push({ ...curr, item: curr.item.uuid }); + + return acc; + }, updates); + return test; } } diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index 2d83b54b..d342bae1 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -548,7 +548,9 @@ export default function DHApplicationMixin(Base) { callback: async (target, _event) => { const doc = await getDocFromElement(target); this.document.update({ - 'system.sidebarFavorites': this.document.system.sidebarFavorites.filter(x => x.id !== doc.id) + 'system.sidebarFavorites': this.document.system.sidebarFavorites + .filter(x => x.item.id !== doc.id) + .map(x => ({ ...x, item: x.item.uuid })) }); } }); @@ -566,8 +568,15 @@ export default function DHApplicationMixin(Base) { }, callback: async (target, _event) => { const doc = await getDocFromElement(target); + const nextSort = this.document.system.sidebarFavorites.length + ? this.document.system.sidebarFavorites.reduce((acc, curr) => Math.max(curr.sort, acc), 0) + + 100000 + : 0; this.document.update({ - 'system.sidebarFavorites': [...this.document.system.sidebarFavorites, doc] + 'system.sidebarFavorites': [ + ...this.document.system.sidebarFavorites.map(x => ({ ...x, item: x.item.uuid })), + { sort: nextSort, item: doc.uuid } + ] }); } }); diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index 4bb47ebc..19e053e2 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -286,7 +286,12 @@ export default class DhCharacter extends BaseDataActor { ignore: new fields.BooleanField() }) }), - sidebarFavorites: new ForeignDocumentUUIDArrayField({ type: 'Item' }) + sidebarFavorites: new fields.ArrayField( + new fields.SchemaField({ + sort: new fields.NumberField({ required: true, nullable: false, integer: true }), + item: new ForeignDocumentUUIDField({ type: 'Item' }) + }) + ) }; } diff --git a/templates/sheets/actors/character/sidebar.hbs b/templates/sheets/actors/character/sidebar.hbs index e14daad2..d0528469 100644 --- a/templates/sheets/actors/character/sidebar.hbs +++ b/templates/sheets/actors/character/sidebar.hbs @@ -109,10 +109,10 @@ type="attack" }} {{/if}} - {{#each this.document.system.sidebarFavorites as |item|}} + {{#each sidebarFavorites as |data|}} {{> 'daggerheart.inventory-item-compact' - item=item - type=item.type + item=data.item + type=data.item.type }} {{/each}} @@ -122,46 +122,28 @@
{{/if}}
-
- - {{!-- {{#each sidebarFavorites as |category|}} -
-
- -

{{localize category.label}}

- +
+
+ +

{{localize "DAGGERHEART.GENERAL.experience.single"}}

+ +
+
+ {{#each document.system.experiences as |experience id|}} +
+ + +{{experience.value}} + + {{experience.name}} +
+ + + +
-
    - {{#each category.items as |item|}} - {{> 'daggerheart.inventory-item-compact' - item=item.value - type=item.type - }} - {{/each}} -
-
- {{/each}} --}} -
-
- -

{{localize "DAGGERHEART.GENERAL.experience.single"}}

- -
-
- {{#each document.system.experiences as |experience id|}} -
- - +{{experience.value}} - - {{experience.name}} -
- - - -
-
- {{/each}} + {{/each}} +
\ No newline at end of file From 201d3c3f3cb73f575815862f9965f8190f84e4cc Mon Sep 17 00:00:00 2001 From: WBHarry Date: Mon, 12 Jan 2026 23:00:55 +0100 Subject: [PATCH 4/9] Fixed sorting --- .../applications/sheets/actors/character.mjs | 48 +++++++++---------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index a6598b74..5a404de0 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -1016,44 +1016,40 @@ export default class CharacterSheet extends DHBaseActorSheet { } async _onSidebarDrop(event, item) { + if (!item.testUserPermission(game.user, 'OWNER')) return; + if (!(item instanceof game.system.api.documents.DHItem)) return; if (!this.document.items.get(item.id)) return; - const data = foundry.applications.ux.TextEditor.getDragEventData(event); - if (data.sidebarReorder) { - return this.document.update({ 'system.sidebarFavorites': this.getDropSortedFavorites(event, item) }); - } - - if (!item.testUserPermission(game.user, 'OWNER')) return; - const allowedItemTypes = ['domainCard', 'feature', 'weapon', 'armor', 'loot', 'consumable']; if (!allowedItemTypes.includes(item.type)) return; - if (this.document.system.sidebarFavorites.some(x => x.item.id === item.id)) return; + const siblings = this.document.system.sidebarFavorites.filter(x => x.item.id !== item.id); + let source = null; - const nextSort = this.document.system.sidebarFavorites.length - ? this.document.system.sidebarFavorites.reduce((acc, curr) => Math.max(curr.sort, acc), 0) + 100000 - : 0; - this.document.update({ - 'system.sidebarFavorites': [ - ...this.document.system.sidebarFavorites.map(x => ({ ...x, item: x.item.uuid })), - { item, sort: nextSort } - ] - }); + const data = foundry.applications.ux.TextEditor.getDragEventData(event); + if (data.sidebarReorder) { + source = this.document.system.sidebarFavorites.find(x => x.item.id === item.id); + } else { + if (this.document.system.sidebarFavorites.some(x => x.item.id === item.id)) return; + source = { sort: null, item }; + } + + const update = siblings.length + ? this.getDropSortedFavorites(event, source, siblings) + : [ + ...this.document.system.sidebarFavorites.map(x => ({ ...x, item: x.item.uuid })), + { item, sort: 100000 } + ]; + if (!update) return; + + this.document.update({ 'system.sidebarFavorites': update }); } - getDropSortedFavorites(event, item) { + getDropSortedFavorites(event, source, siblings) { const dropTarget = event.target.closest('[data-item-id]'); if (!dropTarget) return; - const siblings = []; - for (const element of dropTarget.parentElement.children) { - const siblingId = element.dataset.itemId; - if (siblingId && siblingId !== item.id && element.dataset.type !== 'attack') - siblings.push(this.document.system.sidebarFavorites.find(x => x.item.id === element.dataset.itemId)); - } - - const source = this.document.system.sidebarFavorites.find(x => x.item.id === item.id); const target = this.document.system.sidebarFavorites.find(x => x.item.id === dropTarget.dataset.itemId); const sortUpdates = foundry.utils.performIntegerSort(source, { target, siblings }); const updates = sortUpdates.map(u => ({ From 7614001bddf691c0870673140009d6dffb8dc370 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Tue, 13 Jan 2026 00:35:54 +0100 Subject: [PATCH 5/9] Reverted sorting --- .../applications/sheets/actors/character.mjs | 50 ++----------------- .../sheets/api/application-mixin.mjs | 13 +---- module/data/actor/character.mjs | 7 +-- templates/sheets/actors/character/sidebar.hbs | 48 +++++++++--------- 4 files changed, 31 insertions(+), 87 deletions(-) diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index 5a404de0..db97de64 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -267,7 +267,7 @@ export default class CharacterSheet extends DHBaseActorSheet { context.isDeath = this.document.system.deathMoveViable; context.sidebarFavoritesEmpty = this.document.system.sidebarFavorites.length === 0; context.showfavorites = !context.sidebarFavoritesEmpty || this.document.system.usedUnarmed; - context.sidebarFavorites = this.document.system.sidebarFavorites.sort((a, b) => a.sort - b.sort); + context.sidebarFavorites = this.document.system.sidebarFavorites.sort((a, b) => a.name.localeCompare(b.name)); } /** @@ -962,12 +962,7 @@ export default class CharacterSheet extends DHBaseActorSheet { if (inventoryItem) { event.dataTransfer.setDragImage(inventoryItem.querySelector('img'), 60, 0); } - - await super._onDragStart(event); - const baseDragData = foundry.applications.ux.TextEditor.getDragEventData(event); - - const sidebarReorder = Boolean(event.target.closest('.items-sidebar-list')); - event.dataTransfer.setData('text/plain', JSON.stringify({ ...baseDragData, sidebarReorder })); + super._onDragStart(event); } async _onDropItem(event, item) { @@ -1024,45 +1019,8 @@ export default class CharacterSheet extends DHBaseActorSheet { const allowedItemTypes = ['domainCard', 'feature', 'weapon', 'armor', 'loot', 'consumable']; if (!allowedItemTypes.includes(item.type)) return; - const siblings = this.document.system.sidebarFavorites.filter(x => x.item.id !== item.id); - let source = null; + if (this.document.system.sidebarFavorites.some(x => x.id === item.id)) return; - const data = foundry.applications.ux.TextEditor.getDragEventData(event); - if (data.sidebarReorder) { - source = this.document.system.sidebarFavorites.find(x => x.item.id === item.id); - } else { - if (this.document.system.sidebarFavorites.some(x => x.item.id === item.id)) return; - source = { sort: null, item }; - } - - const update = siblings.length - ? this.getDropSortedFavorites(event, source, siblings) - : [ - ...this.document.system.sidebarFavorites.map(x => ({ ...x, item: x.item.uuid })), - { item, sort: 100000 } - ]; - if (!update) return; - - this.document.update({ 'system.sidebarFavorites': update }); - } - - getDropSortedFavorites(event, source, siblings) { - const dropTarget = event.target.closest('[data-item-id]'); - if (!dropTarget) return; - - const target = this.document.system.sidebarFavorites.find(x => x.item.id === dropTarget.dataset.itemId); - const sortUpdates = foundry.utils.performIntegerSort(source, { target, siblings }); - const updates = sortUpdates.map(u => ({ - sort: u.update.sort, - item: u.target?.uuid ?? u.target.item.uuid - })); - - const test = this.document.system.sidebarFavorites.reduce((acc, curr) => { - if (acc.some(x => x.item === curr.item.uuid)) return acc; - acc.push({ ...curr, item: curr.item.uuid }); - - return acc; - }, updates); - return test; + this.document.update({ 'system.sidebarFavorites': [...this.document.system.sidebarFavorites, item] }); } } diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index d342bae1..2d83b54b 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -548,9 +548,7 @@ export default function DHApplicationMixin(Base) { callback: async (target, _event) => { const doc = await getDocFromElement(target); this.document.update({ - 'system.sidebarFavorites': this.document.system.sidebarFavorites - .filter(x => x.item.id !== doc.id) - .map(x => ({ ...x, item: x.item.uuid })) + 'system.sidebarFavorites': this.document.system.sidebarFavorites.filter(x => x.id !== doc.id) }); } }); @@ -568,15 +566,8 @@ export default function DHApplicationMixin(Base) { }, callback: async (target, _event) => { const doc = await getDocFromElement(target); - const nextSort = this.document.system.sidebarFavorites.length - ? this.document.system.sidebarFavorites.reduce((acc, curr) => Math.max(curr.sort, acc), 0) + - 100000 - : 0; this.document.update({ - 'system.sidebarFavorites': [ - ...this.document.system.sidebarFavorites.map(x => ({ ...x, item: x.item.uuid })), - { sort: nextSort, item: doc.uuid } - ] + 'system.sidebarFavorites': [...this.document.system.sidebarFavorites, doc] }); } }); diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index 19e053e2..4bb47ebc 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -286,12 +286,7 @@ export default class DhCharacter extends BaseDataActor { ignore: new fields.BooleanField() }) }), - sidebarFavorites: new fields.ArrayField( - new fields.SchemaField({ - sort: new fields.NumberField({ required: true, nullable: false, integer: true }), - item: new ForeignDocumentUUIDField({ type: 'Item' }) - }) - ) + sidebarFavorites: new ForeignDocumentUUIDArrayField({ type: 'Item' }) }; } diff --git a/templates/sheets/actors/character/sidebar.hbs b/templates/sheets/actors/character/sidebar.hbs index d0528469..758af1b9 100644 --- a/templates/sheets/actors/character/sidebar.hbs +++ b/templates/sheets/actors/character/sidebar.hbs @@ -109,10 +109,10 @@ type="attack" }} {{/if}} - {{#each sidebarFavorites as |data|}} + {{#each sidebarFavorites as |item|}} {{> 'daggerheart.inventory-item-compact' - item=data.item - type=data.item.type + item=item + type=item.type }} {{/each}} @@ -122,28 +122,28 @@
{{/if}}
- -
-
- -

{{localize "DAGGERHEART.GENERAL.experience.single"}}

- -
-
- {{#each document.system.experiences as |experience id|}} -
- - +{{experience.value}} - - {{experience.name}} -
- - - -
+
+ +
+
+ +

{{localize "DAGGERHEART.GENERAL.experience.single"}}

+ +
+
+ {{#each document.system.experiences as |experience id|}} +
+ + +{{experience.value}} + + {{experience.name}} + - {{/each}} -
+
+ {{/each}}
\ No newline at end of file From ee89d5cb9e279f40ffd5803be93cab1dd6321ba8 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Tue, 27 Jan 2026 16:45:45 +0100 Subject: [PATCH 6/9] 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 7/9] 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 8/9] 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 9/9] . --- .../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);