forked from xziino/ff14-mitigator
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
144
js/analysis.js
144
js/analysis.js
@ -161,6 +161,7 @@
|
||||
let extFights = [];
|
||||
let extReportCode = '';
|
||||
let mitigationNames = {};
|
||||
let planRefId = '';
|
||||
|
||||
// ── Player grid ──────────────────────────────────────────────────────────
|
||||
|
||||
@ -301,8 +302,11 @@
|
||||
const fight = (window.App?.fights ?? []).find(f => f.id === refId);
|
||||
if (!fight) return;
|
||||
|
||||
// Clear ext-report selection
|
||||
// Clear ext-report and plan selections
|
||||
refExtFightSelect.value = '';
|
||||
planRefId = '';
|
||||
refPlanSelect.value = '';
|
||||
refPlanPanel.style.display = 'none';
|
||||
|
||||
refFightSelect.disabled = true;
|
||||
try {
|
||||
@ -446,8 +450,11 @@
|
||||
const fight = extFights.find(f => f.id === refId);
|
||||
if (!fight) return;
|
||||
|
||||
// Clear same-report selection
|
||||
// Clear same-report and plan selections
|
||||
refFightSelect.value = '';
|
||||
planRefId = '';
|
||||
refPlanSelect.value = '';
|
||||
refPlanPanel.style.display = 'none';
|
||||
|
||||
refExtFightSelect.disabled = true;
|
||||
try {
|
||||
@ -485,6 +492,115 @@
|
||||
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 ────────────────────────────────────────────────────
|
||||
|
||||
function renderTimeline(events, fightStart) {
|
||||
@ -630,10 +746,12 @@
|
||||
return `<img class="aoe-target-buff-icon" src="${iconSrc}" alt="${m.name}" title="${m.name}${dr}">`;
|
||||
}).join('');
|
||||
|
||||
const isPlanRef = !!refEv.isPlanRef;
|
||||
|
||||
const refCards = refVisible.map(t => {
|
||||
const curr = currentByName[t.name];
|
||||
const diff = curr ? curr.amount - t.amount : 0;
|
||||
const dead = t.hp === 0 && t.maxHp > 0;
|
||||
const diff = (!isPlanRef && curr) ? curr.amount - t.amount : 0;
|
||||
const dead = !isPlanRef && t.hp === 0 && t.maxHp > 0;
|
||||
|
||||
const deltaHtml = diff !== 0
|
||||
? `<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 jobs = ABILITY_JOBS[k];
|
||||
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;
|
||||
}).join('\n')
|
||||
: 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 `
|
||||
<div class="aoe-target-wrap">
|
||||
<div class="aoe-ref-target${dead ? ' aoe-target--dead' : ''}">
|
||||
@ -671,20 +793,21 @@
|
||||
${deltaHtml}
|
||||
</div>
|
||||
<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>
|
||||
${refMitigIcons ? `<div class="aoe-target-buffs">${refMitigIcons}</div>` : ''}
|
||||
</div>`;
|
||||
}).join('');
|
||||
|
||||
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>`
|
||||
: '';
|
||||
const refLabel = isPlanRef ? 'PLAN' : `REF ${fmtDmg(refEv.totalDamage)} ${totalDelta}`;
|
||||
|
||||
refHtml = `
|
||||
<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>`;
|
||||
}
|
||||
@ -822,7 +945,7 @@
|
||||
mitigationNames,
|
||||
};
|
||||
},
|
||||
hasRefExport() { return refEvents.length > 0; },
|
||||
hasRefExport() { return refEvents.length > 0 && !planRefId; },
|
||||
reset() {
|
||||
lastFightId = null;
|
||||
refEvents = [];
|
||||
@ -831,6 +954,7 @@
|
||||
extFights = [];
|
||||
extReportCode = '';
|
||||
mitigationNames = {};
|
||||
planRefId = '';
|
||||
document.getElementById('ref-player-section').style.display = 'none';
|
||||
refFightSelect.value = '';
|
||||
refFightSelect.style.display = 'none';
|
||||
@ -839,6 +963,8 @@
|
||||
refFflogsLink.style.display = 'none';
|
||||
refFflogsLink.href = '#';
|
||||
refExtPanel.style.display = 'none';
|
||||
refPlanPanel.style.display = 'none';
|
||||
refPlanSelect.value = '';
|
||||
const exportBtn = document.getElementById('export-to-planner-btn');
|
||||
if (exportBtn) exportBtn.style.display = 'none';
|
||||
},
|
||||
|
||||
@ -22,6 +22,15 @@
|
||||
<div class="ref-player-label">REF Spieler</div>
|
||||
<div id="ref-player-grid" class="player-grid"></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">
|
||||
<button id="ref-ext-toggle" class="btn btn-sm">+ Anderer Report</button>
|
||||
<div id="ref-ext-panel" style="display:none">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user