From a48f058583f51a5dd1c008a52b359917d36a73da Mon Sep 17 00:00:00 2001 From: "centron\\schwoerer" Date: Tue, 25 Nov 2025 09:09:43 +0100 Subject: [PATCH] deletion works --- .../scripts/gowlers-tracking-ledger.js | 266 +++++++++++------- 1 file changed, 161 insertions(+), 105 deletions(-) diff --git a/src/macros_new/gowlers-tracking-ledger/foundry.gowlershome.dyndns.org/modules/gowlers-tracking-ledger/scripts/gowlers-tracking-ledger.js b/src/macros_new/gowlers-tracking-ledger/foundry.gowlershome.dyndns.org/modules/gowlers-tracking-ledger/scripts/gowlers-tracking-ledger.js index a3534349..74bdd603 100644 --- a/src/macros_new/gowlers-tracking-ledger/foundry.gowlershome.dyndns.org/modules/gowlers-tracking-ledger/scripts/gowlers-tracking-ledger.js +++ b/src/macros_new/gowlers-tracking-ledger/foundry.gowlershome.dyndns.org/modules/gowlers-tracking-ledger/scripts/gowlers-tracking-ledger.js @@ -15,6 +15,33 @@ const SETTINGS_VERSION = 2; const COIN_ORDER = ["pp", "gp", "sp", "cp"]; const ENCOUNTER_FLAG = "pf1EncounterHistory"; const DAMAGE_DEALT_FLAG = "pf1DamageDealtHistory"; +const DEBUG_LOGS = false; +const HISTORY_PAGE_OPTIONS = [10, 20, 50, "all"]; + +const DAMAGE_ICON_MAP = { + slashing: { icon: "ra ra-sword", color: "#e3c000" }, + piercing: { icon: "ra ra-spear-head", color: "#2c7be5" }, + bludgeoning: { icon: "ra ra-large-hammer", color: "#e03131" }, + fire: { icon: "ra ra-fire", color: "#f76707" }, + cold: { icon: "ra ra-snowflake", color: "#3bc9db" }, + electricity: { icon: "ra ra-lightning-bolt", color: "#f0c419" }, + acid: { icon: "ra ra-round-bottom-flask", color: "#2f9e44" }, + sonic: { icon: "ra ra-megaphone", color: "#22b8cf" }, + force: { icon: "ra ra-crystal-ball", color: "#845ef7" }, + negative: { icon: "ra ra-skull", color: "#7950f2" }, + positive: { icon: "ra ra-sun", color: "#fab005" }, + healing: { icon: "ra ra-health", color: "#4caf50" }, + precision: { icon: "ra ra-target-arrows", color: "#000" }, + nonlethal: { icon: "ra ra-hand", color: "#000" }, + untyped: { icon: "ra ra-uncertainty", color: "#666" }, +}; + +const log = { + debug: (...args) => DEBUG_LOGS && console.debug("[GowlersTracking]", ...args), + info: (...args) => console.info("[GowlersTracking]", ...args), + warn: (...args) => console.warn("[GowlersTracking]", ...args), + error: (...args) => console.error("[GowlersTracking]", ...args), +}; const STAT_CONFIGS = { hp: { @@ -116,12 +143,12 @@ function setActiveHistoryTab(actorId, tabId) { // Global tab switching function for history dialog window.switchHistoryTab = function(tabId, el = null) { - console.log("[GowlersTracking] Tab switched to:", tabId); + log.debug("Tab switched to:", tabId); // Find the root element using the data-history-root attribute const root = el?.closest?.('[data-history-root]') ?? document.querySelector('[data-history-root]'); if (!root) { - console.warn("[GowlersTracking] History root element not found"); + log.warn("History root element not found"); return; } @@ -139,16 +166,16 @@ window.switchHistoryTab = function(tabId, el = null) { if (activeButton) { activeButton.classList.add('active'); - console.log("[GowlersTracking] Tab button activated:", tabId); + log.debug("Tab button activated:", tabId); } else { - console.warn("[GowlersTracking] Tab button not found for:", tabId); + log.warn("Tab button not found for:", tabId); } if (activePanel) { activePanel.style.display = 'block'; - console.log("[GowlersTracking] Tab panel displayed:", tabId); + log.debug("Tab panel displayed:", tabId); } else { - console.warn("[GowlersTracking] Tab panel not found for:", tabId); + log.warn("Tab panel not found for:", tabId); } const actorId = root?.getAttribute('data-history-root'); @@ -500,16 +527,15 @@ async function initializeModule() { } } - // Log detailed inspection for debugging - console.log("[GowlersTracking] Message inspection for damage details:"); - console.log("[GowlersTracking] - pf1Flags keys:", Object.keys(pf1Flags)); - console.log("[GowlersTracking] - Full metadata:", JSON.stringify(metadata, null, 2)); - console.log("[GowlersTracking] - message.rolls:", message.rolls?.length > 0 ? "Present" : "None"); - console.log("[GowlersTracking] - message.content length:", message.content?.length ?? 0); - - // Log HTML content snippet for analysis - if (message.content) { - console.log("[GowlersTracking] - HTML preview (first 1000 chars):", message.content.substring(0, 1000)); + if (DEBUG_LOGS) { + log.debug("Message inspection for damage details:"); + log.debug(" - pf1Flags keys:", Object.keys(pf1Flags)); + log.debug(" - Full metadata:", JSON.stringify(metadata, null, 2)); + log.debug(" - message.rolls:", message.rolls?.length > 0 ? "Present" : "None"); + log.debug(" - message.content length:", message.content?.length ?? 0); + if (message.content) { + log.debug(" - HTML preview (first 1000 chars):", message.content.substring(0, 1000)); + } } return { @@ -982,7 +1008,7 @@ function buildXpBreakdownTooltip(actor, xpEntry) { function buildHistoryContent(actor, tabArg) { const initialTab = tabArg ?? getActiveHistoryTab(actor.id, "hp"); // Explicitly capture the tab parameter - console.log("[GowlersTracking] buildHistoryContent called with initialTab:", initialTab); + log.debug("buildHistoryContent called with initialTab:", initialTab); setActiveHistoryTab(actor.id, initialTab); const canConfigure = game.user?.isGM; const configs = [ @@ -994,7 +1020,7 @@ function buildHistoryContent(actor, tabArg) { { label: "Timestamp", render: (entry) => formatDate(entry.timestamp) }, { label: "HP", render: (entry) => entry.value }, { - label: "Δ", + label: "Delta", render: (entry) => entry.diff, getTitle: (entry) => entry.damageBreakdown ? `${entry.damageBreakdown}` : "" }, @@ -1100,15 +1126,15 @@ function buildHistoryContent(actor, tabArg) { currentPage = totalPages; setHistoryPageState(actor.id, cfg.id, currentPage, rowsPerPage); } - const selectOptions = [10, 20, 50, "all"] + const selectOptions = HISTORY_PAGE_OPTIONS .map((value) => ``) .join(""); return `
${renderHistoryTable(entries, cfg.columns, cfg.id, rowsPerPage, currentPage)}
- + Page ${currentPage} / ${Math.max(1, totalPages)} - + @@ -1185,23 +1211,23 @@ async function recordHistoryEntry(actor, statId, previous, nextValue, userId, op const diffValue = config.diff(previous, nextValue); - // COMPREHENSIVE DEBUG LOGGING FOR DAMAGE REPORTING - console.log("[GowlersTracking] ===== recordHistoryEntry DEBUG ====="); - console.log("[GowlersTracking] statId:", statId); - console.log("[GowlersTracking] Previous:", previous, "Next:", nextValue, "Diff:", diffValue); - console.log("[GowlersTracking] Actor:", actor.name, "(" + actor.id + ")"); - console.log("[GowlersTracking] userId:", userId); - console.log("[GowlersTracking] Full options object:", options); - console.log("[GowlersTracking] Full change object:", change); + if (DEBUG_LOGS) { + log.debug("===== recordHistoryEntry DEBUG ====="); + log.debug("statId:", statId); + log.debug("Previous:", previous, "Next:", nextValue, "Diff:", diffValue); + log.debug("Actor:", actor.name, "(" + actor.id + ")"); + log.debug("userId:", userId); + log.debug("Full options object:", options); + log.debug("Full change object:", change); - // Log all keys in options for inspection - if (Object.keys(options).length > 0) { - console.log("[GowlersTracking] Options keys:", Object.keys(options)); - for (const key of Object.keys(options)) { - console.log(`[GowlersTracking] options.${key}:`, options[key]); + if (Object.keys(options).length > 0) { + log.debug("Options keys:", Object.keys(options)); + for (const key of Object.keys(options)) { + log.debug(`options.${key}:`, options[key]); + } } + log.debug("===== end debug ====="); } - console.log("[GowlersTracking] ===== end debug ====="); // Determine encounter ID: use active combat, or if none, check if combat just ended let encounterId = game.combat?.id ?? null; @@ -1239,28 +1265,29 @@ async function recordHistoryEntry(actor, statId, previous, nextValue, userId, op else if (options?.encounter?.id) encounterId = options.encounter.id; else if (options?.encounter?._id) encounterId = options.encounter._id; - // XP debug logging for data inspection - try { - console.log("[GowlersTracking][XP DEBUG] -----"); - console.log("[GowlersTracking][XP DEBUG] encounterId:", encounterId, "manualXp:", manualXp, "hasExplicitEncounter:", hasExplicitEncounter); - console.log("[GowlersTracking][XP DEBUG] options keys:", Object.keys(options || {})); - console.log("[GowlersTracking][XP DEBUG] change keys:", Object.keys(change || {})); - if (options) console.log("[GowlersTracking][XP DEBUG] options JSON:", JSON.stringify(options, null, 2)); - if (change) console.log("[GowlersTracking][XP DEBUG] change JSON keys:", Object.keys(change)); - if (change?.system?.details?.xp) console.log("[GowlersTracking][XP DEBUG] change.system.details.xp:", change.system.details.xp); - if (change?.system?.attributes?.hp) console.log("[GowlersTracking][XP DEBUG] change.system.attributes.hp (truncated):", JSON.stringify(change.system.attributes.hp, null, 2).substring(0, 500)); - const pf1Flags = actor.getFlag("pf1") ?? {}; - if (pf1Flags && Object.keys(pf1Flags).length) console.log("[GowlersTracking][XP DEBUG] actor pf1 flags keys:", Object.keys(pf1Flags)); - if (game.combat) console.log("[GowlersTracking][XP DEBUG] active combat flags:", game.combat.flags ?? "(none)"); - const encounterFlag = actor.getFlag(FLAG_SCOPE, ENCOUNTER_FLAG) ?? []; - console.log("[GowlersTracking][XP DEBUG] actor encounter flag count:", encounterFlag.length); - if (encounterFlag.length && encounterId) { - const encounterEntry = encounterFlag.find((e) => e.encounterID === encounterId); - console.log("[GowlersTracking][XP DEBUG] matched encounter entry:", encounterEntry ?? "(not found)"); + if (DEBUG_LOGS) { + try { + log.debug("[XP DEBUG] -----"); + log.debug("[XP DEBUG] encounterId:", encounterId, "manualXp:", manualXp, "hasExplicitEncounter:", hasExplicitEncounter); + log.debug("[XP DEBUG] options keys:", Object.keys(options || {})); + log.debug("[XP DEBUG] change keys:", Object.keys(change || {})); + if (options) log.debug("[XP DEBUG] options JSON:", JSON.stringify(options, null, 2)); + if (change) log.debug("[XP DEBUG] change JSON keys:", Object.keys(change)); + if (change?.system?.details?.xp) log.debug("[XP DEBUG] change.system.details.xp:", change.system.details.xp); + if (change?.system?.attributes?.hp) log.debug("[XP DEBUG] change.system.attributes.hp (truncated):", JSON.stringify(change.system.attributes.hp, null, 2).substring(0, 500)); + const pf1Flags = actor.getFlag("pf1") ?? {}; + if (pf1Flags && Object.keys(pf1Flags).length) log.debug("[XP DEBUG] actor pf1 flags keys:", Object.keys(pf1Flags)); + if (game.combat) log.debug("[XP DEBUG] active combat flags:", game.combat.flags ?? "(none)"); + const encounterFlag = actor.getFlag(FLAG_SCOPE, ENCOUNTER_FLAG) ?? []; + log.debug("[XP DEBUG] actor encounter flag count:", encounterFlag.length); + if (encounterFlag.length && encounterId) { + const encounterEntry = encounterFlag.find((e) => e.encounterID === encounterId); + log.debug("[XP DEBUG] matched encounter entry:", encounterEntry ?? "(not found)"); + } + log.debug("[XP DEBUG] -----"); + } catch (e) { + log.warn("[XP DEBUG] logging failed:", e); } - console.log("[GowlersTracking][XP DEBUG] -----"); - } catch (e) { - console.warn("[GowlersTracking][XP DEBUG] logging failed:", e); } } @@ -1584,30 +1611,20 @@ function isNonlethalType(type) { return String(type ?? "").toLowerCase() === "nonlethal"; } +function getPrimaryDamageType(part) { + return (part?.types && part.types[0]) || part?.customTypes?.[0] || part?.materials?.[0] || "untyped"; +} + +function getDamageIconMeta(type) { + return DAMAGE_ICON_MAP[type?.toLowerCase?.()] ?? DAMAGE_ICON_MAP.untyped; +} + function formatDamagePartsWithIcons(parts) { if (!Array.isArray(parts) || !parts.length) return ""; - const iconMap = { - slashing: { icon: "ra ra-sword", color: "#e3c000" }, - piercing: { icon: "ra ra-spear-head", color: "#2c7be5" }, - bludgeoning: { icon: "ra ra-large-hammer", color: "#e03131" }, - fire: { icon: "ra ra-fire", color: "#f76707" }, - cold: { icon: "ra ra-snowflake", color: "#3bc9db" }, - electricity: { icon: "ra ra-lightning-bolt", color: "#f0c419" }, - acid: { icon: "ra ra-round-bottom-flask", color: "#2f9e44" }, - sonic: { icon: "ra ra-megaphone", color: "#22b8cf" }, - force: { icon: "ra ra-crystal-ball", color: "#845ef7" }, - negative: { icon: "ra ra-skull", color: "#7950f2" }, - positive: { icon: "ra ra-sun", color: "#fab005" }, - healing: { icon: "ra ra-health", color: "#4caf50" }, - precision: { icon: "ra ra-target-arrows", color: "#000" }, - nonlethal: { icon: "ra ra-hand", color: "#000" }, - untyped: { icon: "ra ra-uncertainty", color: "#666" }, - }; return parts .map((p) => { - const baseType = (p.types && p.types[0]) || p.customTypes?.[0] || p.materials?.[0] || "untyped"; - const mapEntry = iconMap[baseType?.toLowerCase?.()] ?? iconMap.untyped; - const icon = ``; + const meta = getDamageIconMeta(getPrimaryDamageType(p)); + const icon = ``; const amt = Number.isFinite(p.total) ? p.total : p.formula ?? "?"; return `${icon}${amt}`; }) @@ -1615,27 +1632,10 @@ function formatDamagePartsWithIcons(parts) { } function renderDamageBar(composition = [], total = 0) { - const iconMap = { - slashing: { icon: "ra ra-sword", color: "#e3c000" }, - piercing: { icon: "ra ra-spear-head", color: "#2c7be5" }, - bludgeoning: { icon: "ra ra-large-hammer", color: "#e03131" }, - fire: { icon: "ra ra-fire", color: "#f76707" }, - cold: { icon: "ra ra-snowflake", color: "#3bc9db" }, - electricity: { icon: "ra ra-lightning-bolt", color: "#f0c419" }, - acid: { icon: "ra ra-round-bottom-flask", color: "#2f9e44" }, - sonic: { icon: "ra ra-megaphone", color: "#22b8cf" }, - force: { icon: "ra ra-crystal-ball", color: "#845ef7" }, - negative: { icon: "ra ra-skull", color: "#7950f2" }, - positive: { icon: "ra ra-sun", color: "#fab005" }, - healing: { icon: "ra ra-health", color: "#4caf50" }, - precision: { icon: "ra ra-target-arrows", color: "#000" }, - nonlethal: { icon: "ra ra-hand", color: "#000" }, - untyped: { icon: "ra ra-uncertainty", color: "#666" }, - }; if (!total || !composition.length) return ""; const segments = composition.map((c) => { const pct = Math.max(2, Math.round((c.value / total) * 100)); - const entry = iconMap[c.type?.toLowerCase?.()] ?? iconMap.untyped; + const entry = getDamageIconMeta(c.type); return `
`; @@ -2353,16 +2353,7 @@ class TrackingLedgerConfig extends FormApplication { defaultYes: false, }); if (!confirmed) return; - try { - const actors = collectAllActorDocuments(); - const tokens = collectAllTokens(); - for (const actor of actors) await clearDocumentHistory(actor); - for (const token of tokens) await clearDocumentHistory(token); - ui.notifications?.info?.("History cleared for all actors."); - } catch (err) { - console.error("[GowlersTracking] Failed to clear all histories", err); - ui.notifications?.error?.("Failed to clear all histories; see console."); - } + await clearAllHistoriesWithProgress(); }); html.find("[data-action=\"clear-history\"]").on("click", async (event) => { @@ -2417,6 +2408,71 @@ async function clearDocumentHistory(doc) { } } +async function clearAllHistoriesWithProgress() { + try { + const targets = new Map(); + + // Core actors in the directory + for (const actor of collectAllActorDocuments()) { + const key = `actor:${actor.uuid ?? actor.id}`; + if (!targets.has(key)) targets.set(key, { doc: actor, label: actor.name ?? actor.id }); + } + + // Tokens on scenes (covers unlinked NPCs) and their embedded actor documents + for (const token of collectAllTokens()) { + const tokenKey = `token:${token.uuid ?? token.id}:${token.parent?.uuid ?? "scene"}`; + if (!targets.has(tokenKey)) targets.set(tokenKey, { doc: token, label: token.name ?? token.id }); + + const tokenActor = token.actor; + if (tokenActor) { + const actorKey = `actor:${tokenActor.uuid ?? tokenActor.id}`; + if (!targets.has(actorKey)) targets.set(actorKey, { doc: tokenActor, label: tokenActor.name ?? tokenActor.id }); + } + } + + const entries = Array.from(targets.values()); + if (!entries.length) { + ui.notifications?.info?.("No actor or token histories found to clear."); + return; + } + + // Simple progress dialog + let progressEl = null; + let statusEl = null; + const dialog = new Dialog({ + title: "Clearing Histories", + content: ` +
+ +
Starting...
+
+ `, + buttons: {}, + close: () => {}, + render: (html) => { + progressEl = html[0].querySelector("progress"); + statusEl = html[0].querySelector("[data-status]"); + }, + }); + dialog.render(true); + + let completed = 0; + for (const { doc, label } of entries) { + if (statusEl) statusEl.textContent = `Clearing: ${label}`; + await clearDocumentHistory(doc); + completed += 1; + if (progressEl) progressEl.value = completed; + } + + if (statusEl) statusEl.textContent = "Done."; + setTimeout(() => dialog.close(), 500); + ui.notifications?.info?.("History cleared for all actors and tokens."); + } catch (err) { + console.error("[GowlersTracking] Failed to clear all histories", err); + ui.notifications?.error?.("Failed to clear all histories; see console."); + } +} + function collectAllActorDocuments() { const actors = new Map(); for (const a of game.actors.contents ?? []) { @@ -2584,7 +2640,7 @@ function sendChatNotification(statId, actor, previous, nextValue, entry) {
${title} Update
Actor: ${actor.name}
-
Value: ${prevText} → ${nextText} (${entry.diff})
+
Value: ${prevText} -> ${nextText} (${entry.diff})
Source: ${source}
Encounter: ${encounter}
${detailHtml}