forked from xziino/ff14-mitigator
Analyse-Tab: Job-basierter Mitigation-Vergleich statt Namens-Match
Ref-Vergleich prüft jetzt ob die aktuelle Gruppe den passenden Job hat (via ABILITY_JOBS-Map), statt Spielernamen zu matchen. Fehlende Mitigations werden nur noch in der REF-Zeile hervorgehoben — der aktive Pull zeigt ausschließlich tatsächlich genutzte Mitigations. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
07a140442f
commit
565dedc568
109
js/analysis.js
109
js/analysis.js
@ -54,6 +54,63 @@
|
||||
'Pictomancer': 'PCT', 'BlueMage': 'BLU',
|
||||
};
|
||||
|
||||
// ability name → jobs that can provide it (for job-based ref comparison)
|
||||
const ABILITY_JOBS = {
|
||||
'Passage of Arms': ['PLD'],
|
||||
'Divine Veil': ['PLD'],
|
||||
'Guardian': ['PLD'],
|
||||
'Reprisal': ['PLD', 'WAR', 'DRK', 'GNB'],
|
||||
'Shake It Off': ['WAR'],
|
||||
'Bloodwhetting': ['WAR'],
|
||||
'Dark Missionary': ['DRK'],
|
||||
'Heart of Light': ['GNB'],
|
||||
'Temperance': ['WHM'],
|
||||
'Divine Benison': ['WHM'],
|
||||
'Divine Caress': ['WHM'],
|
||||
'Sacred Soil': ['SCH'],
|
||||
'Expedient': ['SCH'],
|
||||
'Fey Illumination': ['SCH'],
|
||||
'Galvanize': ['SCH'],
|
||||
'Seraphic Veil': ['SCH'],
|
||||
'Catalyze': ['SCH'],
|
||||
'Collective Unconscious': ['AST'],
|
||||
'Neutral Sect': ['AST'],
|
||||
'Intersection': ['AST'],
|
||||
'the Spire': ['AST'],
|
||||
'Kerachole': ['SGE'],
|
||||
'Holos': ['SGE'],
|
||||
'Holosakos': ['SGE'],
|
||||
'Panhaima': ['SGE'],
|
||||
'Eukrasian Prognosis': ['SGE'],
|
||||
'Eukrasian Prognosis II': ['SGE'],
|
||||
'Eukrasian Diagnosis': ['SGE'],
|
||||
'Differential Diagnosis': ['SGE'],
|
||||
'Haima': ['SGE'],
|
||||
'Troubadour': ['BRD'],
|
||||
'Tactician': ['MCH'],
|
||||
'Shield Samba': ['DNC'],
|
||||
'Improvised Finish': ['DNC'],
|
||||
'Feint': ['MNK', 'DRG', 'NIN', 'SAM', 'RPR', 'VPR'],
|
||||
'Addle': ['SCH', 'SGE', 'BLM', 'SMN', 'RDM', 'PCT'],
|
||||
'Radiant Aegis': ['SMN'],
|
||||
'Magick Barrier': ['RDM'],
|
||||
'Tempera Coat': ['PCT'],
|
||||
'Tempera Grassa': ['PCT'],
|
||||
};
|
||||
|
||||
// Deduplicated list of all mitigations across all targets of a ref event
|
||||
function collectRefMitigs(refEvent) {
|
||||
if (!refEvent) return [];
|
||||
const seen = new Set(), result = [];
|
||||
for (const t of refEvent.targets ?? []) {
|
||||
for (const m of (t.mitigations ?? [])) {
|
||||
const k = m.key ?? m.name;
|
||||
if (!seen.has(k)) { seen.add(k); result.push(m); }
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function abbr(type) {
|
||||
return JOB_ABBR[type] ?? type.slice(0, 3).toUpperCase();
|
||||
}
|
||||
@ -441,6 +498,8 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const currentFightJobSet = new Set(currentPlayers.map(p => JOB_ABBR[p.type]).filter(Boolean));
|
||||
|
||||
// Build reference index: abilityName → [events in order]
|
||||
const refIndex = {};
|
||||
for (const ev of refEvents) {
|
||||
@ -455,6 +514,11 @@
|
||||
const occ = abilityOccurrence[ev.abilityName] ?? 0;
|
||||
abilityOccurrence[ev.abilityName] = occ + 1;
|
||||
const refEv = refEvents.length ? (refIndex[ev.abilityName]?.[occ] ?? null) : null;
|
||||
const allRefMitigs = collectRefMitigs(refEv);
|
||||
const currentEventMitigKeys = new Set();
|
||||
for (const t of ev.targets) {
|
||||
for (const m of (t.mitigations ?? [])) currentEventMitigKeys.add(m.key ?? m.name);
|
||||
}
|
||||
|
||||
const visibleTargets = ev.targets.filter(t =>
|
||||
!hiddenPlayers.has(t.id) &&
|
||||
@ -476,7 +540,11 @@
|
||||
}
|
||||
}
|
||||
const eventMissingDebuffs = refEv
|
||||
? (refEv.targets[0]?.mitigations ?? []).filter(m => m.buffType === 'debuff' && !seenDebuffKeys.has(m.key ?? m.name))
|
||||
? allRefMitigs.filter(m => {
|
||||
if (m.buffType !== 'debuff' || seenDebuffKeys.has(m.key ?? m.name)) return false;
|
||||
const jobs = ABILITY_JOBS[m.key] ?? ABILITY_JOBS[m.name];
|
||||
return jobs ? jobs.some(j => currentFightJobSet.has(j)) : false;
|
||||
})
|
||||
: [];
|
||||
const debuffIconsHtml = [
|
||||
...eventDebuffs.map(m => ({ ...m, missing: false })),
|
||||
@ -507,12 +575,6 @@
|
||||
</div>`;
|
||||
})() : '';
|
||||
|
||||
const currentMitigKeys = new Set((t.mitigations ?? []).map(m => m.key ?? m.name));
|
||||
const refTarget = refEv?.targets?.find(rt => t.type ? rt.type === t.type : rt.name === t.name);
|
||||
const missingMitigs = refTarget
|
||||
? (refTarget.mitigations ?? []).filter(m => m.buffType === 'buff' && !currentMitigKeys.has(m.key ?? m.name))
|
||||
: [];
|
||||
|
||||
// DR buff icons (shown below player box)
|
||||
const mitigIcons = (t.mitigations ?? []).filter(m => m.buffType === 'buff').map(m => {
|
||||
const iconSrc = MITIG_ICONS[m.key] ?? MITIG_ICONS[m.name];
|
||||
@ -522,14 +584,8 @@
|
||||
}).join('');
|
||||
|
||||
// Shield tooltip on absorbed value
|
||||
const activeShields = (t.mitigations ?? []).filter(m => m.buffType === 'shield');
|
||||
const missingShields = refTarget
|
||||
? (refTarget.mitigations ?? []).filter(m => m.buffType === 'shield' && !currentMitigKeys.has(m.key ?? m.name))
|
||||
: [];
|
||||
const shieldLines = [
|
||||
...activeShields.map(s => s.name),
|
||||
...missingShields.map(s => `[fehlt: ${s.name}]`),
|
||||
];
|
||||
const activeShields = (t.mitigations ?? []).filter(m => m.buffType === 'shield');
|
||||
const shieldLines = activeShields.map(s => s.name);
|
||||
const shieldTitle = shieldLines.length ? shieldLines.join('\n') : null;
|
||||
|
||||
const dead = t.hp === 0 && t.maxHp > 0;
|
||||
@ -561,8 +617,8 @@
|
||||
(!playerFilter || t.name.toLowerCase().includes(playerFilter))
|
||||
);
|
||||
if (refVisible.length) {
|
||||
const currentByType = {};
|
||||
ev.targets.forEach(t => { currentByType[t.type || t.name] = t; });
|
||||
const currentByName = {};
|
||||
ev.targets.forEach(t => { currentByName[t.name] = t; });
|
||||
|
||||
const seenRefDebuffKeys = new Set();
|
||||
const refDebuffIconsHtml = refVisible.flatMap(t => (t.mitigations ?? []))
|
||||
@ -575,7 +631,7 @@
|
||||
}).join('');
|
||||
|
||||
const refCards = refVisible.map(t => {
|
||||
const curr = currentByType[t.type || t.name];
|
||||
const curr = currentByName[t.name];
|
||||
const diff = curr ? curr.amount - t.amount : 0;
|
||||
const dead = t.hp === 0 && t.maxHp > 0;
|
||||
|
||||
@ -583,13 +639,14 @@
|
||||
? `<span class="${diff > 0 ? 'aoe-delta-worse' : 'aoe-delta-better'}">${diff > 0 ? '+' : '-'}${fmtDmg(Math.abs(diff))}</span>`
|
||||
: '';
|
||||
|
||||
const currMitigKeys = new Set((curr?.mitigations ?? []).map(m => m.key ?? m.name));
|
||||
|
||||
const refMitigIcons = (t.mitigations ?? []).filter(m => m.buffType === 'buff').map(m => {
|
||||
const iconSrc = MITIG_ICONS[m.key] ?? MITIG_ICONS[m.name];
|
||||
if (!iconSrc) return '';
|
||||
const dr = m.dr > 0 ? ` −${m.dr}%` : '';
|
||||
const missing = !currMitigKeys.has(m.key ?? m.name);
|
||||
const dr = m.dr > 0 ? ` −${m.dr}%` : '';
|
||||
const k = m.key ?? m.name;
|
||||
const jobs = ABILITY_JOBS[k] ?? ABILITY_JOBS[m.name];
|
||||
const currentGroupHasJob = jobs ? jobs.some(j => currentFightJobSet.has(j)) : false;
|
||||
const missing = currentGroupHasJob && !currentEventMitigKeys.has(k);
|
||||
const cls = missing ? ' aoe-buff-ref-unique' : '';
|
||||
const titleSufx = missing ? ' (fehlt im aktuellen Pull)' : '';
|
||||
return `<img class="aoe-target-buff-icon${cls}" src="${iconSrc}" alt="${m.name}" title="${m.name}${dr}${titleSufx}">`;
|
||||
@ -597,7 +654,13 @@
|
||||
|
||||
const refShields = (t.mitigations ?? []).filter(m => m.buffType === 'shield');
|
||||
const refShieldTitle = refShields.length
|
||||
? refShields.map(s => currMitigKeys.has(s.key ?? s.name) ? s.name : `${s.name} [fehlt im aktuellen Pull]`).join('\n')
|
||||
? refShields.map(s => {
|
||||
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);
|
||||
return isMissing ? `${s.name} [fehlt im aktuellen Pull]` : s.name;
|
||||
}).join('\n')
|
||||
: null;
|
||||
|
||||
return `
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user