(function () { const MITIG_ICONS = { 'Passage of Arms': 'assets/icons/mitigation/passage-of-arms.png', 'Divine Veil': 'assets/icons/mitigation/divine-veil.png', 'Shake It Off': 'assets/icons/mitigation/shake-it-off.png', 'Dark Missionary': 'assets/icons/mitigation/dark-missionary.png', 'Heart of Light': 'assets/icons/mitigation/heart-of-light.png', 'Temperance': 'assets/icons/mitigation/temperance.png', 'Sacred Soil': 'assets/icons/mitigation/sacred-soil.png', 'Expedient': 'assets/icons/mitigation/expedient.png', 'Fey Illumination': 'assets/icons/mitigation/fey-illumination.png', 'Collective Unconscious': 'assets/icons/mitigation/collective-unconscious.png', 'Holos': 'assets/icons/mitigation/holos.png', 'Kerachole': 'assets/icons/mitigation/kerachole.png', 'Panhaima': 'assets/icons/mitigation/panhaima.png', 'Troubadour': 'assets/icons/mitigation/troubadour.png', 'Tactician': 'assets/icons/mitigation/tactician.png', 'Shield Samba': 'assets/icons/mitigation/shield-samba.png', 'Reprisal': 'assets/icons/mitigation/reprisal.png', 'Feint': 'assets/icons/mitigation/feint.png', 'Addle': 'assets/icons/mitigation/addle.png', }; const JOB_ABBR = { 'Paladin': 'PLD', 'Warrior': 'WAR', 'DarkKnight': 'DRK', 'Gunbreaker': 'GNB', 'WhiteMage': 'WHM', 'Scholar': 'SCH', 'Astrologian': 'AST', 'Sage': 'SGE', 'Monk': 'MNK', 'Dragoon': 'DRG', 'Ninja': 'NIN', 'Samurai': 'SAM', 'Reaper': 'RPR', 'Viper': 'VPR', 'Bard': 'BRD', 'Machinist': 'MCH', 'Dancer': 'DNC', 'BlackMage': 'BLM', 'Summoner': 'SMN', 'RedMage': 'RDM', 'Pictomancer': 'PCT', 'BlueMage': 'BLU', }; function abbr(type) { return JOB_ABBR[type] ?? type.slice(0, 3).toUpperCase(); } function fmtTime(ms, start) { const rel = ms - start; const min = Math.floor(rel / 60000); const sec = String(Math.floor((rel % 60000) / 1000)).padStart(2, '0'); return `${min}:${sec}`; } function fmtDmg(n) { if (n >= 1_000_000) return (n / 1_000_000).toFixed(2) + 'M'; if (n >= 1_000) return Math.round(n / 1_000) + 'k'; return String(n); } function renderPlayers(players) { const grid = document.getElementById('player-grid'); const order = { tank: 0, healer: 1, dps: 2 }; players.sort((a, b) => (order[a.role] ?? 2) - (order[b.role] ?? 2)); grid.innerHTML = players.map(p => `
${abbr(p.type)}
${p.name}
${p.type}
`).join(''); } function renderTimeline(events, fightStart) { const el = document.getElementById('aoe-timeline'); if (!events.length) { el.innerHTML = '

Keine AoE-Events gefunden

'; return; } el.innerHTML = events.map(ev => { const mitigIcons = (ev.mitigations ?? []).map(m => { const iconSrc = MITIG_ICONS[m.name]; if (!iconSrc) return ''; const dr = m.dr > 0 ? ` −${m.dr}%` : ''; return `${m.name}`; }).join(''); const targets = ev.targets.map(t => `
${abbr(t.type)} ${t.name} ${fmtDmg(t.amount)}
${mitigIcons ? `
${mitigIcons}
` : ''}
`).join(''); return `
${fmtTime(ev.timestamp, fightStart)}
${ev.abilityName} — ${fmtDmg(ev.totalDamage)} total
${targets}
`; }).join(''); } function setEmpty(msg) { document.getElementById('analysis-loading').style.display = 'none'; document.getElementById('analysis-content').style.display = 'none'; document.getElementById('analysis-empty').style.display = 'block'; document.getElementById('analysis-empty-msg').textContent = msg; } let lastFightId = null; async function load() { const { reportCode, fightId, fightStart, fightEnd } = window.App ?? {}; if (!reportCode || !fightId) return; if (lastFightId === fightId) return; document.getElementById('analysis-loading').style.display = 'flex'; document.getElementById('analysis-empty').style.display = 'none'; document.getElementById('analysis-content').style.display = 'none'; let json; try { const res = await fetch('api/analysis.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ report_code: reportCode, fight_id: fightId, start_time: fightStart, end_time: fightEnd }), }); json = await res.json(); } catch (err) { setEmpty('Netzwerkfehler: ' + err.message); return; } if (json.reauth) { window.location.href = 'auth/start.php'; return; } if (json.error) { setEmpty('Fehler: ' + json.error); return; } lastFightId = fightId; renderPlayers(json.players ?? []); renderTimeline(json.aoe_events ?? [], json.fight_start ?? fightStart); document.getElementById('analysis-loading').style.display = 'none'; document.getElementById('analysis-content').style.display = 'block'; } window.analysisTab = { onFightSelected: load, onTabOpen: load, reset() { lastFightId = null; }, }; })();