forked from xziino/ff14-mitigator
Compare commits
No commits in common. "76f9baec40d796fa939f2e988828b14cd3273781" and "1e881be4820e223a7e648cc535c71056a2b90379" have entirely different histories.
76f9baec40
...
1e881be482
@ -88,7 +88,6 @@ const MITIGATION_ABILITIES = [
|
|||||||
'Dark Missionary' => ['dr' => 10, 'buffType' => 'buff', 'statusId' => 1001894, 'extraAbilityGameID' => 16471],
|
'Dark Missionary' => ['dr' => 10, 'buffType' => 'buff', 'statusId' => 1001894, 'extraAbilityGameID' => 16471],
|
||||||
'Heart of Light' => ['dr' => 10, 'buffType' => 'buff', 'statusId' => 1001839, 'extraAbilityGameID' => 16160],
|
'Heart of Light' => ['dr' => 10, 'buffType' => 'buff', 'statusId' => 1001839, 'extraAbilityGameID' => 16160],
|
||||||
'Temperance' => ['dr' => 10, 'buffType' => 'buff', 'statusId' => 1001873, 'extraAbilityGameID' => 16536],
|
'Temperance' => ['dr' => 10, 'buffType' => 'buff', 'statusId' => 1001873, 'extraAbilityGameID' => 16536],
|
||||||
'Plenary Indulgence' => ['dr' => 10, 'buffType' => 'buff', 'statusId' => 1001219, 'extraAbilityGameID' => 7433], // FFLogs: "Confession"
|
|
||||||
'Aquaveil' => ['dr' => 15, 'buffType' => 'buff', 'statusId' => 1002708, 'extraAbilityGameID' => 25861], // Personal, WHM auf Ziel
|
'Aquaveil' => ['dr' => 15, 'buffType' => 'buff', 'statusId' => 1002708, 'extraAbilityGameID' => 25861], // Personal, WHM auf Ziel
|
||||||
'Sacred Soil' => ['dr' => 10, 'buffType' => 'buff', 'statusId' => 1001944, 'extraAbilityGameID' => 188],
|
'Sacred Soil' => ['dr' => 10, 'buffType' => 'buff', 'statusId' => 1001944, 'extraAbilityGameID' => 188],
|
||||||
'Expedient' => ['dr' => 10, 'buffType' => 'buff', 'statusId' => 1002711, 'extraAbilityGameID' => 25868], // FFLogs: "Desperate Measures"
|
'Expedient' => ['dr' => 10, 'buffType' => 'buff', 'statusId' => 1002711, 'extraAbilityGameID' => 25868], // FFLogs: "Desperate Measures"
|
||||||
@ -102,7 +101,7 @@ const MITIGATION_ABILITIES = [
|
|||||||
'Shield Samba' => ['dr' => 15, 'buffType' => 'buff', 'statusId' => 1001826, 'extraAbilityGameID' => 16012],
|
'Shield Samba' => ['dr' => 15, 'buffType' => 'buff', 'statusId' => 1001826, 'extraAbilityGameID' => 16012],
|
||||||
'Magick Barrier' => ['dr' => 10, 'buffType' => 'buff', 'statusId' => 1002707, 'extraAbilityGameID' => 25857],
|
'Magick Barrier' => ['dr' => 10, 'buffType' => 'buff', 'statusId' => 1002707, 'extraAbilityGameID' => 25857],
|
||||||
// ── Personal / targeted mitigation ─────────────────────────────────────
|
// ── Personal / targeted mitigation ─────────────────────────────────────
|
||||||
'Rampart' => ['dr' => 20, 'buffType' => 'buff', 'statusId' => 1001191, 'extraAbilityGameID' => 7531],
|
'Rampart' => ['dr' => 20, 'buffType' => 'buff', 'extraAbilityGameID' => 7531],
|
||||||
// PLD
|
// PLD
|
||||||
'Hallowed Ground' => ['dr' => 100, 'buffType' => 'buff', 'extraAbilityGameID' => 30],
|
'Hallowed Ground' => ['dr' => 100, 'buffType' => 'buff', 'extraAbilityGameID' => 30],
|
||||||
'Sentinel' => ['dr' => 30, 'buffType' => 'buff', 'extraAbilityGameID' => 17],
|
'Sentinel' => ['dr' => 30, 'buffType' => 'buff', 'extraAbilityGameID' => 17],
|
||||||
@ -252,7 +251,6 @@ $pdResult = fflogs_gql(<<<GQL
|
|||||||
abilities {
|
abilities {
|
||||||
gameID
|
gameID
|
||||||
name
|
name
|
||||||
type
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -263,12 +261,10 @@ GQL);
|
|||||||
if (isset($pdResult['_reauth'])) { echo json_encode(['reauth' => true]); exit; }
|
if (isset($pdResult['_reauth'])) { echo json_encode(['reauth' => true]); exit; }
|
||||||
if (isset($pdResult['_curl_error'])) { http_response_code(502); echo json_encode(['error' => $pdResult['_curl_error']]); exit; }
|
if (isset($pdResult['_curl_error'])) { http_response_code(502); echo json_encode(['error' => $pdResult['_curl_error']]); exit; }
|
||||||
|
|
||||||
// abilityGameID/statusID → display name + damage type
|
// abilityGameID/statusID → display name
|
||||||
$abilityNames = [];
|
$abilityNames = [];
|
||||||
$abilityTypes = [];
|
|
||||||
foreach ($pdResult['data']['reportData']['report']['masterData']['abilities'] ?? [] as $ab) {
|
foreach ($pdResult['data']['reportData']['report']['masterData']['abilities'] ?? [] as $ab) {
|
||||||
$abilityNames[(int)$ab['gameID']] = $ab['name'];
|
$abilityNames[(int)$ab['gameID']] = $ab['name'];
|
||||||
if (isset($ab['type'])) $abilityTypes[(int)$ab['gameID']] = (int)$ab['type'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// gameID → mitigation meta: primary from masterData, statusId fallback for
|
// gameID → mitigation meta: primary from masterData, statusId fallback for
|
||||||
@ -528,7 +524,6 @@ foreach ($allEvents as $ev) {
|
|||||||
'maxHp' => (int)($ev['targetResources']['maxHitPoints'] ?? 0),
|
'maxHp' => (int)($ev['targetResources']['maxHitPoints'] ?? 0),
|
||||||
'buffs' => $ev['buffs'] ?? '',
|
'buffs' => $ev['buffs'] ?? '',
|
||||||
'name' => $abilityNames[$abId] ?? $ev['ability']['name'] ?? ('Ability #' . $abId),
|
'name' => $abilityNames[$abId] ?? $ev['ability']['name'] ?? ('Ability #' . $abId),
|
||||||
'abilityType' => isset($ev['ability']['type']) ? (int)$ev['ability']['type'] : ($abilityTypes[$abId] ?? null),
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -548,7 +543,6 @@ foreach ($byAbility as $abId => $events) {
|
|||||||
'timestamp' => (int)$ev['ts'],
|
'timestamp' => (int)$ev['ts'],
|
||||||
'abilityId' => $abId,
|
'abilityId' => $abId,
|
||||||
'abilityName' => $ev['name'],
|
'abilityName' => $ev['name'],
|
||||||
'abilityType' => $ev['abilityType'] ?? null,
|
|
||||||
'targets' => [],
|
'targets' => [],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -639,7 +633,6 @@ foreach ($clusters as $group) {
|
|||||||
'timestamp' => $group['timestamp'],
|
'timestamp' => $group['timestamp'],
|
||||||
'abilityId' => $group['abilityId'],
|
'abilityId' => $group['abilityId'],
|
||||||
'abilityName' => $group['abilityName'],
|
'abilityName' => $group['abilityName'],
|
||||||
'abilityType' => $group['abilityType'] ?? null,
|
|
||||||
'targets' => $targets,
|
'targets' => $targets,
|
||||||
'totalDamage' => array_sum(array_column($targets, 'amount')),
|
'totalDamage' => array_sum(array_column($targets, 'amount')),
|
||||||
'isHeavyTankbuster' => $isHeavyTankbuster,
|
'isHeavyTankbuster' => $isHeavyTankbuster,
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
const CACHED_LOG_DIR = __DIR__ . '/../cached_logs';
|
const CACHED_LOG_DIR = __DIR__ . '/../cached_logs';
|
||||||
const CACHED_LOG_VERSION = 'v12';
|
const CACHED_LOG_VERSION = 'v8';
|
||||||
|
|
||||||
function cache_language(string $language): string {
|
function cache_language(string $language): string {
|
||||||
$language = strtolower(trim($language));
|
$language = strtolower(trim($language));
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 5.1 KiB |
@ -419,18 +419,6 @@
|
|||||||
"icon": "https://xivapi.com/i/003000/003087_hr1.png",
|
"icon": "https://xivapi.com/i/003000/003087_hr1.png",
|
||||||
"shield": null
|
"shield": null
|
||||||
},
|
},
|
||||||
"7433": {
|
|
||||||
"cast": 0,
|
|
||||||
"recast": 600,
|
|
||||||
"names": {
|
|
||||||
"en": "Plenary Indulgence",
|
|
||||||
"de": "Vollkommener Ablass",
|
|
||||||
"fr": "Indulgence plénière",
|
|
||||||
"jp": "インドゥルゲンティア"
|
|
||||||
},
|
|
||||||
"icon": null,
|
|
||||||
"shield": null
|
|
||||||
},
|
|
||||||
"16536": {
|
"16536": {
|
||||||
"cast": 0,
|
"cast": 0,
|
||||||
"recast": 1200,
|
"recast": 1200,
|
||||||
|
|||||||
@ -143,10 +143,6 @@ select option { background: var(--bg2); }
|
|||||||
.badge-gold { background: var(--goldbg); border-color: rgba(200,168,75,.4); color: var(--gold); }
|
.badge-gold { background: var(--goldbg); border-color: rgba(200,168,75,.4); color: var(--gold); }
|
||||||
.badge-planned { opacity: 0.55; border-style: dashed; }
|
.badge-planned { opacity: 0.55; border-style: dashed; }
|
||||||
|
|
||||||
.dmg-type-badge { font-size: 11px; font-weight: 600; padding: 1px 5px; border-radius: 3px; letter-spacing: .03em; }
|
|
||||||
.dmg-type--phys { background: rgba(230,140,60,.15); color: var(--orange); border: 1px solid rgba(230,140,60,.35); }
|
|
||||||
.dmg-type--mag { background: rgba(74,158,255,.12); color: var(--blue); border: 1px solid rgba(74,158,255,.3); }
|
|
||||||
|
|
||||||
/* ── DR Bar ─────────────────────────────────────────────────────────────────── */
|
/* ── DR Bar ─────────────────────────────────────────────────────────────────── */
|
||||||
.dr-bar-wrap {
|
.dr-bar-wrap {
|
||||||
background: var(--bg2);
|
background: var(--bg2);
|
||||||
|
|||||||
@ -276,9 +276,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mechanic-name {
|
.mechanic-name {
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
color: var(--t1);
|
color: var(--t1);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
@ -920,9 +917,6 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 8px;
|
top: 8px;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 4px;
|
|
||||||
max-width: 150px;
|
max-width: 150px;
|
||||||
padding: 5px 8px;
|
padding: 5px 8px;
|
||||||
border: 1px solid rgba(224,92,92,.35);
|
border: 1px solid rgba(224,92,92,.35);
|
||||||
@ -932,6 +926,8 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -951,15 +947,6 @@
|
|||||||
background: rgba(177,112,255,.28);
|
background: rgba(177,112,255,.28);
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeline-boss-action-name {
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
.timeline-boss-action .dmg-type-badge {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeline-mitigation {
|
.timeline-mitigation {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 6px;
|
top: 6px;
|
||||||
|
|||||||
@ -1,12 +1,5 @@
|
|||||||
(function () {
|
(function () {
|
||||||
const { MITIG_ICONS, JOB_ABBR, ABILITY_JOBS, JOB_ROLE, abilityTypeIsPhysical, abilityTypeIsMagical, abilityDr } = window.FF14_DATA;
|
const { MITIG_ICONS, JOB_ABBR, ABILITY_JOBS, JOB_ROLE } = window.FF14_DATA;
|
||||||
|
|
||||||
function dmgTypeBadge(abilityType) {
|
|
||||||
if (abilityType == null) return '';
|
|
||||||
if (abilityTypeIsPhysical(abilityType)) return '<span class="dmg-type-badge dmg-type--phys">Phys</span>';
|
|
||||||
if (abilityTypeIsMagical(abilityType)) return '<span class="dmg-type-badge dmg-type--mag">Mag</span>';
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deduplicated list of all mitigations across all targets of a ref event
|
// Deduplicated list of all mitigations across all targets of a ref event
|
||||||
function collectRefMitigs(refEvent) {
|
function collectRefMitigs(refEvent) {
|
||||||
@ -612,8 +605,7 @@
|
|||||||
].map(m => {
|
].map(m => {
|
||||||
const iconSrc = mitigationIcon(m);
|
const iconSrc = mitigationIcon(m);
|
||||||
if (!iconSrc) return '';
|
if (!iconSrc) return '';
|
||||||
const drPct = Math.round(abilityDr(m.name, ev.abilityType) * 100) || m.dr || 0;
|
const dr = m.dr > 0 ? ` −${m.dr}%` : '';
|
||||||
const dr = drPct > 0 ? ` −${drPct}%` : '';
|
|
||||||
const jobAbbr = m.sourcePlayerType ? (JOB_ABBR[m.sourcePlayerType] ?? '') : '';
|
const jobAbbr = m.sourcePlayerType ? (JOB_ABBR[m.sourcePlayerType] ?? '') : '';
|
||||||
const label = jobAbbr ? `${jobAbbr} · ${m.name}` : m.name;
|
const label = jobAbbr ? `${jobAbbr} · ${m.name}` : m.name;
|
||||||
return m.missing
|
return m.missing
|
||||||
@ -766,7 +758,6 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="aoe-ability">
|
<div class="aoe-ability">
|
||||||
${ev.abilityName}
|
${ev.abilityName}
|
||||||
${dmgTypeBadge(ev.abilityType)}
|
|
||||||
<span class="aoe-total">— ${fmtDmg(ev.totalDamage)} total</span>
|
<span class="aoe-total">— ${fmtDmg(ev.totalDamage)} total</span>
|
||||||
${debuffIconsHtml}
|
${debuffIconsHtml}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -10,13 +10,6 @@
|
|||||||
function abilityTypeIsPhysical(type) { return (parseInt(type) & ABILITY_TYPE_PHYSICAL) !== 0; }
|
function abilityTypeIsPhysical(type) { return (parseInt(type) & ABILITY_TYPE_PHYSICAL) !== 0; }
|
||||||
function abilityTypeIsMagical(type) { return (parseInt(type) & ABILITY_TYPE_MAGICAL) !== 0; }
|
function abilityTypeIsMagical(type) { return (parseInt(type) & ABILITY_TYPE_MAGICAL) !== 0; }
|
||||||
|
|
||||||
// Type-aware DR: Feint 10% phys / 5% mag, Addle 10% mag / 5% phys
|
|
||||||
function abilityDr(abilityName, abilityType) {
|
|
||||||
if (abilityName === 'Feint') return abilityTypeIsPhysical(abilityType) ? 0.10 : 0.05;
|
|
||||||
if (abilityName === 'Addle') return abilityTypeIsMagical(abilityType) ? 0.10 : 0.05;
|
|
||||||
return ABILITY_DR[abilityName] ?? 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const JOB_FROM_TYPE = {
|
const JOB_FROM_TYPE = {
|
||||||
'Paladin': 'PLD', 'Warrior': 'WAR', 'DarkKnight': 'DRK', 'Gunbreaker': 'GNB',
|
'Paladin': 'PLD', 'Warrior': 'WAR', 'DarkKnight': 'DRK', 'Gunbreaker': 'GNB',
|
||||||
'WhiteMage': 'WHM', 'Scholar': 'SCH', 'Astrologian': 'AST', 'Sage': 'SGE',
|
'WhiteMage': 'WHM', 'Scholar': 'SCH', 'Astrologian': 'AST', 'Sage': 'SGE',
|
||||||
@ -96,7 +89,6 @@
|
|||||||
],
|
],
|
||||||
'WHM': [
|
'WHM': [
|
||||||
{ name: 'Temperance', buffType: 'buff' },
|
{ name: 'Temperance', buffType: 'buff' },
|
||||||
{ name: 'Plenary Indulgence', buffType: 'buff', extraAbilityGameID: 7433, duration: 10 },
|
|
||||||
{ name: 'Aquaveil', buffType: 'buff', extraAbilityGameID: 25861, duration: 8 }, // Personal, WHM auf Ziel
|
{ name: 'Aquaveil', buffType: 'buff', extraAbilityGameID: 25861, duration: 8 }, // Personal, WHM auf Ziel
|
||||||
{ name: 'Divine Benison', buffType: 'shield', extraAbilityGameID: 7432, duration: 15 },
|
{ name: 'Divine Benison', buffType: 'shield', extraAbilityGameID: 7432, duration: 15 },
|
||||||
{ name: 'Divine Caress', buffType: 'shield' },
|
{ name: 'Divine Caress', buffType: 'shield' },
|
||||||
@ -185,7 +177,7 @@
|
|||||||
'Heart of Light': 'GNB', 'Superbolide': 'GNB', 'Nebula': 'GNB',
|
'Heart of Light': 'GNB', 'Superbolide': 'GNB', 'Nebula': 'GNB',
|
||||||
'Great Nebula': 'GNB', 'Camouflage': 'GNB', 'Heart of Stone': 'GNB',
|
'Great Nebula': 'GNB', 'Camouflage': 'GNB', 'Heart of Stone': 'GNB',
|
||||||
'Heart of Corundum': 'GNB', 'Clarity of Corundum': 'GNB',
|
'Heart of Corundum': 'GNB', 'Clarity of Corundum': 'GNB',
|
||||||
'Temperance': 'WHM', 'Plenary Indulgence': 'WHM', 'Aquaveil': 'WHM', 'Divine Benison': 'WHM', 'Divine Caress': 'WHM',
|
'Temperance': 'WHM', 'Aquaveil': 'WHM', 'Divine Benison': 'WHM', 'Divine Caress': 'WHM',
|
||||||
'Sacred Soil': 'SCH', 'Expedient': 'SCH', 'Fey Illumination': 'SCH',
|
'Sacred Soil': 'SCH', 'Expedient': 'SCH', 'Fey Illumination': 'SCH',
|
||||||
'Galvanize': 'SCH', 'Seraphic Veil': 'SCH', 'Catalyze': 'SCH',
|
'Galvanize': 'SCH', 'Seraphic Veil': 'SCH', 'Catalyze': 'SCH',
|
||||||
'Collective Unconscious': 'AST', 'Exaltation': 'AST', 'Neutral Sect': 'AST',
|
'Collective Unconscious': 'AST', 'Exaltation': 'AST', 'Neutral Sect': 'AST',
|
||||||
@ -212,7 +204,6 @@
|
|||||||
'Dark Missionary': 'assets/icons/mitigation/dark-missionary.png',
|
'Dark Missionary': 'assets/icons/mitigation/dark-missionary.png',
|
||||||
'Heart of Light': 'assets/icons/mitigation/heart-of-light.png',
|
'Heart of Light': 'assets/icons/mitigation/heart-of-light.png',
|
||||||
'Temperance': 'assets/icons/mitigation/temperance.png',
|
'Temperance': 'assets/icons/mitigation/temperance.png',
|
||||||
'Plenary Indulgence': 'assets/icons/mitigation/plenary-indulgence.png',
|
|
||||||
'Sacred Soil': 'assets/icons/mitigation/sacred-soil.png',
|
'Sacred Soil': 'assets/icons/mitigation/sacred-soil.png',
|
||||||
'Expedient': 'assets/icons/mitigation/expedient.png',
|
'Expedient': 'assets/icons/mitigation/expedient.png',
|
||||||
'Fey Illumination': 'assets/icons/mitigation/fey-illumination.png',
|
'Fey Illumination': 'assets/icons/mitigation/fey-illumination.png',
|
||||||
@ -267,7 +258,6 @@
|
|||||||
'Dark Missionary': 0.10,
|
'Dark Missionary': 0.10,
|
||||||
'Heart of Light': 0.10,
|
'Heart of Light': 0.10,
|
||||||
'Temperance': 0.10,
|
'Temperance': 0.10,
|
||||||
'Plenary Indulgence': 0.10,
|
|
||||||
'Sacred Soil': 0.10,
|
'Sacred Soil': 0.10,
|
||||||
'Expedient': 0.10,
|
'Expedient': 0.10,
|
||||||
'Collective Unconscious': 0.10,
|
'Collective Unconscious': 0.10,
|
||||||
@ -333,6 +323,5 @@
|
|||||||
ABILITY_TYPE_MAGICAL,
|
ABILITY_TYPE_MAGICAL,
|
||||||
abilityTypeIsPhysical,
|
abilityTypeIsPhysical,
|
||||||
abilityTypeIsMagical,
|
abilityTypeIsMagical,
|
||||||
abilityDr,
|
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|||||||
@ -651,7 +651,7 @@ function simulateDrMultiplier(mechanic, assignments = mechanic.assignments ?? []
|
|||||||
if (a.buffType === 'shield') continue;
|
if (a.buffType === 'shield') continue;
|
||||||
// Persönliche Mitigation nur bei Tankbustern einrechnen
|
// Persönliche Mitigation nur bei Tankbustern einrechnen
|
||||||
if (!isTankbuster && TIMELINE_PERSONAL_ABILITIES.has(a.ability)) continue;
|
if (!isTankbuster && TIMELINE_PERSONAL_ABILITIES.has(a.ability)) continue;
|
||||||
mult *= (1 - abilityDr(a.ability, mechanic.abilityType));
|
mult *= (1 - (ABILITY_DR[a.ability] ?? 0));
|
||||||
}
|
}
|
||||||
return mult;
|
return mult;
|
||||||
}
|
}
|
||||||
@ -749,7 +749,7 @@ function renderMechanicListHtml(plan) {
|
|||||||
<div class="mechanic-time">${escHtml(fmtTimestamp(m.timestamp))}</div>
|
<div class="mechanic-time">${escHtml(fmtTimestamp(m.timestamp))}</div>
|
||||||
<div class="mechanic-body">
|
<div class="mechanic-body">
|
||||||
${m.phase ? `<div class="mechanic-phase">${escHtml(m.phase)}</div>` : ''}
|
${m.phase ? `<div class="mechanic-phase">${escHtml(m.phase)}</div>` : ''}
|
||||||
<div class="mechanic-name">${escHtml(m.name)}${dmgTypeBadge(m.abilityType)}</div>
|
<div class="mechanic-name">${escHtml(m.name)}</div>
|
||||||
${m.unmitigatedDamage
|
${m.unmitigatedDamage
|
||||||
? `<div class="mechanic-dmg">${fmtNumber(m.unmitigatedDamage)} unmitigiert${avgHp ? ` <span class="mechanic-avg-hp">∅ ${fmtNumber(avgHp)} HP</span>` : ''}</div>`
|
? `<div class="mechanic-dmg">${fmtNumber(m.unmitigatedDamage)} unmitigiert${avgHp ? ` <span class="mechanic-avg-hp">∅ ${fmtNumber(avgHp)} HP</span>` : ''}</div>`
|
||||||
: ''
|
: ''
|
||||||
@ -1258,7 +1258,7 @@ function renderTimelineHtml(plan) {
|
|||||||
style="left:${left}%;top:${8 + lane * 30}px"
|
style="left:${left}%;top:${8 + lane * 30}px"
|
||||||
data-mechanic-id="${escHtml(m.id)}"
|
data-mechanic-id="${escHtml(m.id)}"
|
||||||
title="${escHtml(fmtTimestamp(m.timestamp))} · ${escHtml(m.name)} · ${m.isHeavyTankbuster ? 'Tankbuster' : 'AoE'}">
|
title="${escHtml(fmtTimestamp(m.timestamp))} · ${escHtml(m.name)} · ${m.isHeavyTankbuster ? 'Tankbuster' : 'AoE'}">
|
||||||
<span class="timeline-boss-action-name">${escHtml(m.name)}</span>${dmgTypeBadge(m.abilityType)}
|
${escHtml(m.name)}
|
||||||
</button>
|
</button>
|
||||||
`).join('');
|
`).join('');
|
||||||
|
|
||||||
@ -2338,17 +2338,7 @@ const {
|
|||||||
MELEE_JOBS,
|
MELEE_JOBS,
|
||||||
MITIG_ICONS,
|
MITIG_ICONS,
|
||||||
TANK_JOBS,
|
TANK_JOBS,
|
||||||
abilityTypeIsPhysical,
|
|
||||||
abilityTypeIsMagical,
|
|
||||||
abilityDr,
|
|
||||||
} = window.FF14_DATA;
|
} = window.FF14_DATA;
|
||||||
|
|
||||||
function dmgTypeBadge(abilityType) {
|
|
||||||
if (abilityType == null) return '';
|
|
||||||
if (abilityTypeIsPhysical(abilityType)) return '<span class="dmg-type-badge dmg-type--phys">Phys</span>';
|
|
||||||
if (abilityTypeIsMagical(abilityType)) return '<span class="dmg-type-badge dmg-type--mag">Mag</span>';
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
// Groups of abilities that are functionally equivalent across different jobs.
|
// Groups of abilities that are functionally equivalent across different jobs.
|
||||||
// Used to suggest replacements when a job is missing from the composition.
|
// Used to suggest replacements when a job is missing from the composition.
|
||||||
const ABILITY_EQUIVALENTS = [
|
const ABILITY_EQUIVALENTS = [
|
||||||
@ -2452,7 +2442,6 @@ function aoeEventsToMechanics(aoeEvents, fightStart, phases, players, withMitiga
|
|||||||
id: crypto.randomUUID(),
|
id: crypto.randomUUID(),
|
||||||
name: ev.abilityName,
|
name: ev.abilityName,
|
||||||
abilityId: ev.abilityId,
|
abilityId: ev.abilityId,
|
||||||
abilityType: ev.abilityType ?? null,
|
|
||||||
timestamp: relTs,
|
timestamp: relTs,
|
||||||
phase: phase?.name ?? '',
|
phase: phase?.name ?? '',
|
||||||
unmitigatedDamage: avgUnmit,
|
unmitigatedDamage: avgUnmit,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user