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:
@@ -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
|
||||||
|
|||||||
@@ -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" }
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user