Analyse-Tab: Plan als Referenz-Quelle
- Pläne aus localStorage als Ref-Quelle auswählbar ("+Plan als Referenz")
- planToRefEvents() konvertiert Plan-Mechaniken ins refEvents-Format
- PLAN-Label statt REF, kein Delta, kein Schadenswert
- Buff-Icons mit "fehlt"-Markierung, Debuffs im Header
- Shield-Assignments als "Schild"-Text mit Tooltip
- Schließt sich mit anderen Ref-Quellen gegenseitig aus
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
6024560e61
commit
fb58226be8
148
js/analysis.js
148
js/analysis.js
@ -161,6 +161,7 @@
|
|||||||
let extFights = [];
|
let extFights = [];
|
||||||
let extReportCode = '';
|
let extReportCode = '';
|
||||||
let mitigationNames = {};
|
let mitigationNames = {};
|
||||||
|
let planRefId = '';
|
||||||
|
|
||||||
// ── Player grid ──────────────────────────────────────────────────────────
|
// ── Player grid ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@ -301,8 +302,11 @@
|
|||||||
const fight = (window.App?.fights ?? []).find(f => f.id === refId);
|
const fight = (window.App?.fights ?? []).find(f => f.id === refId);
|
||||||
if (!fight) return;
|
if (!fight) return;
|
||||||
|
|
||||||
// Clear ext-report selection
|
// Clear ext-report and plan selections
|
||||||
refExtFightSelect.value = '';
|
refExtFightSelect.value = '';
|
||||||
|
planRefId = '';
|
||||||
|
refPlanSelect.value = '';
|
||||||
|
refPlanPanel.style.display = 'none';
|
||||||
|
|
||||||
refFightSelect.disabled = true;
|
refFightSelect.disabled = true;
|
||||||
try {
|
try {
|
||||||
@ -446,8 +450,11 @@
|
|||||||
const fight = extFights.find(f => f.id === refId);
|
const fight = extFights.find(f => f.id === refId);
|
||||||
if (!fight) return;
|
if (!fight) return;
|
||||||
|
|
||||||
// Clear same-report selection
|
// Clear same-report and plan selections
|
||||||
refFightSelect.value = '';
|
refFightSelect.value = '';
|
||||||
|
planRefId = '';
|
||||||
|
refPlanSelect.value = '';
|
||||||
|
refPlanPanel.style.display = 'none';
|
||||||
|
|
||||||
refExtFightSelect.disabled = true;
|
refExtFightSelect.disabled = true;
|
||||||
try {
|
try {
|
||||||
@ -485,6 +492,115 @@
|
|||||||
await loadExternalCompare(refId);
|
await loadExternalCompare(refId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ── Plan as reference ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
const refPlanToggle = document.getElementById('ref-plan-toggle');
|
||||||
|
const refPlanPanel = document.getElementById('ref-plan-panel');
|
||||||
|
const refPlanSelect = document.getElementById('ref-plan-select');
|
||||||
|
|
||||||
|
const PLAN_JOB_ROLE = {
|
||||||
|
'PLD': 'tank', 'WAR': 'tank', 'DRK': 'tank', 'GNB': 'tank',
|
||||||
|
'WHM': 'healer', 'SCH': 'healer', 'AST': 'healer', 'SGE': 'healer',
|
||||||
|
};
|
||||||
|
|
||||||
|
function loadPlansForRef() {
|
||||||
|
try { return JSON.parse(localStorage.getItem('ff14-planner-plans') || '[]'); }
|
||||||
|
catch { return []; }
|
||||||
|
}
|
||||||
|
|
||||||
|
function planToRefEvents(plan) {
|
||||||
|
const roster = plan.playerRoster ?? [];
|
||||||
|
const jobComp = plan.jobComposition ?? [];
|
||||||
|
const fightStart = plan.source?.fightStart ?? 0;
|
||||||
|
const mitigNames = plan.mitigationNames ?? {};
|
||||||
|
|
||||||
|
const players = jobComp.map((job, i) => ({
|
||||||
|
job,
|
||||||
|
name: roster[i]?.name ?? '',
|
||||||
|
role: PLAN_JOB_ROLE[job] ?? 'dps',
|
||||||
|
})).filter(p => p.name && p.job);
|
||||||
|
|
||||||
|
return plan.mechanics.map(m => {
|
||||||
|
const mitigations = (m.assignments ?? []).map(a => ({
|
||||||
|
key: a.ability,
|
||||||
|
name: a.abilityName || mitigNames[a.ability] || a.ability,
|
||||||
|
buffType: a.buffType,
|
||||||
|
dr: 0,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const targets = players.map(p => ({
|
||||||
|
id: 0,
|
||||||
|
name: p.name,
|
||||||
|
type: p.job,
|
||||||
|
role: p.role,
|
||||||
|
amount: 0,
|
||||||
|
absorbed: 0,
|
||||||
|
overkill: 0,
|
||||||
|
hp: 0,
|
||||||
|
maxHp: 0,
|
||||||
|
unmitigatedAmount: 0,
|
||||||
|
mitigations,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return {
|
||||||
|
abilityName: m.name,
|
||||||
|
abilityId: m.abilityId ?? 0,
|
||||||
|
timestamp: fightStart + m.timestamp,
|
||||||
|
totalDamage: 0,
|
||||||
|
targets,
|
||||||
|
isPlanRef: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function populateRefPlanSelect() {
|
||||||
|
const plans = loadPlansForRef();
|
||||||
|
refPlanSelect.innerHTML = '<option value="">— Plan auswählen —</option>';
|
||||||
|
plans.forEach(p => {
|
||||||
|
const opt = document.createElement('option');
|
||||||
|
opt.value = p.id;
|
||||||
|
opt.textContent = `${p.name} (${p.mechanics.length} Mechaniken)`;
|
||||||
|
refPlanSelect.appendChild(opt);
|
||||||
|
});
|
||||||
|
refPlanSelect.value = planRefId || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
refPlanToggle.addEventListener('click', () => {
|
||||||
|
const hidden = refPlanPanel.style.display === 'none';
|
||||||
|
refPlanPanel.style.display = hidden ? '' : 'none';
|
||||||
|
if (hidden) populateRefPlanSelect();
|
||||||
|
});
|
||||||
|
|
||||||
|
refPlanSelect.addEventListener('change', () => {
|
||||||
|
const id = refPlanSelect.value;
|
||||||
|
|
||||||
|
// Clear other ref sources
|
||||||
|
refFightSelect.value = '';
|
||||||
|
refExtFightSelect.value = '';
|
||||||
|
updateRefFflogsLink(0);
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
planRefId = '';
|
||||||
|
refEvents = [];
|
||||||
|
refFightStart = 0;
|
||||||
|
refPlayers = [];
|
||||||
|
renderRefPlayers();
|
||||||
|
renderTimeline(lastEvents, lastFightStart);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const plan = loadPlansForRef().find(p => p.id === id);
|
||||||
|
if (!plan) return;
|
||||||
|
|
||||||
|
planRefId = id;
|
||||||
|
refEvents = planToRefEvents(plan);
|
||||||
|
refFightStart = plan.source?.fightStart ?? 0;
|
||||||
|
refPlayers = [];
|
||||||
|
|
||||||
|
renderRefPlayers();
|
||||||
|
renderTimeline(lastEvents, lastFightStart);
|
||||||
|
});
|
||||||
|
|
||||||
// ── Timeline rendering ────────────────────────────────────────────────────
|
// ── Timeline rendering ────────────────────────────────────────────────────
|
||||||
|
|
||||||
function renderTimeline(events, fightStart) {
|
function renderTimeline(events, fightStart) {
|
||||||
@ -630,10 +746,12 @@
|
|||||||
return `<img class="aoe-target-buff-icon" src="${iconSrc}" alt="${m.name}" title="${m.name}${dr}">`;
|
return `<img class="aoe-target-buff-icon" src="${iconSrc}" alt="${m.name}" title="${m.name}${dr}">`;
|
||||||
}).join('');
|
}).join('');
|
||||||
|
|
||||||
|
const isPlanRef = !!refEv.isPlanRef;
|
||||||
|
|
||||||
const refCards = refVisible.map(t => {
|
const refCards = refVisible.map(t => {
|
||||||
const curr = currentByName[t.name];
|
const curr = currentByName[t.name];
|
||||||
const diff = curr ? curr.amount - t.amount : 0;
|
const diff = (!isPlanRef && curr) ? curr.amount - t.amount : 0;
|
||||||
const dead = t.hp === 0 && t.maxHp > 0;
|
const dead = !isPlanRef && t.hp === 0 && t.maxHp > 0;
|
||||||
|
|
||||||
const deltaHtml = diff !== 0
|
const deltaHtml = diff !== 0
|
||||||
? `<span class="${diff > 0 ? 'aoe-delta-worse' : 'aoe-delta-better'}">${diff > 0 ? '+' : '-'}${fmtDmg(Math.abs(diff))}</span>`
|
? `<span class="${diff > 0 ? 'aoe-delta-worse' : 'aoe-delta-better'}">${diff > 0 ? '+' : '-'}${fmtDmg(Math.abs(diff))}</span>`
|
||||||
@ -658,11 +776,15 @@
|
|||||||
const k = s.key ?? s.name;
|
const k = s.key ?? s.name;
|
||||||
const jobs = ABILITY_JOBS[k];
|
const jobs = ABILITY_JOBS[k];
|
||||||
const currentGroupHasJob = jobs ? jobs.some(j => currentFightJobSet.has(j)) : false;
|
const currentGroupHasJob = jobs ? jobs.some(j => currentFightJobSet.has(j)) : false;
|
||||||
const isMissing = currentGroupHasJob && !currentEventMitigKeys.has(k);
|
const isMissing = !isPlanRef && currentGroupHasJob && !currentEventMitigKeys.has(k);
|
||||||
return isMissing ? `${s.name} [fehlt im aktuellen Pull]` : s.name;
|
return isMissing ? `${s.name} [fehlt im aktuellen Pull]` : s.name;
|
||||||
}).join('\n')
|
}).join('\n')
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
const absorbedHtml = isPlanRef
|
||||||
|
? (refShields.length ? ` <span class="aoe-target-absorbed" title="${refShieldTitle ?? ''}">Schild</span>` : '')
|
||||||
|
: (t.absorbed > 0 ? ` <span class="aoe-target-absorbed" title="${refShieldTitle ?? 'Keine erkannten Schilde'}">+${fmtDmg(t.absorbed)}</span>` : '');
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="aoe-target-wrap">
|
<div class="aoe-target-wrap">
|
||||||
<div class="aoe-ref-target${dead ? ' aoe-target--dead' : ''}">
|
<div class="aoe-ref-target${dead ? ' aoe-target--dead' : ''}">
|
||||||
@ -671,20 +793,21 @@
|
|||||||
${deltaHtml}
|
${deltaHtml}
|
||||||
</div>
|
</div>
|
||||||
<span class="aoe-target-name">${t.name}</span>
|
<span class="aoe-target-name">${t.name}</span>
|
||||||
<span class="aoe-target-dmg">${fmtDmg(t.amount)}${t.absorbed > 0 ? ` <span class="aoe-target-absorbed" title="${refShieldTitle ?? 'Keine erkannten Schilde'}">+${fmtDmg(t.absorbed)}</span>` : ''}</span>
|
<span class="aoe-target-dmg">${isPlanRef ? '' : fmtDmg(t.amount)}${absorbedHtml}</span>
|
||||||
</div>
|
</div>
|
||||||
${refMitigIcons ? `<div class="aoe-target-buffs">${refMitigIcons}</div>` : ''}
|
${refMitigIcons ? `<div class="aoe-target-buffs">${refMitigIcons}</div>` : ''}
|
||||||
</div>`;
|
</div>`;
|
||||||
}).join('');
|
}).join('');
|
||||||
|
|
||||||
const totalDiff = ev.totalDamage - refEv.totalDamage;
|
const totalDiff = ev.totalDamage - refEv.totalDamage;
|
||||||
const totalDelta = totalDiff !== 0
|
const totalDelta = (!isPlanRef && totalDiff !== 0)
|
||||||
? `<span class="${totalDiff > 0 ? 'aoe-delta-worse' : 'aoe-delta-better'}">${totalDiff > 0 ? '+' : ''}${fmtDmg(totalDiff)}</span>`
|
? `<span class="${totalDiff > 0 ? 'aoe-delta-worse' : 'aoe-delta-better'}">${totalDiff > 0 ? '+' : ''}${fmtDmg(totalDiff)}</span>`
|
||||||
: '';
|
: '';
|
||||||
|
const refLabel = isPlanRef ? 'PLAN' : `REF ${fmtDmg(refEv.totalDamage)} ${totalDelta}`;
|
||||||
|
|
||||||
refHtml = `
|
refHtml = `
|
||||||
<div class="aoe-ref-row">
|
<div class="aoe-ref-row">
|
||||||
<span class="aoe-ref-label">REF ${fmtDmg(refEv.totalDamage)} ${totalDelta} ${refDebuffIconsHtml}</span>
|
<span class="aoe-ref-label">${refLabel} ${refDebuffIconsHtml}</span>
|
||||||
<div class="aoe-targets">${refCards}</div>
|
<div class="aoe-targets">${refCards}</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
@ -822,7 +945,7 @@
|
|||||||
mitigationNames,
|
mitigationNames,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
hasRefExport() { return refEvents.length > 0; },
|
hasRefExport() { return refEvents.length > 0 && !planRefId; },
|
||||||
reset() {
|
reset() {
|
||||||
lastFightId = null;
|
lastFightId = null;
|
||||||
refEvents = [];
|
refEvents = [];
|
||||||
@ -831,6 +954,7 @@
|
|||||||
extFights = [];
|
extFights = [];
|
||||||
extReportCode = '';
|
extReportCode = '';
|
||||||
mitigationNames = {};
|
mitigationNames = {};
|
||||||
|
planRefId = '';
|
||||||
document.getElementById('ref-player-section').style.display = 'none';
|
document.getElementById('ref-player-section').style.display = 'none';
|
||||||
refFightSelect.value = '';
|
refFightSelect.value = '';
|
||||||
refFightSelect.style.display = 'none';
|
refFightSelect.style.display = 'none';
|
||||||
@ -839,6 +963,8 @@
|
|||||||
refFflogsLink.style.display = 'none';
|
refFflogsLink.style.display = 'none';
|
||||||
refFflogsLink.href = '#';
|
refFflogsLink.href = '#';
|
||||||
refExtPanel.style.display = 'none';
|
refExtPanel.style.display = 'none';
|
||||||
|
refPlanPanel.style.display = 'none';
|
||||||
|
refPlanSelect.value = '';
|
||||||
const exportBtn = document.getElementById('export-to-planner-btn');
|
const exportBtn = document.getElementById('export-to-planner-btn');
|
||||||
if (exportBtn) exportBtn.style.display = 'none';
|
if (exportBtn) exportBtn.style.display = 'none';
|
||||||
},
|
},
|
||||||
|
|||||||
@ -22,6 +22,15 @@
|
|||||||
<div class="ref-player-label">REF Spieler</div>
|
<div class="ref-player-label">REF Spieler</div>
|
||||||
<div id="ref-player-grid" class="player-grid"></div>
|
<div id="ref-player-grid" class="player-grid"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="ref-ext-row">
|
||||||
|
<button id="ref-plan-toggle" class="btn btn-sm">+ Plan als Referenz</button>
|
||||||
|
<div id="ref-plan-panel" style="display:none">
|
||||||
|
<select id="ref-plan-select" class="filter-input">
|
||||||
|
<option value="">— Plan auswählen —</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="ref-ext-row">
|
<div class="ref-ext-row">
|
||||||
<button id="ref-ext-toggle" class="btn btn-sm">+ Anderer Report</button>
|
<button id="ref-ext-toggle" class="btn btn-sm">+ Anderer Report</button>
|
||||||
<div id="ref-ext-panel" style="display:none">
|
<div id="ref-ext-panel" style="display:none">
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user