damageemter-breakdown

This commit is contained in:
centron\schwoerer
2025-11-23 09:52:43 +01:00
parent 56d3056dd5
commit bbb8c7255e

View File

@@ -1540,6 +1540,34 @@ function formatDamagePartsWithIcons(parts) {
.join(" "); .join(" ");
} }
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" },
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;
return `<div style="width:${pct}%; background:${entry.color}; height:10px; display:flex; align-items:center; justify-content:center;">
<i class="${entry.icon}" style="color:#fff; font-size:10px;"></i>
</div>`;
});
return `<div style="display:flex; width:100%; background:#e0e0e0; border-radius:4px; overflow:hidden; height:10px; margin-bottom:4px;">${segments.join("")}</div>`;
}
function resolveParticipantName(participant) { function resolveParticipantName(participant) {
if (!participant) return "Unknown"; if (!participant) return "Unknown";
@@ -1695,13 +1723,37 @@ function computeDamageMeterData() {
const filtered = currentEncounterId ? entries.filter((e) => e.encounterId === currentEncounterId) : entries; const filtered = currentEncounterId ? entries.filter((e) => e.encounterId === currentEncounterId) : entries;
if (!filtered.length) continue; if (!filtered.length) continue;
const total = filtered.reduce((sum, e) => sum + (Number(e.amount) || 0), 0); const typeTotals = new Map();
let total = 0;
for (const e of filtered) {
const amount = Number(e.amount) || 0;
total += amount;
const parts = e.damageDetails?.parts;
if (Array.isArray(parts) && parts.length) {
for (const p of parts) {
const t = (p.types && p.types[0]) || p.customTypes?.[0] || p.materials?.[0] || "untyped";
const prev = typeTotals.get(t) ?? 0;
typeTotals.set(t, prev + (Number(p.total) || 0));
}
} else {
const prev = typeTotals.get("untyped") ?? 0;
typeTotals.set("untyped", prev + amount);
}
}
const composition = Array.from(typeTotals.entries())
.map(([type, value]) => ({ type, value }))
.sort((a, b) => b.value - a.value);
const img = actor.prototypeToken?.texture?.src || actor.img || "";
totals.push({ totals.push({
actorId: actor.id, actorId: actor.id,
name: actor.name, name: actor.name,
total, total,
hits: filtered.length, hits: filtered.length,
last: filtered[0], last: filtered[0],
img,
composition,
}); });
} }
@@ -1721,16 +1773,16 @@ function buildDamageMeterContent() {
(t, idx) => { (t, idx) => {
const max = totals[0]?.total || 1; const max = totals[0]?.total || 1;
const pct = Math.max(1, Math.round((t.total / max) * 100)); const pct = Math.max(1, Math.round((t.total / max) * 100));
const bar = renderDamageBar(t.composition, t.total) || `<div style="background:#e0e0e0; border-radius:4px; overflow:hidden; height:10px; margin-bottom:4px;"><div style="width:${pct}%; background:#ff9800; height:10px;"></div></div>`;
const avatar = t.img ? `<img src="${t.img}" style="width:18px; height:18px; object-fit:cover; border-radius:3px; margin-right:6px;" />` : "";
return ` return `
<tr> <tr>
<td style="padding:4px 6px;">${idx + 1}</td> <td style="padding:4px 6px;">${idx + 1}</td>
<td style="padding:4px 6px;">${t.name}</td> <td style="padding:4px 6px; display:flex; align-items:center;">${avatar}${t.name}</td>
<td style="padding:4px 6px; text-align:right;">${t.total}</td> <td style="padding:4px 6px; text-align:right;">${t.total}</td>
<td style="padding:4px 6px; text-align:right;">${t.hits}</td> <td style="padding:4px 6px; text-align:right;">${t.hits}</td>
<td style="padding:4px 6px; max-width: 220px;"> <td style="padding:4px 6px; max-width: 220px;">
<div style="background:#e0e0e0; border-radius:4px; overflow:hidden; height:10px; margin-bottom:4px;"> ${bar}
<div style="width:${pct}%; background:#ff9800; height:10px;"></div>
</div>
${t.last?.breakdown ?? ""} ${t.last?.breakdown ?? ""}
</td> </td>
</tr>`; </tr>`;