XP breakdown working
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.25";
|
const MODULE_VERSION = "0.1.27";
|
||||||
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;
|
||||||
@@ -862,17 +862,48 @@ function buildXpBreakdownTooltip(actor, xpEntry) {
|
|||||||
return `${xpEntry.diff} XP from encounter`;
|
return `${xpEntry.diff} XP from encounter`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build breakdown from encounter participants
|
// Build breakdown from encounter participants with per-NPC XP pulled from actor docs
|
||||||
let breakdown = [];
|
let breakdown = [];
|
||||||
if (encounter.participants && encounter.participants.length > 0) {
|
|
||||||
// Resolve participant UUIDs/IDs to actor names
|
|
||||||
const participantNames = encounter.participants.slice(0, 2).map(resolveParticipantName);
|
|
||||||
|
|
||||||
const participantsStr = participantNames.join(", ");
|
const participants = encounter.participantDetails && encounter.participantDetails.length
|
||||||
const more = encounter.participants.length > 2 ? ` +${encounter.participants.length - 2} more` : "";
|
? encounter.participantDetails
|
||||||
breakdown.push(`${xpEntry.diff} from: ${participantsStr}${more}`);
|
: (encounter.participants ?? []).map((id) => ({ id, name: resolveParticipantName(id), isPlayer: false }));
|
||||||
} else {
|
|
||||||
breakdown.push(`${xpEntry.diff} XP`);
|
const resolved = participants.map((p) => {
|
||||||
|
const actorDoc = p.id ? game.actors.get(p.id) : null;
|
||||||
|
const name = p.name ?? actorDoc?.name ?? resolveParticipantName(p.id);
|
||||||
|
const isPlayer = p.isPlayer || actorDoc?.type === "character";
|
||||||
|
const npcXp = !isPlayer ? Number(actorDoc?.system?.details?.xp?.value ?? 0) : 0;
|
||||||
|
return { ...p, name, isPlayer, npcXp };
|
||||||
|
});
|
||||||
|
|
||||||
|
const players = resolved.filter((p) => p.isPlayer);
|
||||||
|
const monsters = resolved.filter((p) => !p.isPlayer);
|
||||||
|
|
||||||
|
const playerNames = (players.length ? players : resolved).map((p) => p.name).filter(Boolean);
|
||||||
|
const monsterLines = monsters.map((m) => `${m.name ?? "Unknown"}${m.npcXp ? ` (${m.npcXp.toLocaleString()} XP)` : ""}`);
|
||||||
|
|
||||||
|
const playerCount = playerNames.length || 1;
|
||||||
|
const totalEncounterXp = Math.max(0, xpEntry.diff * playerCount);
|
||||||
|
|
||||||
|
const monsterXpTotal = monsters.reduce((sum, m) => sum + (Number.isFinite(m.npcXp) ? m.npcXp : 0), 0);
|
||||||
|
const bonusXp = Math.max(0, totalEncounterXp - monsterXpTotal);
|
||||||
|
|
||||||
|
breakdown.push(`${xpEntry.diff} XP (per actor)`);
|
||||||
|
if (totalEncounterXp) breakdown.push(`${totalEncounterXp.toLocaleString()} XP encounter total`);
|
||||||
|
if (playerNames.length) breakdown.push(`Players: ${playerNames.join(", ")}`);
|
||||||
|
if (monsterLines.length) breakdown.push(`Monsters: ${monsterLines.join("; ")}`);
|
||||||
|
if (monsterLines.length && monsterXpTotal) breakdown.push(`Monster XP total: ${monsterXpTotal.toLocaleString()}`);
|
||||||
|
if (bonusXp > 0) breakdown.push(`Bonus XP: ${bonusXp.toLocaleString()}`);
|
||||||
|
|
||||||
|
// If raw XP award data exists (from distributor/combat flags), surface it for debugging
|
||||||
|
if (encounter.xpAwardsRaw) {
|
||||||
|
const keys = Object.keys(encounter.xpAwardsRaw);
|
||||||
|
breakdown.push(`XP awards data keys: ${keys.join(", ")}`);
|
||||||
|
if (encounter.xpAwardsRaw.pf1) {
|
||||||
|
breakdown.push(`pf1 keys: ${Object.keys(encounter.xpAwardsRaw.pf1).join(", ")}`);
|
||||||
|
console.log("[GowlersTracking] XP Tooltip build - xpAwardsRaw.pf1 contents:", encounter.xpAwardsRaw.pf1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add encounter status
|
// Add encounter status
|
||||||
@@ -1109,6 +1140,30 @@ async function recordHistoryEntry(actor, statId, previous, nextValue, userId, op
|
|||||||
else if (change?.encounterId) encounterId = change.encounterId;
|
else if (change?.encounterId) encounterId = change.encounterId;
|
||||||
else if (options?.encounter?.id) encounterId = options.encounter.id;
|
else if (options?.encounter?.id) encounterId = options.encounter.id;
|
||||||
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)");
|
||||||
|
}
|
||||||
|
console.log("[GowlersTracking][XP DEBUG] -----");
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("[GowlersTracking][XP DEBUG] logging failed:", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect source of the change with actor and item details
|
// Detect source of the change with actor and item details
|
||||||
@@ -1779,6 +1834,8 @@ async function updateEncounterSummary(actor, combat, status = "ongoing") {
|
|||||||
dateCreated: Date.now(),
|
dateCreated: Date.now(),
|
||||||
dateUpdated: Date.now(),
|
dateUpdated: Date.now(),
|
||||||
participants: [],
|
participants: [],
|
||||||
|
participantDetails: [],
|
||||||
|
xpAwardsRaw: null,
|
||||||
status: status,
|
status: status,
|
||||||
xpGained: 0,
|
xpGained: 0,
|
||||||
rounds: 0,
|
rounds: 0,
|
||||||
@@ -1793,12 +1850,41 @@ async function updateEncounterSummary(actor, combat, status = "ongoing") {
|
|||||||
|
|
||||||
// Update participant list with all combatants
|
// Update participant list with all combatants
|
||||||
const participantIds = new Set();
|
const participantIds = new Set();
|
||||||
|
const participantDetails = [];
|
||||||
for (const combatant of combat.combatants) {
|
for (const combatant of combat.combatants) {
|
||||||
if (combatant.actor) {
|
const cActor = combatant.actor;
|
||||||
participantIds.add(combatant.actor.id);
|
if (cActor) {
|
||||||
|
participantIds.add(cActor.id);
|
||||||
|
participantDetails.push({
|
||||||
|
id: cActor.id,
|
||||||
|
name: cActor.name,
|
||||||
|
isPlayer: cActor.hasPlayerOwner || cActor.type === "character",
|
||||||
|
type: cActor.type,
|
||||||
|
disposition: combatant.token?.disposition,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
encEntry.participants = Array.from(participantIds);
|
encEntry.participants = Array.from(participantIds);
|
||||||
|
encEntry.participantDetails = participantDetails;
|
||||||
|
encEntry.xpAwardsRaw = combat.flags ? foundry.utils.duplicate(combat.flags) : null;
|
||||||
|
if (encEntry.xpAwardsRaw) {
|
||||||
|
try {
|
||||||
|
console.log("[GowlersTracking] Captured xpAwardsRaw keys:", Object.keys(encEntry.xpAwardsRaw));
|
||||||
|
if (encEntry.xpAwardsRaw.pf1) {
|
||||||
|
console.log("[GowlersTracking] xpAwardsRaw.pf1 keys:", Object.keys(encEntry.xpAwardsRaw.pf1));
|
||||||
|
console.log("[GowlersTracking] xpAwardsRaw.pf1 payload:", encEntry.xpAwardsRaw.pf1);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("[GowlersTracking] Failed to log xpAwardsRaw keys:", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log("[GowlersTracking] No xpAwardsRaw captured on combat flags");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
console.log("[GowlersTracking] Combat flags snapshot:", combat.flags ?? "(none)");
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("[GowlersTracking] Failed to log combat flags snapshot:", e);
|
||||||
|
}
|
||||||
|
|
||||||
// Keep recent encounters (max 50)
|
// Keep recent encounters (max 50)
|
||||||
if (existing.length > 50) {
|
if (existing.length > 50) {
|
||||||
|
|||||||
Reference in New Issue
Block a user