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": {
"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",

View file

@ -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',

View file

@ -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(

View file

@ -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
}));
}
};

View file

@ -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',

View file

@ -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({}),

View file

@ -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,

View file

@ -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') {