Planer: Clarity of Corundum + separate Tankbuster-Fixes
- Clarity of Corundum (GNB Proc, 15% DR, statusId 1002684) neu in MITIGATION_ABILITIES, ABILITY_DR, JOB_ABILITIES, TIMELINE_PERSONAL_ABILITIES - statusId fuer Heart of Corundum (1002683) und Great Nebula (1003838) nachgetragen (stabilere Erkennung via buffs-Feld) - Icons heruntergeladen: heart-of-corundum.png, heart-of-stone.png, clarity-of-corundum.png (teilt HoC-Action-Icon vorlaeufig) - Cache-Version v3 -> v4 (erzwingt Neu-Fetch nach Erkennungs-Aenderungen) Separate Tankbuster: - tankMaxHpFromEvent(): maxHP direkt aus dem aoe_event des getroffenen Tanks statt Roster-Durchschnitt (ein Tank getroffen = sein MaxHP) - renderMechanicListHtml: m.tankMaxHp hat Vorrang vor avgTankMaxHp(plan) - plannedAssignmentsForMechanic: persoenliche Mitigation erscheint nur auf der direkt zugewiesenen Mechanik (DRK-CDs nicht mehr auf GNB-TB) - DR-Label: 'mitigiert' -> 'nach DR' (verbleibender Schaden, nicht reduzierter Betrag) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
7eeeb5ef56
commit
a9b3cc8666
@ -122,10 +122,11 @@ const MITIGATION_ABILITIES = [
|
||||
// GNB
|
||||
'Superbolide' => ['dr' => 100, 'buffType' => 'buff', 'extraAbilityGameID' => 16152],
|
||||
'Nebula' => ['dr' => 30, 'buffType' => 'buff', 'extraAbilityGameID' => 16148],
|
||||
'Great Nebula' => ['dr' => 40, 'buffType' => 'buff', 'extraAbilityGameID' => 36935],
|
||||
'Great Nebula' => ['dr' => 40, 'buffType' => 'buff', 'statusId' => 1003838, 'extraAbilityGameID' => 36935],
|
||||
'Camouflage' => ['dr' => 10, 'buffType' => 'buff', 'extraAbilityGameID' => 16140],
|
||||
'Heart of Stone' => ['dr' => 15, 'buffType' => 'buff', 'extraAbilityGameID' => 16161],
|
||||
'Heart of Corundum' => ['dr' => 15, 'buffType' => 'buff', 'extraAbilityGameID' => 25758],
|
||||
'Heart of Corundum' => ['dr' => 15, 'buffType' => 'buff', 'statusId' => 1002683, 'extraAbilityGameID' => 25758],
|
||||
'Clarity of Corundum' => ['dr' => 15, 'buffType' => 'buff', 'statusId' => 1002684, 'extraAbilityGameID' => 25758], // Proc von Heart of Corundum, kann beliebiges Partymitglied treffen
|
||||
// DPS
|
||||
'Riddle of Earth' => ['dr' => 20, 'buffType' => 'buff', 'extraAbilityGameID' => 7394],
|
||||
'Shade Shift' => ['dr' => 0, 'buffType' => 'shield', 'extraAbilityGameID' => 2241],
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
const CACHED_LOG_DIR = __DIR__ . '/../cached_logs';
|
||||
const CACHED_LOG_VERSION = 'v3';
|
||||
const CACHED_LOG_VERSION = 'v4';
|
||||
|
||||
function cache_language(string $language): string {
|
||||
$language = strtolower(trim($language));
|
||||
|
||||
BIN
assets/icons/mitigation/clarity-of-corundum.png
Normal file
BIN
assets/icons/mitigation/clarity-of-corundum.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.3 KiB |
BIN
assets/icons/mitigation/heart-of-corundum.png
Normal file
BIN
assets/icons/mitigation/heart-of-corundum.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.3 KiB |
BIN
assets/icons/mitigation/heart-of-stone.png
Normal file
BIN
assets/icons/mitigation/heart-of-stone.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
@ -69,6 +69,7 @@
|
||||
{ name: 'Camouflage', buffType: 'buff', extraAbilityGameID: 16140, duration: 20 },
|
||||
{ name: 'Heart of Stone', buffType: 'buff', extraAbilityGameID: 16161, duration: 7 },
|
||||
{ name: 'Heart of Corundum', buffType: 'buff', extraAbilityGameID: 25758, duration: 8 },
|
||||
{ name: 'Clarity of Corundum', buffType: 'buff', extraAbilityGameID: 25758, duration: 4 }, // Proc von HoC, geht auf beliebiges Partymitglied
|
||||
{ name: 'Reprisal', buffType: 'debuff' },
|
||||
],
|
||||
'WHM': [
|
||||
@ -156,7 +157,7 @@
|
||||
'Oblation': 'DRK',
|
||||
'Heart of Light': 'GNB', 'Superbolide': 'GNB', 'Nebula': 'GNB',
|
||||
'Great Nebula': 'GNB', 'Camouflage': 'GNB', 'Heart of Stone': 'GNB',
|
||||
'Heart of Corundum': 'GNB',
|
||||
'Heart of Corundum': 'GNB', 'Clarity of Corundum': 'GNB',
|
||||
'Temperance': 'WHM', 'Divine Benison': 'WHM', 'Divine Caress': 'WHM',
|
||||
'Sacred Soil': 'SCH', 'Expedient': 'SCH', 'Fey Illumination': 'SCH',
|
||||
'Galvanize': 'SCH', 'Seraphic Veil': 'SCH', 'Catalyze': 'SCH',
|
||||
@ -219,6 +220,9 @@
|
||||
'Tempera Coat': 'assets/icons/mitigation/tempera-coat.png',
|
||||
'Tempera Grassa': 'assets/icons/mitigation/tempera-grassa.png',
|
||||
'Improvised Finish': 'assets/icons/mitigation/improvised-finish.png',
|
||||
'Heart of Corundum': 'assets/icons/mitigation/heart-of-corundum.png',
|
||||
'Clarity of Corundum': 'assets/icons/mitigation/clarity-of-corundum.png',
|
||||
'Heart of Stone': 'assets/icons/mitigation/heart-of-stone.png',
|
||||
};
|
||||
|
||||
const ABILITY_DR = {
|
||||
@ -259,6 +263,7 @@
|
||||
'Camouflage': 0.10,
|
||||
'Heart of Stone': 0.15,
|
||||
'Heart of Corundum': 0.15,
|
||||
'Clarity of Corundum': 0.15,
|
||||
'Riddle of Earth': 0.20,
|
||||
'Third Eye': 0.10,
|
||||
};
|
||||
|
||||
@ -493,10 +493,40 @@ function avgNonTankMaxHp(plan) {
|
||||
return Math.round(hps.reduce((s, v) => s + v, 0) / hps.length);
|
||||
}
|
||||
|
||||
function avgTankMaxHp(plan) {
|
||||
const roster = plan.playerRoster ?? [];
|
||||
const jobComp = plan.jobComposition ?? [];
|
||||
const hps = jobComp
|
||||
.map((job, i) => ({ job, maxHp: roster[i]?.maxHp ?? 0 }))
|
||||
.filter(p => p.job && JOB_ROLE[p.job] === 'tank' && p.maxHp > 0)
|
||||
.map(p => p.maxHp);
|
||||
if (!hps.length) return 0;
|
||||
// Tankbuster trifft einen Tank → Durchschnitt über alle gefundenen Tanks
|
||||
// (1 Tank → sein MaxHP direkt; 2 Tanks → (hp1 + hp2) / 2)
|
||||
return Math.round(hps.reduce((s, v) => s + v, 0) / hps.length);
|
||||
}
|
||||
|
||||
// Ermittelt den maxHP-Wert direkt aus dem aoe_event (für präzise Tankbuster-Anzeige)
|
||||
function tankMaxHpFromEvent(ev) {
|
||||
if (!ev?.isHeavyTankbuster) return 0;
|
||||
const tankTargets = (ev.targets ?? []).filter(t => t.role === 'tank' && (t.maxHp ?? 0) > 0);
|
||||
if (tankTargets.length === 1) return tankTargets[0].maxHp;
|
||||
if (tankTargets.length > 1) {
|
||||
// Mehrere Tanks gleichzeitig → Durchschnitt
|
||||
return Math.round(tankTargets.reduce((s, t) => s + t.maxHp, 0) / tankTargets.length);
|
||||
}
|
||||
// Fallback: erstes Target mit maxHp (z.B. wenn Role nicht gesetzt)
|
||||
const any = (ev.targets ?? []).find(t => (t.maxHp ?? 0) > 0);
|
||||
return any?.maxHp ?? 0;
|
||||
}
|
||||
|
||||
function simulateDrMultiplier(mechanic, assignments = mechanic.assignments ?? []) {
|
||||
const isTankbuster = !!mechanic.isHeavyTankbuster;
|
||||
let mult = 1;
|
||||
for (const a of assignments) {
|
||||
if (a.buffType === 'shield') continue;
|
||||
// Persönliche Mitigation nur bei Tankbustern einrechnen
|
||||
if (!isTankbuster && TIMELINE_PERSONAL_ABILITIES.has(a.ability)) continue;
|
||||
mult *= (1 - (ABILITY_DR[a.ability] ?? 0));
|
||||
}
|
||||
return mult;
|
||||
@ -510,6 +540,13 @@ function plannedAssignmentsForMechanic(plan, targetMechanic) {
|
||||
for (const entry of canonicalAssignmentActivations(plan, { dedupeKey: canonicalMechanicKey })) {
|
||||
if (targetTime < entry.start - tolerance || targetTime > entry.end + tolerance) continue;
|
||||
|
||||
// Persönliche Mitigation zeigt nur auf der Mechanik, der sie direkt zugewiesen ist
|
||||
// (verhindert dass DRK-Cooldowns auf GNB-Tankbuster erscheinen und umgekehrt)
|
||||
if (TIMELINE_PERSONAL_ABILITIES.has(entry.assignment.ability)
|
||||
&& entry.mechanic.id !== targetMechanic.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result.push({
|
||||
...entry.assignment,
|
||||
sourceMechanicId: entry.mechanic.id,
|
||||
@ -535,9 +572,14 @@ function renderMechanicListHtml(plan) {
|
||||
}
|
||||
|
||||
const activeJobSet = new Set(plan.jobComposition.filter(j => j));
|
||||
const avgHp = avgNonTankMaxHp(plan);
|
||||
const nonTankAvgHp = avgNonTankMaxHp(plan);
|
||||
const tankAvgHp = avgTankMaxHp(plan);
|
||||
|
||||
return mechanics.map(m => {
|
||||
// Tankbuster: gespeicherter maxHP des getroffenen Tanks hat Vorrang (präziser als Roster-Durchschnitt)
|
||||
const avgHp = m.isHeavyTankbuster
|
||||
? ((m.tankMaxHp ?? 0) > 0 ? m.tankMaxHp : tankAvgHp)
|
||||
: nonTankAvgHp;
|
||||
const planned = plannedAssignmentsForMechanic(plan, m);
|
||||
const sorted = sortedAssignments(planned);
|
||||
const assignHtml = sorted.length === 0
|
||||
@ -588,7 +630,7 @@ function renderMechanicListHtml(plan) {
|
||||
: ''
|
||||
}
|
||||
${hasDrAssign || hasShield ? `<div class="mechanic-dmg mechanic-mitig-row">
|
||||
${hasDrAssign ? `<span class="mechanic-mitig-val${drOnlyCls ? ' ' + drOnlyCls : ''}">→ ${fmtNumber(drOnly)}</span> mitigiert` : ''}
|
||||
${hasDrAssign ? `<span class="mechanic-mitig-val${drOnlyCls ? ' ' + drOnlyCls : ''}">→ ${fmtNumber(drOnly)}</span> nach DR` : ''}
|
||||
${hasShield ? `<span class="mechanic-mitig-shield${fullCls ? ' ' + fullCls : ''}">Mitigation mit Schild ${fmtNumber(mitigFull)}</span>` : ''}
|
||||
</div>` : ''}
|
||||
<div class="mechanic-assignments">${assignHtml}</div>
|
||||
@ -711,6 +753,7 @@ const TIMELINE_PERSONAL_ABILITIES = new Set([
|
||||
'Camouflage',
|
||||
'Heart of Stone',
|
||||
'Heart of Corundum',
|
||||
'Clarity of Corundum',
|
||||
'Divine Benison',
|
||||
'Intersection',
|
||||
'the Spire',
|
||||
@ -2267,6 +2310,7 @@ function aoeEventsToMechanics(aoeEvents, fightStart, phases, players, withMitiga
|
||||
phase: phase?.name ?? '',
|
||||
unmitigatedDamage: avgUnmit,
|
||||
isHeavyTankbuster: !!ev.isHeavyTankbuster,
|
||||
tankMaxHp: tankMaxHpFromEvent(ev),
|
||||
notes: '',
|
||||
assignments,
|
||||
};
|
||||
@ -2387,9 +2431,10 @@ async function refreshPlanLanguage(planId) {
|
||||
}));
|
||||
return {
|
||||
...mechanic,
|
||||
name: match.abilityName ?? mechanic.name,
|
||||
abilityId: match.abilityId ?? mechanic.abilityId,
|
||||
name: match.abilityName ?? mechanic.name,
|
||||
abilityId: match.abilityId ?? mechanic.abilityId,
|
||||
isHeavyTankbuster: mechanic.mechanicTypeManual ? !!mechanic.isHeavyTankbuster : !!match.isHeavyTankbuster,
|
||||
tankMaxHp: mechanic.mechanicTypeManual ? (mechanic.tankMaxHp ?? 0) : tankMaxHpFromEvent(match),
|
||||
assignments,
|
||||
};
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user