From 4c34d83c82a02c077d7053d09fc73ea6217df32e Mon Sep 17 00:00:00 2001 From: Vollmer Date: Tue, 10 Sep 2024 05:47:35 +0200 Subject: [PATCH] [Evoker] Update MajorDefensive module for Lifecinders & Hardened Scales (#7004) * add Hardened Scales multiplier to ObsidianScales * prevent innocent removebuff events from being removed * update castlinks * custom trigger to avoid external buff * use generic getter everywhere * update generic getter * update structure to include partnerAmount * show partner amount in CooldownDetails * changelogs --- .../retail/evoker/augmentation/CHANGELOG.tsx | 1 + .../retail/evoker/devastation/CHANGELOG.tsx | 1 + .../retail/evoker/shared/constants.ts | 1 + .../MajorDefensives/ObsidianScales.tsx | 12 ++- .../modules/MajorDefensives/RenewingBlaze.tsx | 102 +++++++++++------- .../DefensiveCastLinkNormalizer.ts | 20 +--- .../normalizers/DefensiveNormalizer.ts | 7 +- 7 files changed, 84 insertions(+), 60 deletions(-) diff --git a/src/analysis/retail/evoker/augmentation/CHANGELOG.tsx b/src/analysis/retail/evoker/augmentation/CHANGELOG.tsx index 58f1d136236..bff7963bae5 100644 --- a/src/analysis/retail/evoker/augmentation/CHANGELOG.tsx +++ b/src/analysis/retail/evoker/augmentation/CHANGELOG.tsx @@ -4,6 +4,7 @@ import { SpellLink } from 'interface'; import TALENTS from 'common/TALENTS/evoker'; export default [ + change(date(2024, 9, 6), <>Update MajorDefensive module for and , Vollmer), change(date(2024, 9, 6), <>Implement module, Vollmer), change(date(2024, 9, 6), <>Implement module, Vollmer), change(date(2024, 8, 14), <>Implement module, Vollmer), diff --git a/src/analysis/retail/evoker/devastation/CHANGELOG.tsx b/src/analysis/retail/evoker/devastation/CHANGELOG.tsx index a228971f24c..09892112ed1 100644 --- a/src/analysis/retail/evoker/devastation/CHANGELOG.tsx +++ b/src/analysis/retail/evoker/devastation/CHANGELOG.tsx @@ -5,6 +5,7 @@ import { TALENTS_EVOKER } from 'common/TALENTS/evoker'; import SPELLS from 'common/SPELLS/evoker'; export default [ + change(date(2024, 9, 6), <>Update MajorDefensive module for and , Vollmer), change(date(2024, 9, 6), <>Implement module, Vollmer), change(date(2024, 9, 6), <>Implement module, Vollmer), change(date(2024, 8, 14), <>Implement module, Vollmer), diff --git a/src/analysis/retail/evoker/shared/constants.ts b/src/analysis/retail/evoker/shared/constants.ts index 4286e3590ac..9d8423d3b63 100644 --- a/src/analysis/retail/evoker/shared/constants.ts +++ b/src/analysis/retail/evoker/shared/constants.ts @@ -79,6 +79,7 @@ export const IMMINENT_DESTRUCTION_ESSENCE_REDUCTION = 1; export const MELT_ARMOR_MULTIPLIER = 0.2; export const MIGHT_OF_THE_BLACK_DRAGONFLIGHT_MULTIPLIER = 0.2; export const UNRELENTING_SIEGE_MULTIPLIER_PER_STACK = 0.01; +export const HARDENED_SCALES_MITIGATION = 0.1; export const MASS_DISINTEGRATE_MULTIPLIER_PER_MISSING_TARGET = 0.15; export const MASS_ERUPTION_MULTIPLIER_PER_MISSING_TARGET = diff --git a/src/analysis/retail/evoker/shared/modules/MajorDefensives/ObsidianScales.tsx b/src/analysis/retail/evoker/shared/modules/MajorDefensives/ObsidianScales.tsx index 94516f8936b..d399f0acd98 100644 --- a/src/analysis/retail/evoker/shared/modules/MajorDefensives/ObsidianScales.tsx +++ b/src/analysis/retail/evoker/shared/modules/MajorDefensives/ObsidianScales.tsx @@ -9,13 +9,12 @@ import Events, { DamageEvent } from 'parser/core/Events'; import { SpellLink } from 'interface'; import MajorDefensiveStatistic from 'interface/MajorDefensiveStatistic'; import STATISTIC_CATEGORY from 'parser/ui/STATISTIC_CATEGORY'; +import { HARDENED_SCALES_MITIGATION } from '../../constants'; -// TWW ScaleCommander hero talent has a +5% buff so just preparing for that. const BASE_MITIGATION = 0.3; -const HARDENED_SCALES_MITIGATION = 0.05; class ObsidianScales extends MajorDefensiveBuff { - hasHardenedScales = false; + hasHardenedScales = this.selectedCombatant.hasTalent(TALENTS.HARDENED_SCALES_TALENT); mitPct: number = BASE_MITIGATION + (this.hasHardenedScales ? HARDENED_SCALES_MITIGATION : 0); constructor(options: Options) { @@ -39,6 +38,13 @@ class ObsidianScales extends MajorDefensiveBuff { return (

reduces the damage you take by 30%. + {this.hasHardenedScales && ( + <> +
+ increases this mitigation to{' '} + {this.mitPct * 100}%. + + )}

); } diff --git a/src/analysis/retail/evoker/shared/modules/MajorDefensives/RenewingBlaze.tsx b/src/analysis/retail/evoker/shared/modules/MajorDefensives/RenewingBlaze.tsx index c8cb314be19..3fca4fe72b3 100644 --- a/src/analysis/retail/evoker/shared/modules/MajorDefensives/RenewingBlaze.tsx +++ b/src/analysis/retail/evoker/shared/modules/MajorDefensives/RenewingBlaze.tsx @@ -7,11 +7,10 @@ import MajorDefensive, { } from 'interface/guide/components/MajorDefensives/MajorDefensiveAnalyzer'; import { Options, SELECTED_PLAYER } from 'parser/core/Analyzer'; import TALENTS from 'common/TALENTS/evoker'; -import SPELLS from 'common/SPELLS/evoker'; import Events, { ApplyBuffEvent, DamageEvent, - GetRelatedEvent, + GetRelatedEvents, HealEvent, } from 'parser/core/Events'; import { SpellLink } from 'interface'; @@ -35,34 +34,43 @@ import BoringValue from 'parser/ui/BoringValueText'; import MAGIC_SCHOOLS, { color } from 'game/MAGIC_SCHOOLS'; import { QualitativePerformance } from 'parser/ui/QualitativePerformance'; import { ReactNode } from 'react'; -import { - RENEWING_BLAZE_BUFFS, - RENEWING_BLAZE_HEAL, -} from '../normalizers/DefensiveCastLinkNormalizer'; +import { RENEWING_BLAZE_HEAL } from '../normalizers/DefensiveCastLinkNormalizer'; type RenewingBlazeHealBuff = { start: ApplyBuffEvent; - events: HealEvent[]; amount: number; overheal: number; + partnerAmount: number; }; class RenewingBlaze extends MajorDefensiveBuff { renewingBlazeHealBuffs: RenewingBlazeHealBuff[] = []; normalizedMitigations: Mitigation[] = []; + hasCinders = false; constructor(options: Options) { - super(TALENTS.RENEWING_BLAZE_TALENT, buff(TALENTS.RENEWING_BLAZE_TALENT), options); + // Custom trigger since we only want to track mitigation during our + // personal buffs, not any external ones + const trigger = buff(TALENTS.RENEWING_BLAZE_TALENT); + trigger.applyTrigger = Events.applybuff + .spell(TALENTS.RENEWING_BLAZE_TALENT) + .by(SELECTED_PLAYER) + .to(SELECTED_PLAYER); + trigger.removeTrigger = Events.removebuff + .spell(TALENTS.RENEWING_BLAZE_TALENT) + .by(SELECTED_PLAYER) + .to(SELECTED_PLAYER); + + super(TALENTS.RENEWING_BLAZE_TALENT, trigger, options); this.active = this.selectedCombatant.hasTalent(TALENTS.RENEWING_BLAZE_TALENT); + this.hasCinders = this.selectedCombatant.hasTalent(TALENTS.LIFECINDERS_TALENT); + this.addEventListener(Events.damage.to(SELECTED_PLAYER), this.recordDamage); + this.addEventListener( - Events.heal.to(SELECTED_PLAYER).spell(SPELLS.RENEWING_BLAZE_HEAL), - this.recordHeal, - ); - this.addEventListener( - Events.applybuff.to(SELECTED_PLAYER).spell(SPELLS.RENEWING_BLAZE_HEAL), - this.applyHealBuff, + Events.applybuff.by(SELECTED_PLAYER).to(SELECTED_PLAYER).spell(TALENTS.RENEWING_BLAZE_TALENT), + this.applyBuff, ); this.addEventListener(Events.fightend, this.onFightEnd); } @@ -78,29 +86,33 @@ class RenewingBlaze extends MajorDefensiveBuff { }); } - private recordHeal(event: HealEvent) { - const heal = this.renewingBlazeHealBuffs.find( - (buff) => GetRelatedEvent(event, RENEWING_BLAZE_HEAL) === buff.start, - ); - if (!heal) { - console.warn('Unable to find parent buff for Major Defensive analyzer', this.spell, event); - return; + private applyBuff(event: ApplyBuffEvent) { + const heal = { + start: event, + amount: 0, + overheal: 0, + partnerAmount: -1, + }; + if (this.hasCinders) { + heal.partnerAmount = 0; } - heal.events.push(event); - heal.amount += event.amount; - heal.overheal += event.overheal ?? 0; - } + const healEvents = GetRelatedEvents(event, RENEWING_BLAZE_HEAL); + for (const healEvent of healEvents) { + if (healEvent.targetID !== this.selectedCombatant.id) { + heal.partnerAmount += healEvent.amount; + continue; + } - private applyHealBuff(event: ApplyBuffEvent) { - this.renewingBlazeHealBuffs.push({ start: event, events: [], amount: 0, overheal: 0 }); + heal.amount += healEvent.amount; + heal.overheal += healEvent.overheal ?? 0; + } + this.renewingBlazeHealBuffs.push(heal); } /** Returns the related Renewing Healing buff, for our Acc buff. */ - private healBuff(mit: Mitigation): RenewingBlazeHealBuff | undefined { - return this.renewingBlazeHealBuffs.find( - (buff) => GetRelatedEvent(buff.start, RENEWING_BLAZE_BUFFS) === mit.start, - ); + private healBuff(mit: Mitigation | undefined): RenewingBlazeHealBuff | undefined { + return this.renewingBlazeHealBuffs.find((buff) => buff.start === mit?.start); } // For some reason healing numbers and damage taken doesn't always @@ -119,9 +131,7 @@ class RenewingBlaze extends MajorDefensiveBuff { } mitigationSegments(mit: Mitigation): MitigationSegment[] { - const heal = this.renewingBlazeHealBuffs.find( - (buff) => GetRelatedEvent(buff.start, RENEWING_BLAZE_BUFFS) === mit.start, - ); + const heal = this.healBuff(mit); return [ { @@ -321,9 +331,7 @@ class RenewingBlaze extends MajorDefensiveBuff { get cooldownDetailsComponent() { return ({ analyzer, mit }: CooldownDetailsProps) => { - const heal = this.renewingBlazeHealBuffs.find( - (buff) => GetRelatedEvent(buff.start, RENEWING_BLAZE_BUFFS) === mit?.start, - ); + const heal = this.healBuff(mit); return ; }; } @@ -402,6 +410,26 @@ const CooldownDetails = ({ /> + + {heal.partnerAmount >= 0 && ( + + Partner healing + {formatNumber(heal.partnerAmount)} + + + + + + + )} ) : ( <> diff --git a/src/analysis/retail/evoker/shared/modules/normalizers/DefensiveCastLinkNormalizer.ts b/src/analysis/retail/evoker/shared/modules/normalizers/DefensiveCastLinkNormalizer.ts index 073d76a6906..d3d3f18ee46 100644 --- a/src/analysis/retail/evoker/shared/modules/normalizers/DefensiveCastLinkNormalizer.ts +++ b/src/analysis/retail/evoker/shared/modules/normalizers/DefensiveCastLinkNormalizer.ts @@ -6,14 +6,10 @@ import { Options } from 'parser/core/Module'; export const OBSIDIAN_SCALES = 'obsidianScales'; // links cast to buff apply export const RENEWING_BLAZE = 'renewingBlaze'; // links cast to buff apply -export const RENEWING_BLAZE_BUFFS = 'renewingBlazeBuffs'; // links acc & heal buffs export const RENEWING_BLAZE_HEAL = 'renewingBlazeHeal'; // links heal buff and healing const TWIN_GUARDIAN_PARTNER = 'twinGuardianPartner'; // links external and personal buffs const CAST_BUFFER = 25; -/** Heal buff gets applied once you first take damage, so there is a non-zero chance it won't apply - * till very late into the acc buff */ -const RENEWING_BLAZE_BUFF_BUFFER = 10_000; /** Heal buff can get applied immediately on use, and keeps getting refreshed on damage until * main acc buff runs out, so we set this high to make sure we catch all. */ const RENEWING_BLAZE_DURATION = 25_000; @@ -37,29 +33,19 @@ const EVENT_LINKS: EventLink[] = [ linkingEventType: EventType.ApplyBuff, referencedEventId: TALENTS.RENEWING_BLAZE_TALENT.id, referencedEventType: EventType.Cast, - anyTarget: true, // TODO: Revisit in TWW - Flame shaper can share RB + anyTarget: true, forwardBufferMs: CAST_BUFFER, backwardBufferMs: CAST_BUFFER, }, - { - linkRelation: RENEWING_BLAZE_BUFFS, - reverseLinkRelation: RENEWING_BLAZE_BUFFS, - linkingEventId: TALENTS.RENEWING_BLAZE_TALENT.id, - linkingEventType: EventType.ApplyBuff, - referencedEventId: SPELLS.RENEWING_BLAZE_HEAL.id, - referencedEventType: EventType.ApplyBuff, - anyTarget: true, // TODO: Revisit in TWW - Flameshaper can share RB - forwardBufferMs: RENEWING_BLAZE_BUFF_BUFFER, - maximumLinks: 1, - }, { linkRelation: RENEWING_BLAZE_HEAL, reverseLinkRelation: RENEWING_BLAZE_HEAL, - linkingEventId: SPELLS.RENEWING_BLAZE_HEAL.id, + linkingEventId: TALENTS.RENEWING_BLAZE_TALENT.id, linkingEventType: EventType.ApplyBuff, referencedEventId: SPELLS.RENEWING_BLAZE_HEAL.id, referencedEventType: EventType.Heal, anyTarget: true, + anySource: false, // We only want to be tracking our own Buffs, not any external ones forwardBufferMs: RENEWING_BLAZE_DURATION, }, { diff --git a/src/analysis/retail/evoker/shared/modules/normalizers/DefensiveNormalizer.ts b/src/analysis/retail/evoker/shared/modules/normalizers/DefensiveNormalizer.ts index c02b822bada..6c1d94c2de2 100644 --- a/src/analysis/retail/evoker/shared/modules/normalizers/DefensiveNormalizer.ts +++ b/src/analysis/retail/evoker/shared/modules/normalizers/DefensiveNormalizer.ts @@ -44,6 +44,7 @@ class DefensiveNormalizer extends EventsNormalizer { continue; } + const targetID = event.targetID ?? 0; // You never know if (event.type === EventType.ApplyBuff) { // fake apply don't push if (!HasRelatedEvent(event, castLink)) { @@ -51,10 +52,10 @@ class DefensiveNormalizer extends EventsNormalizer { } // on new "real" apply we push in our last removeBuffEvent - const hasStoredEnd = latestBuffRemoveEvents.get(spellId); + const hasStoredEnd = latestBuffRemoveEvents.get(spellId + targetID); if (hasStoredEnd) { fixedEvents.push(hasStoredEnd); - latestBuffRemoveEvents.delete(spellId); + latestBuffRemoveEvents.delete(spellId + targetID); } fixedEvents.push(event); continue; @@ -62,7 +63,7 @@ class DefensiveNormalizer extends EventsNormalizer { if (event.type === EventType.RemoveBuff) { // store the latests event so we only push the latests one - latestBuffRemoveEvents.set(spellId, event); + latestBuffRemoveEvents.set(spellId + targetID, event); continue; } }