diff --git a/api/analysis.php b/api/analysis.php index 61f86ac..ef81a15 100644 --- a/api/analysis.php +++ b/api/analysis.php @@ -88,6 +88,7 @@ const MITIGATION_ABILITIES = [ 'Dark Missionary' => ['dr' => 10, 'buffType' => 'buff', 'statusId' => 1001894, 'extraAbilityGameID' => 16471], 'Heart of Light' => ['dr' => 10, 'buffType' => 'buff', 'statusId' => 1001839, 'extraAbilityGameID' => 16160], '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 'Sacred Soil' => ['dr' => 10, 'buffType' => 'buff', 'statusId' => 1001944, 'extraAbilityGameID' => 188], 'Expedient' => ['dr' => 10, 'buffType' => 'buff', 'statusId' => 1002711, 'extraAbilityGameID' => 25868], // FFLogs: "Desperate Measures" diff --git a/api/cache.php b/api/cache.php index b06f825..d39879d 100644 --- a/api/cache.php +++ b/api/cache.php @@ -2,7 +2,7 @@ declare(strict_types=1); const CACHED_LOG_DIR = __DIR__ . '/../cached_logs'; -const CACHED_LOG_VERSION = 'v9'; +const CACHED_LOG_VERSION = 'v11'; function cache_language(string $language): string { $language = strtolower(trim($language)); diff --git a/assets/icons/mitigation/plenary-indulgence.png b/assets/icons/mitigation/plenary-indulgence.png new file mode 100644 index 0000000..3aac192 Binary files /dev/null and b/assets/icons/mitigation/plenary-indulgence.png differ diff --git a/assets/jsons/Action.json b/assets/jsons/Action.json index ddb34e8..0e85458 100644 --- a/assets/jsons/Action.json +++ b/assets/jsons/Action.json @@ -419,6 +419,18 @@ "icon": "https://xivapi.com/i/003000/003087_hr1.png", "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": { "cast": 0, "recast": 1200, diff --git a/js/analysis.js b/js/analysis.js index 5b895dc..0f545e3 100644 --- a/js/analysis.js +++ b/js/analysis.js @@ -1,5 +1,5 @@ (function () { - const { MITIG_ICONS, JOB_ABBR, ABILITY_JOBS, JOB_ROLE, abilityTypeIsPhysical, abilityTypeIsMagical } = window.FF14_DATA; + const { MITIG_ICONS, JOB_ABBR, ABILITY_JOBS, JOB_ROLE, abilityTypeIsPhysical, abilityTypeIsMagical, abilityDr } = window.FF14_DATA; function dmgTypeBadge(abilityType) { if (abilityType == null) return ''; @@ -612,7 +612,8 @@ ].map(m => { const iconSrc = mitigationIcon(m); if (!iconSrc) return ''; - const dr = m.dr > 0 ? ` −${m.dr}%` : ''; + const drPct = Math.round(abilityDr(m.name, ev.abilityType) * 100) || m.dr || 0; + const dr = drPct > 0 ? ` −${drPct}%` : ''; const jobAbbr = m.sourcePlayerType ? (JOB_ABBR[m.sourcePlayerType] ?? '') : ''; const label = jobAbbr ? `${jobAbbr} · ${m.name}` : m.name; return m.missing diff --git a/js/ffxiv-data.js b/js/ffxiv-data.js index 3f43a77..15f1cea 100644 --- a/js/ffxiv-data.js +++ b/js/ffxiv-data.js @@ -10,6 +10,13 @@ function abilityTypeIsPhysical(type) { return (parseInt(type) & ABILITY_TYPE_PHYSICAL) !== 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 = { 'Paladin': 'PLD', 'Warrior': 'WAR', 'DarkKnight': 'DRK', 'Gunbreaker': 'GNB', 'WhiteMage': 'WHM', 'Scholar': 'SCH', 'Astrologian': 'AST', 'Sage': 'SGE', @@ -89,6 +96,7 @@ ], 'WHM': [ { 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: 'Divine Benison', buffType: 'shield', extraAbilityGameID: 7432, duration: 15 }, { name: 'Divine Caress', buffType: 'shield' }, @@ -177,7 +185,7 @@ 'Heart of Light': 'GNB', 'Superbolide': 'GNB', 'Nebula': 'GNB', 'Great Nebula': 'GNB', 'Camouflage': 'GNB', 'Heart of Stone': 'GNB', 'Heart of Corundum': 'GNB', 'Clarity of Corundum': 'GNB', - 'Temperance': 'WHM', 'Aquaveil': 'WHM', 'Divine Benison': 'WHM', 'Divine Caress': 'WHM', + 'Temperance': 'WHM', 'Plenary Indulgence': 'WHM', 'Aquaveil': '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', 'Exaltation': 'AST', 'Neutral Sect': 'AST', @@ -204,6 +212,7 @@ 'Dark Missionary': 'assets/icons/mitigation/dark-missionary.png', 'Heart of Light': 'assets/icons/mitigation/heart-of-light.png', 'Temperance': 'assets/icons/mitigation/temperance.png', + 'Plenary Indulgence': 'assets/icons/mitigation/plenary-indulgence.png', 'Sacred Soil': 'assets/icons/mitigation/sacred-soil.png', 'Expedient': 'assets/icons/mitigation/expedient.png', 'Fey Illumination': 'assets/icons/mitigation/fey-illumination.png', @@ -258,6 +267,7 @@ 'Dark Missionary': 0.10, 'Heart of Light': 0.10, 'Temperance': 0.10, + 'Plenary Indulgence': 0.10, 'Sacred Soil': 0.10, 'Expedient': 0.10, 'Collective Unconscious': 0.10, @@ -323,5 +333,6 @@ ABILITY_TYPE_MAGICAL, abilityTypeIsPhysical, abilityTypeIsMagical, + abilityDr, }; })(); diff --git a/js/planner.js b/js/planner.js index 91cda55..78df6ee 100644 --- a/js/planner.js +++ b/js/planner.js @@ -651,7 +651,7 @@ function simulateDrMultiplier(mechanic, assignments = mechanic.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)); + mult *= (1 - abilityDr(a.ability, mechanic.abilityType)); } return mult; } @@ -2340,6 +2340,7 @@ const { TANK_JOBS, abilityTypeIsPhysical, abilityTypeIsMagical, + abilityDr, } = window.FF14_DATA; function dmgTypeBadge(abilityType) {