feat(gowlers-tracking-ledger): improve damage source extraction with proper metadata resolution

- Implement resolveActorFromMetadata() to extract attacker from message metadata UUID
- Implement resolveItemFromMetadata() to extract weapon/ability from attacker's inventory
- Change label format from "Damage (Actor, Weapon)" to "Actor -> Weapon" (e.g., "Goblin -> Scimitar Slash")
- Add support for identifiedInfo, metadata, flavor text, and speaker.alias in priority order
- Add support for Critical and Nonlethal damage modifiers
- Update version to 0.1.19

This properly resolves WHO did the damage (attacker name) and WHAT was used (weapon/ability name),
addressing the missing damage details. Format matches reference macros (macro_activate-hp-tracking.js).

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
centron\schwoerer
2025-11-20 14:36:40 +01:00
parent cc58627342
commit 91a5176374
2 changed files with 61 additions and 50 deletions

View File

@@ -1,6 +1,6 @@
const MODULE_ID = "gowlers-tracking-ledger"; const MODULE_ID = "gowlers-tracking-ledger";
const MODULE_VERSION = "0.1.18"; const MODULE_VERSION = "0.1.19";
const TRACK_SETTING = "actorSettings"; const TRACK_SETTING = "actorSettings";
const FLAG_SCOPE = "world"; const FLAG_SCOPE = "world";
const MAX_HISTORY_ROWS = 100; const MAX_HISTORY_ROWS = 100;
@@ -283,10 +283,11 @@ async function initializeModule() {
Hooks.on("updateCombat", (combat) => onCombatUpdate(combat)); Hooks.on("updateCombat", (combat) => onCombatUpdate(combat));
// Helper: Build source label from damage/healing context // Helper: Build source label from damage/healing context
function buildSourceLabel(value, options) { // Format: "ActorName -> ActionName" (e.g., "Goblin -> Scimitar Slash")
// Order of precedence (per Manual_dmgtracking.md): function buildSourceLabel(value, options = {}) {
// 1. identifiedInfo from chat message // Order of precedence (per reference macros and Manual_dmgtracking.md):
// 2. Chat card metadata (actor/item) // 1. identifiedInfo from chat message (action name, item name)
// 2. Chat card metadata (actor/item UUIDs) - resolve to documents
// 3. Chat card flavor text // 3. Chat card flavor text
// 4. Fallbacks // 4. Fallbacks
@@ -296,55 +297,65 @@ async function initializeModule() {
return null; return null;
} }
console.log("[GowlersTracking] buildSourceLabel: Processing damage value:", value); const info = message.flags?.pf1?.identifiedInfo ?? {};
console.log("[GowlersTracking] buildSourceLabel: Message flags:", message.flags); const metadata = message.flags?.pf1?.metadata ?? {};
const fromChatFlavor = message.flavor?.trim();
// Check for identifiedInfo (action name, item name) // Try to resolve the actor document from metadata
if (message.flags?.pf1?.identifiedInfo) { const actorDoc = resolveActorFromMetadata(metadata);
const identified = message.flags.pf1.identifiedInfo; // Try to resolve the item document from the actor
const actionName = identified.action || identified.actionName || identified.itemName; const itemDoc = actorDoc ? resolveItemFromMetadata(actorDoc, metadata) : null;
const actorName = identified.actorName || "Unknown";
console.log("[GowlersTracking] Found identifiedInfo:", identified); // Build actor name from multiple sources in order of preference
if (actionName) { const actorName = actorDoc?.name ??
const label = value < 0 ? `Damage (${actorName}, ${actionName})` : `Healing (${actionName})`; info.actorName ??
console.log("[GowlersTracking] Using identifiedInfo label:", label); message.speaker?.alias ??
null;
// Build action/item name from multiple sources
const actionName = info.actionName ??
info.name ??
itemDoc?.name ??
fromChatFlavor ??
null;
// Build final label with format "ActorName -> ActionName"
let label = null;
if (actorName && actionName) label = `${actorName} -> ${actionName}`;
else if (actionName) label = actionName;
else if (actorName) label = actorName;
else label = value < 0 ? "Damage" : "Healing";
// Add special modifiers
if (options.isCritical) label += " (Critical)";
if (options.asNonlethal) label += " [Nonlethal]";
console.log("[GowlersTracking] buildSourceLabel resolved to:", label);
return label; return label;
} }
// Helper: Resolve actor from metadata UUID
function resolveActorFromMetadata(metadata = {}) {
if (!metadata.actor) return null;
try {
if (typeof fromUuidSync === "function") {
const doc = fromUuidSync(metadata.actor);
if (doc instanceof Actor) return doc;
}
} catch (err) {
console.warn("[GowlersTracking] Failed to resolve actor UUID:", metadata.actor, err);
} }
// Check for chat card metadata (actor + item responsible) // Fallback: extract actor ID from UUID and look up
if (message.flags?.pf1?.metadata) { const id = String(metadata.actor).split(".").pop();
const meta = message.flags.pf1.metadata; return game.actors.get(id) ?? null;
const actorName = meta.actor?.name || meta.actorName || "Unknown";
const itemName = meta.item?.name || meta.itemName || "Attack";
console.log("[GowlersTracking] Found metadata:", meta);
if (value < 0) {
const label = `Damage (${actorName}, ${itemName})`;
console.log("[GowlersTracking] Using metadata label:", label);
return label;
} else {
const label = `Healing (${itemName})`;
console.log("[GowlersTracking] Using metadata healing label:", label);
return label;
}
} }
// Check for flavor text (custom macros may only have flavor) // Helper: Resolve item from actor's inventory using metadata UUID
if (message.flavor) { function resolveItemFromMetadata(actor, metadata = {}) {
// Try to parse flavor for action names if (!metadata.item || !(actor?.items instanceof Collection)) return null;
const flavorMatch = message.flavor.match(/\*\*(.*?)\*\*|<strong>(.*?)<\/strong>/); return actor.items.get(metadata.item) ?? null;
if (flavorMatch) {
const actionName = flavorMatch[1] || flavorMatch[2];
const label = value < 0 ? `Damage (${actionName})` : `Healing (${actionName})`;
console.log("[GowlersTracking] Using flavor text label:", label);
return label;
}
}
// Fallbacks
const fallback = value < 0 ? "Damage" : "Healing";
console.log("[GowlersTracking] Using fallback label:", fallback);
return fallback;
} }
// Helper: Record damage source for later consumption // Helper: Record damage source for later consumption

View File

@@ -3,7 +3,7 @@
"type": "module", "type": "module",
"title": "Gowler's Tracking Ledger", "title": "Gowler's Tracking Ledger",
"description": "Adds HP/XP/Currency log buttons to PF1 sheets and opens the tracking dialog preloaded with the actor's logs.", "description": "Adds HP/XP/Currency log buttons to PF1 sheets and opens the tracking dialog preloaded with the actor's logs.",
"version": "0.1.18", "version": "0.1.19",
"authors": [ "authors": [
{ "name": "Gowler", "url": "https://foundryvtt.com" } { "name": "Gowler", "url": "https://foundryvtt.com" }
], ],