feat(gowlers-tracking-ledger): add source detection and change user to source column

- Replace 'User' column with 'Source' column in HP, XP, and Currency tabs
- Add automatic source detection: Attack, Spell, Healing, Damage, XP Award, Manual
- Pass options and change data to recordHistoryEntry for source detection
- Detects source based on pf1DamageData, healing flag, spell action type, and value changes
- Defaults to 'Manual' for direct edits
- Update version to 0.1.12

Now you can see what caused each stat change (attack, spell, healing, etc)
in the tracking ledger instead of just the user who made the change.

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
centron\schwoerer
2025-11-20 10:40:29 +01:00
parent 86fb09a867
commit dcbd76adb8
2 changed files with 28 additions and 8 deletions

View File

@@ -1,6 +1,6 @@
const MODULE_ID = "gowlers-tracking-ledger"; const MODULE_ID = "gowlers-tracking-ledger";
const MODULE_VERSION = "0.1.11"; const MODULE_VERSION = "0.1.12";
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;
@@ -214,7 +214,7 @@ function handleActorUpdate(actor, change, options, userId) {
const previous = baselines[statId]; const previous = baselines[statId];
baselines[statId] = config.clone(nextValue); baselines[statId] = config.clone(nextValue);
changed = true; changed = true;
recordHistoryEntry(actor, statId, previous, nextValue, userId).catch((err) => recordHistoryEntry(actor, statId, previous, nextValue, userId, options, change).catch((err) =>
console.error(`Tracking Ledger | Failed to append ${statId} entry`, err) console.error(`Tracking Ledger | Failed to append ${statId} entry`, err)
); );
} }
@@ -431,7 +431,7 @@ function buildHistoryContent(actor, tabArg) {
{ label: "Timestamp", render: (entry) => formatDate(entry.timestamp) }, { label: "Timestamp", render: (entry) => formatDate(entry.timestamp) },
{ label: "HP", render: (entry) => entry.value }, { label: "HP", render: (entry) => entry.value },
{ label: "Δ", render: (entry) => entry.diff }, { label: "Δ", render: (entry) => entry.diff },
{ label: "User", render: (entry) => entry.user ?? "" }, { label: "Source", render: (entry) => entry.source ?? "Manual" },
{ label: "Encounter", render: (entry) => entry.encounterId ? entry.encounterId.slice(0, 8) : "N/A" }, { label: "Encounter", render: (entry) => entry.encounterId ? entry.encounterId.slice(0, 8) : "N/A" },
], ],
}, },
@@ -443,7 +443,7 @@ function buildHistoryContent(actor, tabArg) {
{ label: "Timestamp", render: (entry) => formatDate(entry.timestamp) }, { label: "Timestamp", render: (entry) => formatDate(entry.timestamp) },
{ label: "XP", render: (entry) => entry.value }, { label: "XP", render: (entry) => entry.value },
{ label: "Δ", render: (entry) => entry.diff }, { label: "Δ", render: (entry) => entry.diff },
{ label: "User", render: (entry) => entry.user ?? "" }, { label: "Source", render: (entry) => entry.source ?? "Manual" },
{ label: "Encounter", render: (entry) => entry.encounterId ? entry.encounterId.slice(0, 8) : "N/A" }, { label: "Encounter", render: (entry) => entry.encounterId ? entry.encounterId.slice(0, 8) : "N/A" },
], ],
}, },
@@ -455,7 +455,7 @@ function buildHistoryContent(actor, tabArg) {
{ label: "Timestamp", render: (entry) => formatDate(entry.timestamp) }, { label: "Timestamp", render: (entry) => formatDate(entry.timestamp) },
{ label: "Totals", render: (entry) => entry.value }, { label: "Totals", render: (entry) => entry.value },
{ label: "Δ", render: (entry) => entry.diff }, { label: "Δ", render: (entry) => entry.diff },
{ label: "User", render: (entry) => entry.user ?? "" }, { label: "Source", render: (entry) => entry.source ?? "Manual" },
{ label: "Encounter", render: (entry) => entry.encounterId ? entry.encounterId.slice(0, 8) : "N/A" }, { label: "Encounter", render: (entry) => entry.encounterId ? entry.encounterId.slice(0, 8) : "N/A" },
], ],
}, },
@@ -542,7 +542,7 @@ function renderHistoryTable(entries, columns, id) {
</table>`; </table>`;
} }
async function recordHistoryEntry(actor, statId, previous, nextValue, userId) { async function recordHistoryEntry(actor, statId, previous, nextValue, userId, options = {}, change = {}) {
const config = STAT_CONFIGS[statId]; const config = STAT_CONFIGS[statId];
if (!config) return; if (!config) return;
@@ -564,12 +564,32 @@ async function recordHistoryEntry(actor, statId, previous, nextValue, userId) {
} }
} }
// Detect source of the change
let source = "Manual";
if (options?.pf1DamageData) {
source = "Attack";
} else if (options?.healing) {
source = "Healing";
} else if (options?.pf1?.actionType === "spell") {
source = "Spell";
} else if (statId === "xp" && diffValue > 0) {
source = "XP Award";
} else if (statId === "hp") {
// Check if it's damage or healing based on the diff
const hpDiff = parseInt(diffValue);
if (hpDiff < 0) {
source = "Damage";
} else if (hpDiff > 0) {
source = "Healing";
}
}
const entry = { const entry = {
timestamp: Date.now(), timestamp: Date.now(),
value: config.formatValue(nextValue), value: config.formatValue(nextValue),
diff: config.formatDiff(diffValue), diff: config.formatDiff(diffValue),
user: game.users.get(userId)?.name ?? "System", user: game.users.get(userId)?.name ?? "System",
source: "", source: source,
encounterId: encounterId, encounterId: encounterId,
}; };

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.11", "version": "0.1.12",
"authors": [ "authors": [
{ "name": "Gowler", "url": "https://foundryvtt.com" } { "name": "Gowler", "url": "https://foundryvtt.com" }
], ],