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

View File

@@ -3,7 +3,7 @@
"type": "module",
"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.",
"version": "0.1.18",
"version": "0.1.19",
"authors": [
{ "name": "Gowler", "url": "https://foundryvtt.com" }
],