Feature/349 items actions macro use (#356)

* Added itemUse macro on drag to hotbar

* Fixed item.type logic

* Added support for actionMacro drag from items

* Added MacroDrag for Attacks

* Fixed so UseItem macros get the img set
This commit is contained in:
WBHarry 2025-07-17 19:03:54 +02:00 committed by GitHub
parent d7e024be02
commit f15483c722
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 181 additions and 5 deletions

View file

@ -23,7 +23,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
actions: {
openSettings: DHBaseActorSheet.#openSettings
},
dragDrop: []
dragDrop: [{ dragSelector: '.inventory-item[data-type="attack"]', dropSelector: null }]
};
/**@type {typeof DHBaseActorSettings}*/
@ -49,4 +49,27 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
static async #openSettings() {
await this.settingSheet.render({ force: true });
}
/* -------------------------------------------- */
/* Application Drag/Drop */
/* -------------------------------------------- */
/**
* On dragStart on the item.
* @param {DragEvent} event - The drag event
*/
async _onDragStart(event) {
const attackItem = event.currentTarget.closest('.inventory-item[data-type="attack"]');
if (attackItem) {
const attackData = {
type: 'Attack',
actorUuid: this.document.uuid,
img: this.document.system.attack.img,
fromInternal: true
};
event.dataTransfer.setData('text/plain', JSON.stringify(attackData));
event.dataTransfer.setDragImage(attackItem.querySelector('img'), 60, 0);
}
}
}

View file

@ -30,7 +30,8 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
},
dragDrop: [
{ dragSelector: null, dropSelector: '.tab.features .drop-section' },
{ dragSelector: '.feature-item', dropSelector: null }
{ dragSelector: '.feature-item', dropSelector: null },
{ dragSelector: '.action-item', dropSelector: null }
]
};
@ -258,6 +259,23 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
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);
} else {
const actionItem = event.currentTarget.closest('.action-item');
if (actionItem) {
const action = this.document.system.actions[actionItem.dataset.index];
if (!action) {
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.actionIsMissing'));
return;
}
const actionData = {
type: 'Action',
data: { ...action.toObject(), id: action.id, itemUuid: this.document.uuid },
fromInternal: true
};
event.dataTransfer.setData('text/plain', JSON.stringify(actionData));
event.dataTransfer.setDragImage(actionItem.querySelector('img'), 60, 0);
}
}
}

View file

@ -2,3 +2,4 @@ export { default as DhChatLog } from './chatLog.mjs';
export { default as DhCombatTracker } from './combatTracker.mjs';
export * as DhCountdowns from './countdowns.mjs';
export { default as DhFearTracker } from './fearTracker.mjs';
export { default as DhHotbar } from './hotbar.mjs';

View file

@ -0,0 +1,129 @@
export default class DhHotbar extends foundry.applications.ui.Hotbar {
constructor(options) {
super(options);
this.setupHooks();
}
static async useItem(uuid) {
const item = await fromUuid(uuid);
if (!item) {
return ui.notifications.warn('WARNING.ObjectDoesNotExist', {
format: {
name: game.i18n.localize('Document'),
identifier: uuid
}
});
}
await item.use({});
}
static async useAction(itemUuid, actionId) {
const item = await foundry.utils.fromUuid(itemUuid);
if (!item) {
return ui.notifications.warn('WARNING.ObjectDoesNotExist', {
format: {
name: game.i18n.localize('Document'),
identifier: itemUuid
}
});
}
const action = item.system.actions.find(x => x.id === actionId);
if (!action) {
return ui.notifications.warn('DAGGERHEART.UI.Notifications.actionIsMissing');
}
await action.use({});
}
static async useAttack(actorUuid) {
const actor = await foundry.utils.fromUuid(actorUuid);
if (!actor) {
return ui.notifications.warn('WARNING.ObjectDoesNotExist', {
format: {
name: game.i18n.localize('Document'),
identifier: actorUuid
}
});
}
const attack = actor.system.attack;
if (!attack) {
return ui.notifications.warn('DAGGERHEART.UI.Notifications.attackIsMissing');
}
await attack.use({});
}
setupHooks() {
Hooks.on('hotbarDrop', (bar, data, slot) => {
if (data.type === 'Item') {
const item = foundry.utils.fromUuidSync(data.uuid);
if (item.uuid.startsWith('Compendium') || !item.isOwned || !item.isOwner) return true;
switch (item.type) {
case 'ancestry':
case 'community':
case 'class':
case 'subclass':
return true;
default:
this.createItemMacro(item, slot);
return false;
}
} else if (data.type === 'Action') {
const item = foundry.utils.fromUuidSync(data.data.itemUuid);
if (item.uuid.startsWith('Compendium')) return true;
if (!item.isOwned || !item.isOwner) {
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.unownedActionMacro'));
return false;
}
this.createActionMacro(data, slot);
return false;
} else if (data.type === 'Attack') {
const actor = foundry.utils.fromUuidSync(data.actorUuid);
if (actor.uuid.startsWith('Compendium')) return true;
if (!actor.isOwner) {
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.unownedAttackMacro'));
return false;
}
this.createAttackMacro(data, slot);
return false;
}
});
}
async createItemMacro(data, slot) {
const macro = await Macro.implementation.create({
name: `${game.i18n.localize('Display')} ${name}`,
type: CONST.MACRO_TYPES.SCRIPT,
img: data.img,
command: `await game.system.api.applications.ui.DhHotbar.useItem("${data.uuid}");`
});
await game.user.assignHotbarMacro(macro, slot);
}
async createActionMacro(data, slot) {
const macro = await Macro.implementation.create({
name: `${game.i18n.localize('Display')} ${name}`,
type: CONST.MACRO_TYPES.SCRIPT,
img: data.data.img,
command: `await game.system.api.applications.ui.DhHotbar.useAction("${data.data.itemUuid}", "${data.data.id}");`
});
await game.user.assignHotbarMacro(macro, slot);
}
async createAttackMacro(data, slot) {
const macro = await Macro.implementation.create({
name: `${game.i18n.localize('Display')} ${name}`,
type: CONST.MACRO_TYPES.SCRIPT,
img: data.img,
command: `await game.system.api.applications.ui.DhHotbar.useAttack("${data.actorUuid}");`
});
await game.user.assignHotbarMacro(macro, slot);
}
}

View file

@ -135,8 +135,8 @@ export const registerCountdownHooks = () => {
if (application) {
foundry.applications.instances.get(application)?.render();
} else {
foundry.applications.instances.get('narrative-countdowns').render();
foundry.applications.instances.get('encounter-countdowns').render();
foundry.applications.instances.get('narrative-countdowns')?.render();
foundry.applications.instances.get('encounter-countdowns')?.render();
}
return false;