add working buttons and fix no token selected localization

This commit is contained in:
walther.johnson 2025-07-06 21:59:56 -06:00
parent c8d2429b53
commit e4fa33082e
8 changed files with 97 additions and 107 deletions

View file

@ -1261,11 +1261,7 @@
"fear": { "fear": {
"name": "Fear", "name": "Fear",
"hint": "The Fear pool of the GM." "hint": "The Fear pool of the GM."
}, }
"hope": "Hope",
"hitPoints": "Hit Points",
"stress": "Stress",
"armorStack": "Armor Marks"
}, },
"VariantRules": { "VariantRules": {
"FIELDS": { "FIELDS": {
@ -1317,13 +1313,7 @@
"communityTitle": "Community Card", "communityTitle": "Community Card",
"subclassFeatureTitle": "Subclass Feature" "subclassFeatureTitle": "Subclass Feature"
}, },
"featureTitle": "Class 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?"
}
}, },
"Notifications": { "Notifications": {
"adversaryMissing": "The linked adversary doesn't exist in the world.", "adversaryMissing": "The linked adversary doesn't exist in the world.",
@ -1333,7 +1323,14 @@
"attackTargetDoesNotExist": "The target token no longer exists", "attackTargetDoesNotExist": "The target token no longer exists",
"insufficentAdvancements": "You don't have enough advancements left.", "insufficentAdvancements": "You don't have enough advancements left.",
"noAssignedPlayerCharacter": "You have no assigned character.", "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", "onlyUseableByPC": "This can only be used with a PC token",
"dualityParsing": "Duality roll not properly formated", "dualityParsing": "Duality roll not properly formated",
"attributeFaulty": "The supplied Attribute doesn't exist", "attributeFaulty": "The supplied Attribute doesn't exist",
@ -1363,13 +1360,7 @@
"damageAlreadyNone": "The damage has already been reduced to none", "damageAlreadyNone": "The damage has already been reduced to none",
"noAvailableArmorMarks": "You have no more available armor marks", "noAvailableArmorMarks": "You have no more available armor marks",
"notEnoughStress": "You don't have enough stress", "notEnoughStress": "You don't have enough stress",
"damageIgnore": "{character} did not take damage", "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"
}, },
"Tooltip": { "Tooltip": {
"openItemWorld": "Open Item World", "openItemWorld": "Open Item World",

View file

@ -122,7 +122,11 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
name: game.i18n.localize('DAGGERHEART.GENERAL.Experience.single'), name: game.i18n.localize('DAGGERHEART.GENERAL.Experience.single'),
description: `${experience.name} ${ description: `${experience.name} ${
experience.modifier < 0 ? experience.modifier : `+${experience.modifier}` experience.modifier < 0 ? experience.modifier : `+${experience.modifier}`
}` }`,
source: {
actor: this.document.uuid,
item: null
}
}; };
const msg = new cls({ const msg = new cls({
type: 'abilityUse', type: 'abilityUse',

View file

@ -700,7 +700,11 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
img: card.img, img: card.img,
name: card.name, name: card.name,
description: card.system.effect, 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({ const msg = new cls({
type: 'abilityUse', type: 'abilityUse',
@ -818,7 +822,11 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
img: item.img, img: item.img,
name: item.name, name: item.name,
description: item.system.description, 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({ const msg = new cls({
type: 'abilityUse', type: 'abilityUse',
@ -839,7 +847,11 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
const cls = getDocumentClass('ChatMessage'); const cls = getDocumentClass('ChatMessage');
const systemData = { const systemData = {
name: game.i18n.localize('DAGGERHEART.GENERAL.Experience.single'), 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({ const msg = new cls({
type: 'abilityUse', type: 'abilityUse',
@ -875,7 +887,11 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
origin: this.document.id, origin: this.document.id,
name: title, name: title,
img: item.img, img: item.img,
description: ability.description description: ability.description,
source: {
actor: this.document.uuid,
item: item.uuid ?? item.id
}
}; };
const msg = new cls({ const msg = new cls({
type: 'abilityUse', type: 'abilityUse',
@ -899,9 +915,14 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
origin: this.document.id, origin: this.document.id,
name: item.name, name: item.name,
img: item.img, 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({ const msg = new cls({
type: 'abilityUse',
user: game.user.id, user: game.user.id,
system: systemData, system: systemData,
content: await foundry.applications.handlebars.renderTemplate( content: await foundry.applications.handlebars.renderTemplate(

View file

@ -283,99 +283,63 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
// Get the action data from the chat message // Get the action data from the chat message
const actionData = message.system.actions[Number.parseInt(event.currentTarget.dataset.index)]; const actionData = message.system.actions[Number.parseInt(event.currentTarget.dataset.index)];
// Get the currently selected actor (from selected token) // Get the currently selected actor (from selected token)
const actor = getCommandTarget(); const actor = getCommandTarget();
if (!actor) { if (!actor) {
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.noSelectedToken')); ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.noTokenSelected'));
return; 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 { try {
// Find any weapon or item on the selected actor to use as a template // Get the source item from the chat message
const actorItems = actor.items.filter(item => item.system.actions?.length > 0); const sourceItemUuid = message.system.source?.item;
let templateItem = actorItems.find(item => item.system.actions.some(a => a.type === actionData.type)); if (!sourceItemUuid) {
ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.noSourceItem'));
if (!templateItem && actorItems.length > 0) { return;
templateItem = actorItems[0]; // Use any item with actions as a fallback
} }
if (templateItem) { const sourceItem = await foundry.utils.fromUuid(sourceItemUuid);
// Use the template item's action structure but with our action data if (!sourceItem) {
const { actionsTypes } = await import('../../data/action/_module.mjs'); ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.sourceItemNotFound'));
const ActionClass = actionsTypes[actionData.type]; return;
}
if (ActionClass) { // Create a temporary copy of the item with a unique name
// Create the action with the template item's system as parent const tempItemData = sourceItem.toObject();
const actionInstance = new ActionClass({ tempItemData._id = foundry.utils.randomID(); // Give it a new ID
...actionData, const originalName = tempItemData.name;
_id: foundry.utils.randomID(), tempItemData.name = `${tempItemData.name} (${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; return;
} }
}
// Final fallback - just show a notification // Use the action
ui.notifications.info(`Using ${actionData.name || actionData.type} action with selected token: ${actor.name}`); await action.use(event);
} finally {
// Clean up: delete the temporary item
await actor.deleteEmbeddedDocuments('Item', [tempItem.id]);
}
} catch (e) { } catch (e) {
console.error('Error using action from chat:', 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
}));
} }
}; };

View file

@ -568,7 +568,11 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
img: this.img, img: this.img,
name: this.name, name: this.name,
description: this.description, 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({ const msg = new cls({
type: 'abilityUse', type: 'abilityUse',

View file

@ -8,6 +8,10 @@ export default class DHAbilityUse extends foundry.abstract.TypeDataModel {
img: new fields.StringField({}), img: new fields.StringField({}),
name: new fields.StringField({}), name: new fields.StringField({}),
description: 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( actions: new fields.ArrayField(
new fields.ObjectField({ new fields.ObjectField({
name: new fields.StringField({}), name: new fields.StringField({}),

View file

@ -111,6 +111,7 @@ export default class DHItem extends foundry.documents.Item {
async toChat(origin) { async toChat(origin) {
const cls = getDocumentClass('ChatMessage'); const cls = getDocumentClass('ChatMessage');
const systemData = { const systemData = {
title: title:
this.type === 'ancestry' this.type === 'ancestry'
@ -127,9 +128,10 @@ export default class DHItem extends foundry.documents.Item {
actions: this.system.actions ?? [], actions: this.system.actions ?? [],
source: { source: {
actor: this.actor?.uuid ?? this.actor?.id ?? null, actor: this.actor?.uuid ?? this.actor?.id ?? null,
item: this.id item: this.uuid ?? this.id
} }
}; };
const msg = new cls({ const msg = new cls({
type: 'abilityUse', type: 'abilityUse',
user: game.user.id, user: game.user.id,

View file

@ -111,7 +111,7 @@ export const getCommandTarget = () => {
} }
} }
if (!target) { if (!target) {
ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.noSelectedToken')); ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.noTokenSelected'));
return null; return null;
} }
if (target.type !== 'character') { if (target.type !== 'character') {