From b4fff7b9e6b88eda14e56b0a3fe6df2beee76585 Mon Sep 17 00:00:00 2001
From: WBHarry <89362246+WBHarry@users.noreply.github.com>
Date: Sat, 28 Jun 2025 10:05:53 +0200
Subject: [PATCH 2/2] 174 character context menu (#189)
* Set up the contextMenues properly
* Fixed double item generation on drop
---
lang/en.json | 11 +
module/applications/sheets/character.mjs | 195 +++++++++++++-----
module/documents/item.mjs | 28 +++
styles/chat.less | 1 +
styles/daggerheart.css | 1 +
templates/chat/ability-use.hbs | 1 -
templates/sheets/actors/character/loadout.hbs | 2 +-
templates/sheets/actors/character/sidebar.hbs | 2 +-
.../global/partials/domain-card-item.hbs | 6 +-
.../partials/inventory-fieldset-items.hbs | 2 +-
.../sheets/global/partials/inventory-item.hbs | 10 +-
11 files changed, 197 insertions(+), 62 deletions(-)
diff --git a/lang/en.json b/lang/en.json
index 205a77e4..9a614b68 100755
--- a/lang/en.json
+++ b/lang/en.json
@@ -1099,6 +1099,17 @@
"biography": "Biography",
"effects": "Effects"
},
+ "ContextMenu": {
+ "UseItem": "Use",
+ "Equip": "Equip",
+ "Unequip": "Unequip",
+ "Edit": "Edit",
+ "Delete": "Delete",
+ "ToLoadout": "Send to Loadout",
+ "ToVault": "Send to Vault",
+ "Consume": "Consume Item",
+ "SendToChat": "Send To Chat"
+ },
"Armor": {
"Title": "Active Armor"
},
diff --git a/module/applications/sheets/character.mjs b/module/applications/sheets/character.mjs
index 82679ccd..665c203e 100644
--- a/module/applications/sheets/character.mjs
+++ b/module/applications/sheets/character.mjs
@@ -43,10 +43,11 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
makeDeathMove: this.makeDeathMove,
itemQuantityDecrease: (_, button) => this.setItemQuantity(button, -1),
itemQuantityIncrease: (_, button) => this.setItemQuantity(button, 1),
- useAbility: this.useAbility,
+ toChat: this.toChat,
useAdvancementCard: this.useAdvancementCard,
useAdvancementAbility: this.useAdvancementAbility,
toggleEquipItem: this.toggleEquipItem,
+ toggleVault: this.toggleVault,
levelManagement: this.levelManagement,
editImage: this._onEditImage
},
@@ -58,11 +59,7 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
submitOnChange: true,
closeOnSubmit: false
},
- dragDrop: [
- { dragSelector: null, dropSelector: '.weapon-section' },
- { dragSelector: null, dropSelector: '.armor-section' },
- { dragSelector: '.item-list .item', dropSelector: null }
- ]
+ dragDrop: []
};
static PARTS = {
@@ -214,6 +211,109 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
return { primary: primaryTabs, secondary: secondaryTabs };
}
+ async _onFirstRender(context, options) {
+ await super._onFirstRender(context, options);
+
+ this._createContextMenues();
+ }
+
+ _createContextMenues() {
+ const allOptions = {
+ useItem: {
+ name: 'DAGGERHEART.Sheets.PC.ContextMenu.UseItem',
+ icon: '
',
+ callback: this.constructor.useItem.bind(this)
+ },
+ equip: {
+ name: 'DAGGERHEART.Sheets.PC.ContextMenu.Equip',
+ icon: '
',
+ condition: el => {
+ const item = foundry.utils.fromUuidSync(el.dataset.uuid);
+ return !item.system.equipped;
+ },
+ callback: this.constructor.toggleEquipItem.bind(this)
+ },
+ unequip: {
+ name: 'DAGGERHEART.Sheets.PC.ContextMenu.Unequip',
+ icon: '
',
+ condition: el => {
+ const item = foundry.utils.fromUuidSync(el.dataset.uuid);
+ return item.system.equipped;
+ },
+ callback: this.constructor.toggleEquipItem.bind(this)
+ },
+ edit: {
+ name: 'DAGGERHEART.Sheets.PC.ContextMenu.Edit',
+ icon: '
',
+ callback: this.constructor.viewObject.bind(this)
+ },
+ delete: {
+ name: 'DAGGERHEART.Sheets.PC.ContextMenu.Delete',
+ icon: '
',
+ callback: this.constructor.deleteItem.bind(this)
+ },
+ sendToLoadout: {
+ name: 'DAGGERHEART.Sheets.PC.ContextMenu.ToLoadout',
+ icon: '
',
+ condition: el => {
+ const item = foundry.utils.fromUuidSync(el.dataset.uuid);
+ return item.system.inVault;
+ },
+ callback: this.constructor.toggleVault.bind(this)
+ },
+ sendToVault: {
+ name: 'DAGGERHEART.Sheets.PC.ContextMenu.ToVault',
+ icon: '
',
+ condition: el => {
+ const item = foundry.utils.fromUuidSync(el.dataset.uuid);
+ return !item.system.inVault;
+ },
+ callback: this.constructor.toggleVault.bind(this)
+ },
+ sendToChat: {
+ name: 'DAGGERHEART.Sheets.PC.ContextMenu.SendToChat',
+ icon: '
',
+ callback: this.constructor.toChat.bind(this)
+ }
+ };
+
+ const getMenuOptions = type => () => {
+ let menuItems = ['class', 'subclass'].includes(type) ? [] : [allOptions.useItem];
+ switch (type) {
+ case 'weapon':
+ case 'armor':
+ menuItems.push(...[allOptions.equip, allOptions.unequip]);
+ break;
+ case 'domainCard':
+ menuItems.push(...[allOptions.sendToLoadout, allOptions.sendToVault]);
+ break;
+ }
+ menuItems.push(...[allOptions.sendToChat, allOptions.edit, allOptions.delete]);
+
+ return menuItems;
+ };
+
+ const menuConfigs = [
+ 'armor',
+ 'weapon',
+ 'miscellaneous',
+ 'consumable',
+ 'domainCard',
+ 'miscellaneous',
+ 'ancestry',
+ 'community',
+ 'class',
+ 'subclass'
+ ];
+ menuConfigs.forEach(type => {
+ this._createContextMenu(getMenuOptions(type), `.${type}-context-menu`, {
+ eventName: 'click',
+ parentClassHooks: false,
+ fixed: true
+ });
+ });
+ }
+
_attachPartListeners(partId, htmlElement, options) {
super._attachPartListeners(partId, htmlElement, options);
@@ -544,20 +644,14 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
(await game.packs.get('daggerheart.communities'))?.render(true);
}
- static useItem(event) {
- const uuid = event.target.closest('[data-item-id]').dataset.itemId,
- item = this.document.items.find(i => i.uuid === uuid);
- item.use(event);
+ static useItem(element, button) {
+ const id = (button ?? element).closest('a').id,
+ item = this.document.items.get(id);
+ item.use({});
}
- static async viewObject(_, button) {
- const object = await fromUuid(button.dataset.value);
- if (!object) return;
-
- const tab = button.dataset.tab;
- if (tab && object.sheet._tabs) object.sheet._tabs[0].active = tab;
-
- if (object.sheet.editMode) object.sheet.editMode = false;
+ static async viewObject(element, button) {
+ const object = await fromUuid((button ?? element).dataset.uuid);
object.sheet.render(true);
}
@@ -620,8 +714,8 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
this.render();
}
- static async deleteItem(_, button) {
- const item = await fromUuid($(button).closest('[data-item-id]')[0].dataset.itemId);
+ static async deleteItem(element, button) {
+ const item = await fromUuid((button ?? element).closest('a').dataset.uuid);
await item.delete();
}
@@ -655,35 +749,29 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
cls.create(msg.toObject());
}
- static async useAbility(_, button) {
- const item = await fromUuid(button.dataset.feature);
- const type = button.dataset.type;
+ static async toChat(element, button) {
+ if (button?.dataset?.type === 'experience') {
+ const experience = this.document.system.experiences[button.dataset.uuid];
+ const cls = getDocumentClass('ChatMessage');
+ const systemData = {
+ name: game.i18n.localize('DAGGERHEART.General.Experience.Single'),
+ description: `${experience.description} ${experience.total < 0 ? experience.total : `+${experience.total}`}`
+ };
+ const msg = new cls({
+ type: 'abilityUse',
+ user: game.user.id,
+ system: systemData,
+ content: await foundry.applications.handlebars.renderTemplate(
+ 'systems/daggerheart/templates/chat/ability-use.hbs',
+ systemData
+ )
+ });
- const cls = getDocumentClass('ChatMessage');
- const systemData = {
- title:
- type === 'ancestry'
- ? game.i18n.localize('DAGGERHEART.Chat.FoundationCard.AncestryTitle')
- : type === 'community'
- ? game.i18n.localize('DAGGERHEART.Chat.FoundationCard.CommunityTitle')
- : game.i18n.localize('DAGGERHEART.Chat.FoundationCard.SubclassFeatureTitle'),
- origin: this.document.id,
- img: item.img,
- name: item.name,
- description: item.system.description,
- actions: []
- };
- const msg = new cls({
- type: 'abilityUse',
- user: game.user.id,
- system: systemData,
- content: await foundry.applications.handlebars.renderTemplate(
- 'systems/daggerheart/templates/chat/ability-use.hbs',
- systemData
- )
- });
-
- cls.create(msg.toObject());
+ cls.create(msg.toObject());
+ } else {
+ const item = await fromUuid((button ?? element).dataset.uuid);
+ item.toChat(this.document.id);
+ }
}
static async useAdvancementCard(_, button) {
@@ -738,8 +826,9 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
cls.create(msg.toObject());
}
- static async toggleEquipItem(_, button) {
- const item = this.document.items.get(button.id);
+ static async toggleEquipItem(element, button) {
+ const id = (button ?? element).closest('a').id;
+ const item = this.document.items.get(id);
if (item.system.equipped) {
await item.update({ 'system.equipped': false });
return;
@@ -763,6 +852,12 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
this.render();
}
+ static async toggleVault(element, button) {
+ const id = (button ?? element).closest('a').id;
+ const item = this.document.items.get(id);
+ await item.update({ 'system.inVault': !item.system.inVault });
+ }
+
async _onDragStart(_, event) {
super._onDragStart(event);
}
diff --git a/module/documents/item.mjs b/module/documents/item.mjs
index 54759542..ca86ce98 100644
--- a/module/documents/item.mjs
+++ b/module/documents/item.mjs
@@ -141,4 +141,32 @@ export default class DhpItem extends Item {
// Display Item Card in chat
return response;
}
+
+ async toChat(origin) {
+ const cls = getDocumentClass('ChatMessage');
+ const systemData = {
+ title:
+ this.type === 'ancestry'
+ ? game.i18n.localize('DAGGERHEART.Chat.FoundationCard.AncestryTitle')
+ : this.type === 'community'
+ ? game.i18n.localize('DAGGERHEART.Chat.FoundationCard.CommunityTitle')
+ : game.i18n.localize('DAGGERHEART.Chat.FoundationCard.SubclassFeatureTitle'),
+ origin: origin,
+ img: this.img,
+ name: this.name,
+ description: this.system.description,
+ actions: []
+ };
+ const msg = new cls({
+ type: 'abilityUse',
+ user: game.user.id,
+ system: systemData,
+ content: await foundry.applications.handlebars.renderTemplate(
+ 'systems/daggerheart/templates/chat/ability-use.hbs',
+ systemData
+ )
+ });
+
+ cls.create(msg.toObject());
+ }
}
diff --git a/styles/chat.less b/styles/chat.less
index c1de3106..e075274f 100644
--- a/styles/chat.less
+++ b/styles/chat.less
@@ -283,6 +283,7 @@
h2 {
width: 100%;
text-align: center;
+ margin: 0;
}
}
diff --git a/styles/daggerheart.css b/styles/daggerheart.css
index c1824583..994293bf 100755
--- a/styles/daggerheart.css
+++ b/styles/daggerheart.css
@@ -1598,6 +1598,7 @@
.daggerheart.chat.domain-card .domain-card-title h2 {
width: 100%;
text-align: center;
+ margin: 0;
}
.daggerheart.chat.domain-card .ability-card-footer {
display: flex;
diff --git a/templates/chat/ability-use.hbs b/templates/chat/ability-use.hbs
index 51d56ace..4ebaa293 100644
--- a/templates/chat/ability-use.hbs
+++ b/templates/chat/ability-use.hbs
@@ -1,6 +1,5 @@

diff --git a/templates/sheets/actors/character/loadout.hbs b/templates/sheets/actors/character/loadout.hbs
index a03f393c..375581f3 100644
--- a/templates/sheets/actors/character/loadout.hbs
+++ b/templates/sheets/actors/character/loadout.hbs
@@ -23,6 +23,6 @@
{{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize 'DAGGERHEART.Sheets.PC.Tabs.Loadout') type='domainCard' isGlassy=true cardView='list'}}
- {{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize 'DAGGERHEART.Sheets.PC.Tabs.Vault') type='domainCard' isGlassy=true cardView='list'}}
+ {{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize 'DAGGERHEART.Sheets.PC.Tabs.Vault') type='domainCard' isVault=true isGlassy=true cardView='list'}}
\ No newline at end of file
diff --git a/templates/sheets/actors/character/sidebar.hbs b/templates/sheets/actors/character/sidebar.hbs
index 8e4891dc..bba7b3e3 100644
--- a/templates/sheets/actors/character/sidebar.hbs
+++ b/templates/sheets/actors/character/sidebar.hbs
@@ -111,7 +111,7 @@
{{/each}}
diff --git a/templates/sheets/global/partials/domain-card-item.hbs b/templates/sheets/global/partials/domain-card-item.hbs
index 9198c8af..55662833 100644
--- a/templates/sheets/global/partials/domain-card-item.hbs
+++ b/templates/sheets/global/partials/domain-card-item.hbs
@@ -1,5 +1,5 @@