diff --git a/lang/en.json b/lang/en.json index 40a10993..a33f2ff5 100755 --- a/lang/en.json +++ b/lang/en.json @@ -1261,11 +1261,7 @@ "fear": { "name": "Fear", "hint": "The Fear pool of the GM." - }, - "hope": "Hope", - "hitPoints": "Hit Points", - "stress": "Stress", - "armorStack": "Armor Marks" + } }, "VariantRules": { "FIELDS": { @@ -1317,13 +1313,7 @@ "communityTitle": "Community Card", "subclassFeatureTitle": "Subclass Feature" }, - "featureTitle": "Class Feature", - "refundResources": { - "button": "Refund Resources", - "refunded": "Resources Refunded", - "confirmTitle": "Refund Resources", - "confirmText": "Are you sure you want to refund the following resources?" - } + "featureTitle": "Class Feature" }, "Notifications": { "adversaryMissing": "The linked adversary doesn't exist in the world.", @@ -1333,7 +1323,14 @@ "attackTargetDoesNotExist": "The target token no longer exists", "insufficentAdvancements": "You don't have enough advancements left.", "noAssignedPlayerCharacter": "You have no assigned character.", - "noSelectedToken": "You have no selected token", + "noTokenSelected": "No token is selected", + "noSourceItem": "No source item found in chat message", + "sourceItemNotFound": "Source item not found", + "failedToCreateTemporaryItem": "Failed to create temporary item for action use", + "actionNotFound": "Action '{id}' not found on source item", + "actionTypeNotFound": "Action type '{type}' not found", + "actionUseFailed": "Failed to use action '{action}': {error}", + "actionNotAvailable": "This action is not available", "onlyUseableByPC": "This can only be used with a PC token", "dualityParsing": "Duality roll not properly formated", "attributeFaulty": "The supplied Attribute doesn't exist", @@ -1363,13 +1360,7 @@ "damageAlreadyNone": "The damage has already been reduced to none", "noAvailableArmorMarks": "You have no more available armor marks", "notEnoughStress": "You don't have enough stress", - "damageIgnore": "{character} did not take damage", - "actorNotFound": "Actor not found", - "noPermissionToRefund": "You don't have permission to refund resources for this actor", - "resourcesAlreadyRefunded": "Resources have already been refunded for this action", - "noResourcesToRefund": "No resources were spent that can be refunded", - "resourcesRefunded": "Resources have been successfully refunded", - "refundFailed": "Failed to refund resources" + "damageIgnore": "{character} did not take damage" }, "Tooltip": { "openItemWorld": "Open Item World", diff --git a/module/applications/sheets/actors/adversary.mjs b/module/applications/sheets/actors/adversary.mjs index 97d25c36..61170689 100644 --- a/module/applications/sheets/actors/adversary.mjs +++ b/module/applications/sheets/actors/adversary.mjs @@ -122,7 +122,11 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) { name: game.i18n.localize('DAGGERHEART.GENERAL.Experience.single'), description: `${experience.name} ${ experience.modifier < 0 ? experience.modifier : `+${experience.modifier}` - }` + }`, + source: { + actor: this.document.uuid, + item: null + } }; const msg = new cls({ type: 'abilityUse', diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index 95bbbec1..63acd31e 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -700,7 +700,11 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) { img: card.img, name: card.name, description: card.system.effect, - actions: card.system.actions + actions: card.system.actions, + source: { + actor: card.actor?.uuid ?? card.actor?.id ?? null, + item: card.uuid ?? card.id + } }; const msg = new cls({ type: 'abilityUse', @@ -818,7 +822,11 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) { img: item.img, name: item.name, description: item.system.description, - actions: item.system.actions + actions: item.system.actions, + source: { + actor: item.actor?.uuid ?? item.actor?.id ?? null, + item: item.uuid ?? item.id + } }; const msg = new cls({ type: 'abilityUse', @@ -839,7 +847,11 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) { const cls = getDocumentClass('ChatMessage'); const systemData = { name: game.i18n.localize('DAGGERHEART.GENERAL.Experience.single'), - description: `${experience.name} ${experience.total < 0 ? experience.total : `+${experience.total}`}` + description: `${experience.name} ${experience.total < 0 ? experience.total : `+${experience.total}`}`, + source: { + actor: this.document.uuid, + item: null + } }; const msg = new cls({ type: 'abilityUse', @@ -875,7 +887,11 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) { origin: this.document.id, name: title, img: item.img, - description: ability.description + description: ability.description, + source: { + actor: this.document.uuid, + item: item.uuid ?? item.id + } }; const msg = new cls({ type: 'abilityUse', @@ -899,9 +915,14 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) { origin: this.document.id, name: item.name, img: item.img, - description: item.system.description + description: item.system.description, + source: { + actor: item.actor?.uuid ?? item.actor?.id ?? null, + item: item.uuid ?? item.id + } }; const msg = new cls({ + type: 'abilityUse', user: game.user.id, system: systemData, content: await foundry.applications.handlebars.renderTemplate( diff --git a/module/applications/ui/chatLog.mjs b/module/applications/ui/chatLog.mjs index 8a050b03..6f675b3a 100644 --- a/module/applications/ui/chatLog.mjs +++ b/module/applications/ui/chatLog.mjs @@ -283,99 +283,63 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo // Get the action data from the chat message const actionData = message.system.actions[Number.parseInt(event.currentTarget.dataset.index)]; + // Get the currently selected actor (from selected token) const actor = getCommandTarget(); + if (!actor) { - ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.noSelectedToken')); + ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.noTokenSelected')); return; } - // Try to find the original action from the source item first - const sourceActor = message.system.source?.actor ? await foundry.utils.fromUuid(message.system.source.actor) : null; - const sourceItem = message.system.source?.item ? sourceActor?.items?.get(message.system.source.item) : null; - - if (sourceItem && sourceActor) { - // Find the specific action on the source item - const sourceAction = sourceItem.system.actions?.find(a => a._id === actionData._id); - if (sourceAction) { - try { - // Temporarily override the action's actor reference to use the selected actor - const originalActor = sourceAction.actor; - const originalItem = sourceAction.item; - - // Create temporary getters that return our selected actor - Object.defineProperty(sourceAction, 'actor', { - get: () => actor, - configurable: true - }); - - Object.defineProperty(sourceAction, 'item', { - get: () => ({ - ...originalItem, - actor: actor, - parent: actor - }), - configurable: true - }); - - // Use the action - await sourceAction.use(event); - - // Restore the original references - Object.defineProperty(sourceAction, 'actor', { - get: () => originalActor, - configurable: true - }); - - Object.defineProperty(sourceAction, 'item', { - get: () => originalItem, - configurable: true - }); - - return; - } catch (e) { - console.error('Error using source action from chat:', e); - // Restore original references if there was an error - delete sourceAction.actor; - delete sourceAction.item; - } - } - } - - // Fallback: Create action using an existing item from the selected actor try { - // Find any weapon or item on the selected actor to use as a template - const actorItems = actor.items.filter(item => item.system.actions?.length > 0); - let templateItem = actorItems.find(item => item.system.actions.some(a => a.type === actionData.type)); - - if (!templateItem && actorItems.length > 0) { - templateItem = actorItems[0]; // Use any item with actions as a fallback + // Get the source item from the chat message + const sourceItemUuid = message.system.source?.item; + if (!sourceItemUuid) { + ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.noSourceItem')); + return; } + + const sourceItem = await foundry.utils.fromUuid(sourceItemUuid); + if (!sourceItem) { + ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.sourceItemNotFound')); + return; + } + + // Create a temporary copy of the item with a unique name + const tempItemData = sourceItem.toObject(); + tempItemData._id = foundry.utils.randomID(); // Give it a new ID + const originalName = tempItemData.name; + tempItemData.name = `${tempItemData.name} (${foundry.utils.randomID()})`; - if (templateItem) { - // Use the template item's action structure but with our action data - const { actionsTypes } = await import('../../data/action/_module.mjs'); - const ActionClass = actionsTypes[actionData.type]; - - if (ActionClass) { - // Create the action with the template item's system as parent - const actionInstance = new ActionClass({ - ...actionData, - _id: foundry.utils.randomID(), - name: actionData.name || game.i18n.localize(`DAGGERHEART.ACTIONS.TYPES.${actionData.type}.name`) - }, { parent: templateItem.system }); - - await actionInstance.use(event); + // Create the temporary item on the selected actor + const [tempItem] = await actor.createEmbeddedDocuments('Item', [tempItemData]); + + // Immediately rename it back to the original name for display purposes + await tempItem.update({ name: originalName }); + + try { + // Find the action on the temporary item + const action = tempItem.system.actions?.find(a => a._id === actionData._id); + if (!action) { + ui.notifications.error(game.i18n.format('DAGGERHEART.UI.Notifications.actionNotFound', { id: actionData._id })); return; } + + // Use the action + await action.use(event); + + } finally { + // Clean up: delete the temporary item + await actor.deleteEmbeddedDocuments('Item', [tempItem.id]); } - // Final fallback - just show a notification - ui.notifications.info(`Using ${actionData.name || actionData.type} action with selected token: ${actor.name}`); - } catch (e) { console.error('Error using action from chat:', e); - ui.notifications.error(`Error using action: ${e.message}`); + ui.notifications.error(game.i18n.format('DAGGERHEART.UI.Notifications.actionUseFailed', { + action: actionData.name || actionData.type, + error: e.message + })); } }; diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index f797323c..1ea921e9 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -568,7 +568,11 @@ export default class DHBaseAction extends foundry.abstract.DataModel { img: this.img, name: this.name, description: this.description, - actions: [] + actions: [], + source: { + actor: this.item?.actor?.uuid ?? this.item?.actor?.id ?? null, + item: this.item?.uuid ?? this.item?.id ?? null + } }; const msg = new cls({ type: 'abilityUse', diff --git a/module/data/chat-message/abilityUse.mjs b/module/data/chat-message/abilityUse.mjs index c431f14a..b593d271 100644 --- a/module/data/chat-message/abilityUse.mjs +++ b/module/data/chat-message/abilityUse.mjs @@ -8,6 +8,10 @@ export default class DHAbilityUse extends foundry.abstract.TypeDataModel { img: new fields.StringField({}), name: new fields.StringField({}), description: new fields.StringField({}), + source: new fields.SchemaField({ + actor: new fields.StringField({ nullable: true }), + item: new fields.StringField({ nullable: true }) + }), actions: new fields.ArrayField( new fields.ObjectField({ name: new fields.StringField({}), diff --git a/module/documents/item.mjs b/module/documents/item.mjs index 3e9d51d8..be6e004b 100644 --- a/module/documents/item.mjs +++ b/module/documents/item.mjs @@ -111,6 +111,7 @@ export default class DHItem extends foundry.documents.Item { async toChat(origin) { const cls = getDocumentClass('ChatMessage'); + const systemData = { title: this.type === 'ancestry' @@ -127,9 +128,10 @@ export default class DHItem extends foundry.documents.Item { actions: this.system.actions ?? [], source: { actor: this.actor?.uuid ?? this.actor?.id ?? null, - item: this.id + item: this.uuid ?? this.id } }; + const msg = new cls({ type: 'abilityUse', user: game.user.id, diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index e719b25e..3d6ad4bc 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -111,7 +111,7 @@ export const getCommandTarget = () => { } } if (!target) { - ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.noSelectedToken')); + ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.noTokenSelected')); return null; } if (target.type !== 'character') {