Release gowlers-tracking-ledger v1.0.0
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
|
||||
const MODULE_ID = "gowlers-tracking-ledger";
|
||||
const MODULE_VERSION = "0.1.27";
|
||||
const MODULE_VERSION = "1.0.0";
|
||||
const TRACK_SETTING = "actorSettings";
|
||||
const FLAG_SCOPE = "world";
|
||||
const MAX_HISTORY_ROWS = 100;
|
||||
@@ -865,36 +865,18 @@ function buildXpBreakdownTooltip(actor, xpEntry) {
|
||||
// Build breakdown from encounter participants with per-NPC XP pulled from actor docs
|
||||
let breakdown = [];
|
||||
|
||||
const participants = encounter.participantDetails && encounter.participantDetails.length
|
||||
? encounter.participantDetails
|
||||
: (encounter.participants ?? []).map((id) => ({ id, name: resolveParticipantName(id), isPlayer: false }));
|
||||
|
||||
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 { monsters, players, playerCount, monsterTotalXp, perActorMonsterXp } = computeEncounterXp(encounter);
|
||||
const playerNames = (players.length ? players : monsters).map((p) => p.name ?? resolveParticipantName(p.id)).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);
|
||||
const bonusXp = Math.max(0, xpEntry.diff - perActorMonsterXp);
|
||||
|
||||
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 (monsterLines.length && monsterTotalXp) breakdown.push(`Monster XP total: ${monsterTotalXp.toLocaleString()}`);
|
||||
if (perActorMonsterXp) breakdown.push(`Monsters (per actor): ${Math.round(perActorMonsterXp).toLocaleString()}`);
|
||||
if (bonusXp > 0) breakdown.push(`Bonus XP: ${Math.round(bonusXp).toLocaleString()}`);
|
||||
|
||||
// If raw XP award data exists (from distributor/combat flags), surface it for debugging
|
||||
if (encounter.xpAwardsRaw) {
|
||||
@@ -979,6 +961,15 @@ function buildHistoryContent(actor, tabArg) {
|
||||
{ label: "Status", render: (entry) => entry.status ?? "unknown" },
|
||||
{ label: "Rounds", render: (entry) => entry.rounds || 0 },
|
||||
{ label: "Participants", render: (entry) => entry.participants ? entry.participants.length : 0 },
|
||||
{
|
||||
label: "XP",
|
||||
render: (entry) => {
|
||||
const xpInfo = computeEncounterXp(entry);
|
||||
const total = xpInfo.monsterTotalXp ? xpInfo.monsterTotalXp.toLocaleString() : "0";
|
||||
return total;
|
||||
},
|
||||
getTitle: (entry) => buildEncounterXpTooltip(entry),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -1447,6 +1438,43 @@ function resolveParticipantName(participant) {
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
function computeEncounterXp(encounter) {
|
||||
const participants = encounter?.participantDetails ?? [];
|
||||
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 monsters = resolved.filter((p) => !p.isPlayer);
|
||||
const players = resolved.filter((p) => p.isPlayer);
|
||||
const playerCount = Math.max(players.length || resolved.length || 1, 1);
|
||||
|
||||
const monsterTotalXp = monsters.reduce((sum, m) => sum + (Number.isFinite(m.npcXp) ? m.npcXp : 0), 0);
|
||||
const perActorMonsterXp = monsterTotalXp / playerCount;
|
||||
|
||||
return { monsters, monsterTotalXp, players, playerCount, perActorMonsterXp };
|
||||
}
|
||||
|
||||
function buildEncounterXpTooltip(encounter) {
|
||||
try {
|
||||
const { monsters, monsterTotalXp, players, playerCount } = computeEncounterXp(encounter);
|
||||
const playerNames = players.map((p) => p.name ?? resolveParticipantName(p.id)).filter(Boolean);
|
||||
const monsterLines = monsters.map((m) => `${m.name ?? "Unknown"}${m.npcXp ? ` (${m.npcXp.toLocaleString()} XP)` : ""}`);
|
||||
const lines = [];
|
||||
if (monsterTotalXp) lines.push(`Total XP: ${monsterTotalXp.toLocaleString()}`);
|
||||
if (monsterLines.length) lines.push(`Monsters: ${monsterLines.join("; ")}`);
|
||||
if (playerNames.length) lines.push(`Players: ${playerNames.join(", ")}`);
|
||||
lines.push(`Players counted: ${playerCount}`);
|
||||
return lines.join("\n");
|
||||
} catch (e) {
|
||||
console.warn("[GowlersTracking] Failed to build encounter XP tooltip:", e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
function checkboxValue(value, fallback = false) {
|
||||
if (value === undefined || value === null) return fallback;
|
||||
if (Array.isArray(value)) value = value[value.length - 1];
|
||||
|
||||
Reference in New Issue
Block a user