diff --git a/static/script.js b/static/script.js
index da359a1a..f7518b6f 100644
--- a/static/script.js
+++ b/static/script.js
@@ -4810,85 +4810,61 @@ function renderCombatStatsContent(win, charName) {
const avgNormal = normalHits > 0 ? Math.round(offAll.totalNormalDamage / normalHits) : 0;
const avgCrit = crits > 0 ? Math.round(offAll.totalCritDamage / crits) : 0;
- // Damage received by element
- const defDmgMM = {}, defDmgMag = {};
- let totalDefMM = 0, totalDefMag = 0;
+ // Damage by element — offense (given) and defense (received)
+ const offDmgMM = {}, offDmgMag = {}, defDmgMM = {}, defDmgMag = {};
+ let totalOffMM = 0, totalOffMag = 0, totalDefMM = 0, totalDefMag = 0;
DAMAGE_ELEMENTS.forEach(el => {
+ offDmgMM[el] = getDefDmg(offense, 'MeleeMissile', el);
+ offDmgMag[el] = getDefDmg(offense, 'Magic', el);
defDmgMM[el] = getDefDmg(defense, 'MeleeMissile', el);
defDmgMag[el] = getDefDmg(defense, 'Magic', el);
+ totalOffMM += offDmgMM[el];
+ totalOffMag += offDmgMag[el];
totalDefMM += defDmgMM[el];
totalDefMag += defDmgMag[el];
});
const totalDmgGiven = offAll.totalNormalDamage + offAll.totalCritDamage;
- // Build the grid HTML — matches Mag-Tools CombatTrackerGUIInfo layout
+ // Build the grid HTML
const fmtN = n => n === 0 ? '' : n.toLocaleString();
const rightCell = (text) => `
${text} | `;
const labelCell = (text) => `${text} | `;
const headerCell = (text) => `${text} | `;
const statLabel = (text) => `${text} | `;
+ const sepCell = ` | `;
let html = '';
- // Header
- html += ` | ${headerCell('Mel/Msl')}${headerCell('Magic')}${statLabel('Attacks')}${rightCell(totalAttacks > 0 ? `${fmtN(totalAttacks)} (${hitRate}%)` : '')}
`;
- const statRows = [
- ['Typeless', 'Attacks', () => totalAttacks > 0 ? `${fmtN(totalAttacks)} (${hitRate}%)` : ''],
- ['Slash', 'Evades', () => totalMeleeDefends > 0 ? `${fmtN(totalMeleeDefends)} (${evadeRate}%)` : ''],
- ['Pierce', 'Resists', () => totalMagicDefends > 0 ? `${fmtN(totalMagicDefends)} (${resistRate}%)` : ''],
- ['Bludgeon', 'A.Surges', () => totalAethSurges > 0 ? `${fmtN(totalAethSurges)} (${aethRate}%)` : ''],
- ['Fire', 'C.Surges', () => totalCloakSurges > 0 ? `${fmtN(totalCloakSurges)} (${cloakRate}%)` : ''],
- ['Cold', '', () => ''],
- ['Acid', 'Av/Mx', () => avgNormal > 0 ? `${fmtN(avgNormal)} / ${fmtN(offAll.maxNormalDamage)}` : ''],
- ['Electric', 'Crits', () => crits > 0 ? `${fmtN(crits)} (${critRate}%)` : ''],
+ // Column headers: Element | Dmg Given (M/M, Mag) | Dmg Recv (M/M, Mag) | Stats
+ html += ` | ${headerCell('Given M/M')}${headerCell('Given Mag')}${sepCell}${headerCell('Recv M/M')}${headerCell('Recv Mag')}${sepCell}${statLabel('Attacks')}${rightCell(totalAttacks > 0 ? `${fmtN(totalAttacks)} (${hitRate}%)` : '')}
`;
+
+ // Stats to show on the right side of each element row
+ const rightStats = [
+ ['Evades', () => totalMeleeDefends > 0 ? `${fmtN(totalMeleeDefends)} (${evadeRate}%)` : ''],
+ ['Resists', () => totalMagicDefends > 0 ? `${fmtN(totalMagicDefends)} (${resistRate}%)` : ''],
+ ['A.Surges', () => totalAethSurges > 0 ? `${fmtN(totalAethSurges)} (${aethRate}%)` : ''],
+ ['C.Surges', () => totalCloakSurges > 0 ? `${fmtN(totalCloakSurges)} (${cloakRate}%)` : ''],
+ ['', () => ''],
+ ['', () => ''],
+ ['Av/Mx', () => avgNormal > 0 ? `${fmtN(avgNormal)} / ${fmtN(offAll.maxNormalDamage)}` : ''],
+ ['Crits', () => crits > 0 ? `${fmtN(crits)} (${critRate}%)` : ''],
];
- // First row already printed above as header. Now element rows:
for (let i = 0; i < DAMAGE_ELEMENTS.length; i++) {
const el = DAMAGE_ELEMENTS[i];
- const sr = statRows[i];
- let statLabelText = sr ? sr[1] : '';
- let statValue = sr ? sr[2]() : '';
-
- // Skip the duplicate "Attacks" row — it's already in the header
- if (i === 0) {
- statLabelText = 'Evades';
- statValue = totalMeleeDefends > 0 ? `${fmtN(totalMeleeDefends)} (${evadeRate}%)` : '';
- } else if (i === 1) {
- statLabelText = 'Resists';
- statValue = totalMagicDefends > 0 ? `${fmtN(totalMagicDefends)} (${resistRate}%)` : '';
- } else if (i === 2) {
- statLabelText = 'A.Surges';
- statValue = totalAethSurges > 0 ? `${fmtN(totalAethSurges)} (${aethRate}%)` : '';
- } else if (i === 3) {
- statLabelText = 'C.Surges';
- statValue = totalCloakSurges > 0 ? `${fmtN(totalCloakSurges)} (${cloakRate}%)` : '';
- } else if (i === 4) {
- statLabelText = '';
- statValue = '';
- } else if (i === 5) {
- statLabelText = '';
- statValue = '';
- } else if (i === 6) {
- statLabelText = 'Av/Mx';
- statValue = avgNormal > 0 ? `${fmtN(avgNormal)} / ${fmtN(offAll.maxNormalDamage)}` : '';
- } else if (i === 7) {
- statLabelText = 'Crits';
- statValue = crits > 0 ? `${fmtN(crits)} (${critRate}%)` : '';
- }
-
- html += `${labelCell(el)}${rightCell(fmtN(defDmgMM[el]))}${rightCell(fmtN(defDmgMag[el]))}${statLabelText ? statLabel(statLabelText) : ' | '}${rightCell(statValue)}
`;
+ const rs = rightStats[i] || ['', () => ''];
+ html += `${labelCell(el)}${rightCell(fmtN(offDmgMM[el]))}${rightCell(fmtN(offDmgMag[el]))}${sepCell}${rightCell(fmtN(defDmgMM[el]))}${rightCell(fmtN(defDmgMag[el]))}${sepCell}${rs[0] ? statLabel(rs[0]) : ' | '}${rightCell(rs[1]())}
`;
}
// Crit Avg/Max row
- html += ` | | | ${statLabel('Av/Mx')}${rightCell(avgCrit > 0 ? `${fmtN(avgCrit)} / ${fmtN(offAll.maxCritDamage)}` : '')}
`;
+ html += ` | | | ${sepCell} | | ${sepCell}${statLabel('Av/Mx')}${rightCell(avgCrit > 0 ? `${fmtN(avgCrit)} / ${fmtN(offAll.maxCritDamage)}` : '')}
`;
// Blank row
- html += ' |
';
+ html += ` |
`;
// Total row
- html += `${labelCell('Total')}${rightCell(fmtN(totalDefMM))}${rightCell(fmtN(totalDefMag))}${statLabel('Total')}${rightCell(fmtN(totalDmgGiven))}
`;
+ html += `${labelCell('Total')}${rightCell(fmtN(totalOffMM))}${rightCell(fmtN(totalOffMag))}${sepCell}${rightCell(fmtN(totalDefMM))}${rightCell(fmtN(totalDefMag))}${sepCell}${statLabel('Total')}${rightCell(fmtN(totalDmgGiven))}
`;
html += '
';
grid.innerHTML = html;