From 767567c0c3b15301bd6078cfb4b5c4f4dca81ce0 Mon Sep 17 00:00:00 2001 From: Liavre Date: Mon, 9 Sep 2024 20:20:14 -0700 Subject: [PATCH] Holy word cdr rewrite (#7009) * moved values to /constants for futur maintainability * updated maintainers * formatting * formatting 2 * deleted dragonflight tiersets * rewrote holy word calc to be easier to maintain * finalized CDR modules * formatting * ripped out the old module * changelog for holy word CDR update * corrected talent active condition in display module * energy cycle moved into the overall CDR module * further clean ups * resolving github comments --- src/analysis/retail/priest/holy/CHANGELOG.tsx | 1 + .../retail/priest/holy/CombatLogParser.tsx | 19 +- src/analysis/retail/priest/holy/constants.ts | 34 ++- .../priest/holy/modules/core/HolyWordCDR.tsx | 189 +++++++++++++ .../holy/modules/core/HolyWordCDRBySpell.tsx | 228 ++++++++++++++++ .../modules/core/HolyWordWastedAmounts.tsx | 49 ---- .../core/HolyWordsReductionBySpell.tsx | 190 ------------- .../modules/spells/holyword/HolyWordBase.tsx | 258 ------------------ .../spells/holyword/HolyWordChastise.tsx | 37 --- .../spells/holyword/HolyWordSalvation.tsx | 35 --- .../spells/holyword/HolyWordSanctify.tsx | 47 ---- .../spells/holyword/HolyWordSerenity.tsx | 51 ---- .../talents/Archon/EnergyCycleHoly.tsx | 133 --------- .../holy/modules/talents/Archon/index.tsx | 2 - .../modules/talents/BottomRow/Apotheosis.tsx | 83 +++--- .../talents/BottomRow/HarmoniousApparatus.tsx | 104 ------- .../talents/BottomRow/LightOfTheNaaru.tsx | 56 ---- .../holy/modules/talents/BottomRow/index.tsx | 4 - .../holy/normalizers/CastLinkNormalizer.ts | 22 ++ 19 files changed, 514 insertions(+), 1028 deletions(-) create mode 100644 src/analysis/retail/priest/holy/modules/core/HolyWordCDR.tsx create mode 100644 src/analysis/retail/priest/holy/modules/core/HolyWordCDRBySpell.tsx delete mode 100644 src/analysis/retail/priest/holy/modules/core/HolyWordWastedAmounts.tsx delete mode 100644 src/analysis/retail/priest/holy/modules/core/HolyWordsReductionBySpell.tsx delete mode 100644 src/analysis/retail/priest/holy/modules/spells/holyword/HolyWordBase.tsx delete mode 100644 src/analysis/retail/priest/holy/modules/spells/holyword/HolyWordChastise.tsx delete mode 100644 src/analysis/retail/priest/holy/modules/spells/holyword/HolyWordSalvation.tsx delete mode 100644 src/analysis/retail/priest/holy/modules/spells/holyword/HolyWordSanctify.tsx delete mode 100644 src/analysis/retail/priest/holy/modules/spells/holyword/HolyWordSerenity.tsx delete mode 100644 src/analysis/retail/priest/holy/modules/talents/Archon/EnergyCycleHoly.tsx delete mode 100644 src/analysis/retail/priest/holy/modules/talents/BottomRow/HarmoniousApparatus.tsx delete mode 100644 src/analysis/retail/priest/holy/modules/talents/BottomRow/LightOfTheNaaru.tsx diff --git a/src/analysis/retail/priest/holy/CHANGELOG.tsx b/src/analysis/retail/priest/holy/CHANGELOG.tsx index f1bc59e5393..d41036f3033 100644 --- a/src/analysis/retail/priest/holy/CHANGELOG.tsx +++ b/src/analysis/retail/priest/holy/CHANGELOG.tsx @@ -5,6 +5,7 @@ import { Arlie, Hana, Litena, Liavre, Squided, ToppleTheNun, Trevor, Saeldur } f import { SpellLink } from 'interface'; export default [ + change(date(2024, 9, 6), <>Implemented 2pc, rewrote back end for Holy Word CDR and remove old modules. , Liavre), change(date(2024, 9, 6), <>Moved all spell constants to constants file for ease of maintenance , Liavre), change(date(2024, 8, 31), <>Implemented an Echo of Light per heal/amp attributor , Liavre), change(date(2024, 8, 20), <>Implemented/pushed Archon , Liavre), diff --git a/src/analysis/retail/priest/holy/CombatLogParser.tsx b/src/analysis/retail/priest/holy/CombatLogParser.tsx index 764f634e8be..f1d3c326656 100644 --- a/src/analysis/retail/priest/holy/CombatLogParser.tsx +++ b/src/analysis/retail/priest/holy/CombatLogParser.tsx @@ -9,8 +9,6 @@ import Abilities from './modules/Abilities'; import Checklist from './modules/checklist/Module'; import EchoOfLightMastery from './modules/core/EchoOfLightMastery'; import FortitudeRaidBuff from './modules/core/FortitudeRaidBuff'; -import HolyWordsReductionBySpell from './modules/core/HolyWordsReductionBySpell'; -import HolyWordWastedAmounts from './modules/core/HolyWordWastedAmounts'; import SpellManaCost from './modules/core/SpellManaCost'; // Spell data import AlwaysBeCasting from './modules/features/AlwaysBeCasting'; @@ -24,10 +22,6 @@ import CircleOfHealing from './modules/spells/CircleOfHealing'; import DivineHymn from './modules/spells/DivineHymn'; import GuardianSpirit from './modules/spells/GuardianSpirit'; import HolyNova from './modules/spells/HolyNova'; -import HolyWordChastise from './modules/spells/holyword/HolyWordChastise'; -import HolyWordSalvationCooldown from './modules/spells/holyword/HolyWordSalvation'; -import HolyWordSanctify from './modules/spells/holyword/HolyWordSanctify'; -import HolyWordSerenity from './modules/spells/holyword/HolyWordSerenity'; import HymnBuffBenefit from './modules/spells/HymnBuffBenefit'; import PrayerOfMending from './modules/spells/PrayerOfMending'; import Renew from './modules/spells/Renew'; @@ -41,6 +35,8 @@ import Benevolence from '../shared/Benevolence'; import RenewTracker from './modules/talents/Oracle/OracleCore/RenewTracker'; import RenewAttributor from './modules/talents/Oracle/OracleCore/RenewAttributor'; import EOLAttrib from './modules/core/EchoOfLightAttributor'; +import HolyWordCDRBySpell from './modules/core/HolyWordCDRBySpell'; +import HolyWordCDR from './modules/core/HolyWordCDR'; class CombatLogParser extends CoreCombatLogParser { static specModules = { @@ -67,21 +63,17 @@ class CombatLogParser extends CoreCombatLogParser { // Core echoOfLightMastery: EchoOfLightMastery, fortitudeRaidBuff: FortitudeRaidBuff, - holyWordsReductionBySpell: HolyWordsReductionBySpell, - holyWordWastedAmounts: HolyWordWastedAmounts, renewTracker: RenewTracker, renewAttributor: RenewAttributor, eolAttrib: EOLAttrib, + holyWordCDRBySpell: HolyWordCDRBySpell, + holyWordCDR: HolyWordCDR, // Spells divineHymn: DivineHymn, guardianSpirit: GuardianSpirit, holyNova: HolyNova, hymnBuffBenefit: HymnBuffBenefit, - holyWordSanctify: HolyWordSanctify, - holyWordSerenity: HolyWordSerenity, - holyWordChastise: HolyWordChastise, - holyWordSalvation: HolyWordSalvationCooldown, circleOfHealing: CircleOfHealing, prayerOfHealing: PrayerOfHealing, @@ -128,8 +120,6 @@ class CombatLogParser extends CoreCombatLogParser { Lightweaver: Talents.BottomRow.Lightweaver, divineImage: Talents.BottomRow.DivineImage, - lightOfTheNaaru: Talents.BottomRow.LightOfTheNaaru, - harmoniousApparatus: Talents.BottomRow.HarmoniousApparatus, resonantWords: Talents.BottomRow.ResonantWords, TranslucentImage: TranslucentImage, miracleWorker: Talents.BottomRow.MiracleWorker, @@ -142,7 +132,6 @@ class CombatLogParser extends CoreCombatLogParser { ResonantEnergyHoly: Talents.Archon.ResonantEnergyHoly, ManifestedPowerHoly: Talents.Archon.ManifestedPowerHoly, EmpoweredSurgesHoly: Talents.Archon.EmpoweredSurgesHoly, - EnergyCycleHoly: Talents.Archon.EnergyCycleHoly, EnergyCompressionHoly: Talents.Archon.EnergyCompressionHoly, PowerSurgeAndDivineHaloHoly: Talents.Archon.PowerSurgeAndDivineHaloHoly, diff --git a/src/analysis/retail/priest/holy/constants.ts b/src/analysis/retail/priest/holy/constants.ts index d359e623e08..807245787c8 100644 --- a/src/analysis/retail/priest/holy/constants.ts +++ b/src/analysis/retail/priest/holy/constants.ts @@ -221,7 +221,6 @@ export const HOLY_ABILITIES_AFFECTED_BY_HEALING_INCREASES = [ SPELLS.ECHO_OF_LIGHT_HEAL, ]; -// legacy list for some modules export const ABILITIES_THAT_TRIGGER_MASTERY = [ SPELLS.DIVINE_HYMN_HEAL.id, SPELLS.GREATER_HEAL.id, @@ -302,3 +301,36 @@ export const EFFECTS_INCREASED_BY_BENEVOLENCE_HOLY = [ //REMOVE WHEN PROPER EOL ATTRIB IS WRITTEN SPELLS.ECHO_OF_LIGHT_HEAL.id, ]; + +export const serenityHWCDR = new Map([ + [SPELLS.FLASH_HEAL.id, { baseCDR: 6 }], + [SPELLS.GREATER_HEAL.id, { baseCDR: 6 }], + [TALENTS.PRAYER_OF_MENDING_TALENT.id, { baseCDR: 4, vohDependent: true }], +]); + +export const sanctifyHWCDR = new Map([ + [TALENTS.PRAYER_OF_HEALING_TALENT.id, { baseCDR: 6 }], + [TALENTS.RENEW_TALENT.id, { baseCDR: 2 }], + [TALENTS.CIRCLE_OF_HEALING_TALENT.id, { baseCDR: 4, vohDependent: true }], +]); + +export const chastiseHWCDR = new Map([ + [SPELLS.SMITE.id, { baseCDR: 6 }], + [SPELLS.HOLY_FIRE.id, { baseCDR: 4, vohDependent: true }], + [TALENTS.HOLY_NOVA_TALENT.id, { baseCDR: 4, vohDependent: true }], +]); + +export const salvationHWCDR = new Map([ + [TALENTS.HOLY_WORD_SERENITY_TALENT.id, { baseCDR: 15, apothDisable: true }], + [TALENTS.HOLY_WORD_SANCTIFY_TALENT.id, { baseCDR: 15, apothDisable: true }], +]); + +export const energyCycleCDR = new Map([ + [TALENTS.ENERGY_CYCLE_TALENT.id, { baseCDR: 4 }], +]); + +export interface baseHolyWordCDR { + baseCDR: number; + vohDependent?: boolean; + apothDisable?: boolean; +} diff --git a/src/analysis/retail/priest/holy/modules/core/HolyWordCDR.tsx b/src/analysis/retail/priest/holy/modules/core/HolyWordCDR.tsx new file mode 100644 index 00000000000..b4f7b6ead36 --- /dev/null +++ b/src/analysis/retail/priest/holy/modules/core/HolyWordCDR.tsx @@ -0,0 +1,189 @@ +import Analyzer from 'parser/core/Analyzer'; +import { Options } from 'parser/core/Module'; +import Combatants from 'parser/shared/modules/Combatants'; + +import { TALENTS_PRIEST } from 'common/TALENTS'; +import SpellUsable from 'parser/shared/modules/SpellUsable'; +import { TIERS } from 'game/TIERS'; +import { CastEvent } from 'parser/core/Events'; +import TALENTS from 'common/TALENTS/priest'; +import { + APOTH_MULTIPIER, + baseHolyWordCDR, + chastiseHWCDR, + energyCycleCDR, + LIGHT_OF_THE_NAARU_REDUCTION_PER_RANK, + salvationHWCDR, + sanctifyHWCDR, + serenityHWCDR, + TWW_TIER1_2PC_CDR, +} from '../../constants'; + +/** + * This module drives all holy word CDR evaluations, use as a dependency and call one of the public handlers + * or make a new one + */ + +class HolyWordCDR extends Analyzer { + static dependencies = { + combatants: Combatants, + spellUsable: SpellUsable, + }; + + protected combatants!: Combatants; + protected spellUsable!: SpellUsable; + + private baseHolyWordCDR = 1; + private baseVohMult = 0; + private lotnMult = 1; + private twwS1TierMult = 1; + + constructor(options: Options) { + super(options); + + // The below 3 if statements scale the CDR base on talents/gear + if (this.selectedCombatant.hasTalent(TALENTS_PRIEST.LIGHT_OF_THE_NAARU_TALENT)) { + this.lotnMult = + this.selectedCombatant.getTalentRank(TALENTS_PRIEST.LIGHT_OF_THE_NAARU_TALENT) * + LIGHT_OF_THE_NAARU_REDUCTION_PER_RANK + + 1; + } + + if (this.selectedCombatant.has2PieceByTier(TIERS.TWW1)) { + this.twwS1TierMult = TWW_TIER1_2PC_CDR + 1; + } + + if (this.selectedCombatant.hasTalent(TALENTS_PRIEST.VOICE_OF_HARMONY_TALENT)) { + this.baseVohMult = + 0.5 * this.selectedCombatant.getTalentRank(TALENTS_PRIEST.VOICE_OF_HARMONY_TALENT); + } + + this.baseHolyWordCDR = this.lotnMult * this.twwS1TierMult; + } + + public handleAny(event: CastEvent, specialEvent?: string): hwCDRBreakdown | undefined { + if (specialEvent === 'ENERGY_CYCLE') { + return this.handleCDR( + event, + energyCycleCDR.get(TALENTS.ENERGY_CYCLE_TALENT.id), + TALENTS.HOLY_WORD_SANCTIFY_TALENT.id, + ); + } + if (chastiseHWCDR.has(event.ability.guid)) { + return this.handleCDR( + event, + chastiseHWCDR.get(event.ability.guid), + TALENTS.HOLY_WORD_CHASTISE_TALENT.id, + ); + } + if (sanctifyHWCDR.has(event.ability.guid)) { + return this.handleCDR( + event, + sanctifyHWCDR.get(event.ability.guid), + TALENTS.HOLY_WORD_SANCTIFY_TALENT.id, + ); + } + if (serenityHWCDR.has(event.ability.guid)) { + return this.handleCDR( + event, + serenityHWCDR.get(event.ability.guid), + TALENTS.HOLY_WORD_SERENITY_TALENT.id, + ); + } + if (salvationHWCDR.has(event.ability.guid)) { + return this.handleCDR( + event, + salvationHWCDR.get(event.ability.guid), + TALENTS.HOLY_WORD_SALVATION_TALENT.id, + ); + } + } + /** + * this function is called by one of the above handlers and returns a breakdown of what effects contributed to + * holy word CDR effects. + * + * if vohAffectsBase is ever true, attribute the entire base to voice of harmony + * + * this takes a baseHolyWordCDR type, you can manually write one to fit in special cases + */ + + private handleCDR( + event: CastEvent, //future proof for tww s1 tier check + hwMap: baseHolyWordCDR | undefined, + hwToReduceId: number, + ): hwCDRBreakdown | undefined { + let baseMult = 1; + let modHolyWordCDR = this.baseHolyWordCDR; + let apothMult = 1; + + if (this.selectedCombatant.hasBuff(TALENTS.APOTHEOSIS_TALENT) && !hwMap?.apothDisable) { + modHolyWordCDR *= 1 + APOTH_MULTIPIER; + apothMult = 1 + APOTH_MULTIPIER; + } + + if (hwMap?.vohDependent) { + baseMult = this.baseVohMult; + } + + // At this point all modifiers should be settled, if blizzard ever adds a new modifier + // add it into the calcs somewhere here + + // TODO: twws1 tier set supposedly scales the CDR down to 35% effectiveness + // if this is true, if(getRelatedEvents(tier proc)) -> multipliers cut down to 0.35 + + const baseCDR = (hwMap?.baseCDR || 0) * baseMult; + const idealCDR = baseCDR * modHolyWordCDR; + const actualCDR = this.spellUsable.reduceCooldown(hwToReduceId, idealCDR); + + // if there is no base CDR, then a VoH spell was passed and VoH was not specced, return 0 + if (baseCDR === 0) { + return; + } + // if base CDR is larger than actual CDR none of the modifiers mattered + else if (baseCDR >= actualCDR) { + return { + idealTotalCDR: idealCDR, + actualTotalCDR: actualCDR, + cdrFromBase: actualCDR, + cdrFromLOTN: 0, + cdrFromApoth: 0, + cdrFromTwwTier: 0, + vohAffectsBase: hwMap?.vohDependent, + affectedSpell: hwToReduceId, + }; + } + + const cdrScaler = (actualCDR - baseCDR) / (idealCDR - baseCDR); + + return { + idealTotalCDR: idealCDR, + actualTotalCDR: actualCDR, + cdrFromBase: baseCDR, + cdrFromLOTN: this.getCDRComponent(idealCDR, cdrScaler, this.lotnMult), + cdrFromApoth: this.getCDRComponent(idealCDR, cdrScaler, apothMult), + cdrFromTwwTier: this.getCDRComponent(idealCDR, cdrScaler, this.twwS1TierMult), + vohAffectsBase: hwMap?.vohDependent, + affectedSpell: hwToReduceId, + }; + } + + // figure out what the other multipliers combine to without and minus from total + private getCDRComponent(idealCDR: number, cdrScaler: number, amp: number): number { + return cdrScaler * (idealCDR - idealCDR / amp); + } +} + +// If vohAffectsBase is true, the attribute base CDR to Voice of Harmony +// can directly add new effects as needed +interface hwCDRBreakdown { + idealTotalCDR: number; + actualTotalCDR: number; + cdrFromBase: number; + cdrFromLOTN: number; + cdrFromApoth: number; + cdrFromTwwTier: number; + vohAffectsBase: boolean | undefined; + affectedSpell: number; +} + +export default HolyWordCDR; diff --git a/src/analysis/retail/priest/holy/modules/core/HolyWordCDRBySpell.tsx b/src/analysis/retail/priest/holy/modules/core/HolyWordCDRBySpell.tsx new file mode 100644 index 00000000000..ff401d941a1 --- /dev/null +++ b/src/analysis/retail/priest/holy/modules/core/HolyWordCDRBySpell.tsx @@ -0,0 +1,228 @@ +import SPELLS from 'common/SPELLS'; +import TALENTS from 'common/TALENTS/priest'; +import { SpellIcon } from 'interface'; +import Analyzer, { Options, SELECTED_PLAYER } from 'parser/core/Analyzer'; +import BoringSpellValueText from 'parser/ui/BoringSpellValueText'; +import Statistic from 'parser/ui/Statistic'; +import { STATISTIC_ORDER } from 'parser/ui/StatisticBox'; +import HolyWordCDR from './HolyWordCDR'; +import Events, { CastEvent, RemoveBuffEvent, RemoveBuffStackEvent } from 'parser/core/Events'; +import { TIERS } from 'game/TIERS'; +import { buffedBySurgeOfLight, getSOLFlashCast } from '../../normalizers/CastLinkNormalizer'; + +/** + * this is just the display function for talents powered by the core of HolyWordCDR + */ + +class HolyWordCDRBySpell extends Analyzer { + static dependencies = { + holyWordCDR: HolyWordCDR, + }; + protected holyWordCDR!: HolyWordCDR; + + private lightOfTheNaaruActive = false; + private apotheosisActive = false; + private voiceOfHarmonyActive = false; + private tierActive = false; + + private hwContainer: hwCDRBreakdown[] = []; + + constructor(options: Options) { + super(options); + + this.lightOfTheNaaruActive = this.selectedCombatant.hasTalent( + TALENTS.LIGHT_OF_THE_NAARU_TALENT, + ); + + this.apotheosisActive = + this.selectedCombatant.hasTalent(TALENTS.APOTHEOSIS_TALENT) || + this.selectedCombatant.hasTalent(TALENTS.ANSWERED_PRAYERS_TALENT); + + this.voiceOfHarmonyActive = this.selectedCombatant.hasTalent(TALENTS.VOICE_OF_HARMONY_TALENT); + + this.tierActive = this.selectedCombatant.has2PieceByTier(TIERS.TWW1); + + this.addEventListener(Events.cast.by(SELECTED_PLAYER), this.handleOnCast); + + if (this.selectedCombatant.hasTalent(TALENTS.ENERGY_CYCLE_TALENT)) { + this.addEventListener(Events.removebuff.by(SELECTED_PLAYER), this.handleEnergyCycle); + this.addEventListener(Events.removebuffstack.by(SELECTED_PLAYER), this.handleEnergyCycle); + } + } + + handleOnCast(event: CastEvent, specialEvent?: string) { + const hwBreakdown = this.holyWordCDR.handleAny(event, specialEvent); + let spellId = event.ability.guid; + + if (specialEvent === 'ENERGY_CYCLE') { + spellId = TALENTS.ENERGY_CYCLE_TALENT.id; + } + + let baseCd = hwBreakdown?.cdrFromBase || 0; + const idealCd = hwBreakdown?.idealTotalCDR || 0; + // filters out non HW reducing abilities + if (idealCd === 0) { + return; + } + let vohCD = 0; + + // if Voice of Harmony enables this ability, switch the base CDR to VoH + if (hwBreakdown?.vohAffectsBase) { + baseCd = 0; + vohCD = hwBreakdown.cdrFromBase; + } + + if (hwBreakdown) { + const wastedCDR = hwBreakdown.idealTotalCDR - hwBreakdown.actualTotalCDR; + const cdrFromLotn = hwBreakdown.cdrFromLOTN; + const cdrFromApoth = hwBreakdown.cdrFromApoth; + const cdrFromTwwTier = hwBreakdown.cdrFromTwwTier; + const totalEffectiveCDR = baseCd + vohCD + cdrFromApoth + cdrFromLotn + cdrFromTwwTier; + const affectedSpell = hwBreakdown.affectedSpell; + + if (this.hwContainer[spellId]) { + this.hwContainer[spellId].wastedCdr += + hwBreakdown.idealTotalCDR - hwBreakdown.actualTotalCDR; + this.hwContainer[spellId].cdrFromBase += baseCd; + this.hwContainer[spellId].cdrFromLOTN += cdrFromLotn; + this.hwContainer[spellId].cdrFromApoth += cdrFromApoth; + this.hwContainer[spellId].cdrFromTwwTier += cdrFromTwwTier; + this.hwContainer[spellId].cdrFromVoh += vohCD; + this.hwContainer[spellId].totalCDR += totalEffectiveCDR; + } + // first time + else { + this.hwContainer[spellId] = { + wastedCdr: wastedCDR, + cdrFromBase: baseCd, + cdrFromLOTN: cdrFromLotn, + cdrFromApoth: cdrFromApoth, + cdrFromTwwTier: cdrFromTwwTier, + cdrFromVoh: vohCD, + spellNum: spellId, + totalCDR: totalEffectiveCDR, + affectedSpell: affectedSpell, + }; + } + } + } + + /** + * + * SPECIAL CASES + * + */ + + handleEnergyCycle(event: RemoveBuffEvent | RemoveBuffStackEvent) { + // this uses heal event because tww S1 tier set is stupid and doesn't "cast" it just procs a second heal + // but still consumes SoL potentially + const castEvent = getSOLFlashCast(event); + + if (castEvent) { + if (buffedBySurgeOfLight(event)) { + this.handleOnCast(castEvent, 'ENERGY_CYCLE'); + } + } + } + + roundVal(num: number) { + return Math.round(num * 10) / 10; + } + + statistic() { + this.hwContainer = this.hwContainer.filter(Boolean); + this.hwContainer.sort((a, b) => { + if (a.affectedSpell < b.affectedSpell) { + return -1; + } + if (a.affectedSpell > b.affectedSpell) { + return 1; + } + return 0; + }); + return ( + + + + + + + + {this.apotheosisActive && ( + + )} + {this.lightOfTheNaaruActive && ( + + )} + {this.voiceOfHarmonyActive && ( + + )} + {this.tierActive && } + + + + + + {Array.from(this.hwContainer.keys()).map((e, i) => ( + + + + + {this.apotheosisActive && ( + + )} + {this.lightOfTheNaaruActive && ( + + )} + {this.voiceOfHarmonyActive && ( + + )} + {this.tierActive && ( + + )} + + + + ))} + +
Cast SpellReduced SpellBase + + + + + + Tier Total Used Wasted
+ + + + {this.roundVal(this.hwContainer[e].cdrFromBase)}s{this.roundVal(this.hwContainer[e].cdrFromApoth)}s{this.roundVal(this.hwContainer[e].cdrFromLOTN)}s{this.roundVal(this.hwContainer[e].cdrFromVoh)}s{this.roundVal(this.hwContainer[e].cdrFromTwwTier)}s{this.roundVal(this.hwContainer[Number(e)].totalCDR)}s{this.roundVal(this.hwContainer[Number(e)].wastedCdr)}s
+ + } + > + + Total holy word CDR from all sources. + +
+ ); + } +} + +// Add more values here as needed +interface hwCDRBreakdown { + wastedCdr: number; + cdrFromBase: number; + cdrFromLOTN: number; + cdrFromApoth: number; + cdrFromTwwTier: number; + cdrFromVoh: number; + spellNum: number; + totalCDR: number; + affectedSpell: number; +} + +export default HolyWordCDRBySpell; diff --git a/src/analysis/retail/priest/holy/modules/core/HolyWordWastedAmounts.tsx b/src/analysis/retail/priest/holy/modules/core/HolyWordWastedAmounts.tsx deleted file mode 100644 index 2aee6d946ac..00000000000 --- a/src/analysis/retail/priest/holy/modules/core/HolyWordWastedAmounts.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import HolyWordChastise from 'analysis/retail/priest/holy/modules/spells/holyword/HolyWordChastise'; -import HolyWordSanctify from 'analysis/retail/priest/holy/modules/spells/holyword/HolyWordSanctify'; -import HolyWordSerenity from 'analysis/retail/priest/holy/modules/spells/holyword/HolyWordSerenity'; -import { formatNumber, formatPercentage } from 'common/format'; -import SPELLS from 'common/SPELLS'; -import { SpellIcon } from 'interface'; -import Analyzer from 'parser/core/Analyzer'; -import StatisticBox, { STATISTIC_ORDER } from 'parser/ui/StatisticBox'; - -// dependencies - -class HolyWordWastedAmounts extends Analyzer { - static dependencies = { - sanctify: HolyWordSanctify, - serenity: HolyWordSerenity, - chastise: HolyWordChastise, - }; - - protected sanctify!: HolyWordSanctify; - protected serenity!: HolyWordSerenity; - protected chastise!: HolyWordChastise; - - statistic() { - const percWastedVersusTotal = - (this.serenity.holyWordWastedCooldown + this.sanctify.holyWordWastedCooldown) / - (this.serenity.totalCooldownReduction + this.sanctify.totalCooldownReduction); - - return ( - } - value={`${formatPercentage(percWastedVersusTotal)}%`} - label="Wasted Holy Words reduction" - tooltip={ - <> - {formatNumber(this.serenity.holyWordWastedCooldown / 1000)}s wasted Serenity reduction - (of {formatNumber(this.serenity.totalCooldownReduction / 1000)}s total) -
- {formatNumber(this.sanctify.holyWordWastedCooldown / 1000)}s wasted Sanctify reduction - (of {formatNumber(this.sanctify.totalCooldownReduction / 1000)}s total) -
- - } - position={STATISTIC_ORDER.CORE(4)} - /> - ); - } -} - -export default HolyWordWastedAmounts; diff --git a/src/analysis/retail/priest/holy/modules/core/HolyWordsReductionBySpell.tsx b/src/analysis/retail/priest/holy/modules/core/HolyWordsReductionBySpell.tsx deleted file mode 100644 index f7280513a28..00000000000 --- a/src/analysis/retail/priest/holy/modules/core/HolyWordsReductionBySpell.tsx +++ /dev/null @@ -1,190 +0,0 @@ -import HolyWordChastise from 'analysis/retail/priest/holy/modules/spells/holyword/HolyWordChastise'; -import HolyWordSalvation from 'analysis/retail/priest/holy/modules/spells/holyword/HolyWordSalvation'; -import HolyWordSanctify from 'analysis/retail/priest/holy/modules/spells/holyword/HolyWordSanctify'; -import HolyWordSerenity from 'analysis/retail/priest/holy/modules/spells/holyword/HolyWordSerenity'; -import { formatPercentage } from 'common/format'; -import SPELLS from 'common/SPELLS'; -import TALENTS from 'common/TALENTS/priest'; -import { SpellIcon } from 'interface'; -import Analyzer, { Options } from 'parser/core/Analyzer'; -import BoringSpellValueText from 'parser/ui/BoringSpellValueText'; -import Statistic from 'parser/ui/Statistic'; -import { STATISTIC_ORDER } from 'parser/ui/StatisticBox'; - -class HolyWordsReductionBySpell extends Analyzer { - static dependencies = { - sanctify: HolyWordSanctify, - serenity: HolyWordSerenity, - chastise: HolyWordChastise, - salvation: HolyWordSalvation, - }; - lightOfTheNaaruActive = false; - apotheosisActive = false; - voiceOfHarmonyActive = false; - - protected sanctify!: HolyWordSanctify; - protected serenity!: HolyWordSerenity; - protected chastise!: HolyWordChastise; - protected salvation!: HolyWordSalvation; - - constructor(options: Options) { - super(options); - this.lightOfTheNaaruActive = this.selectedCombatant.hasTalent( - TALENTS.LIGHT_OF_THE_NAARU_TALENT, - ); - this.apotheosisActive = this.selectedCombatant.hasTalent(TALENTS.APOTHEOSIS_TALENT); - this.voiceOfHarmonyActive = this.selectedCombatant.hasTalent(TALENTS.VOICE_OF_HARMONY_TALENT); - } - - get totalReduction() { - return ( - this.sanctify.totalCooldownReduction + - this.serenity.totalCooldownReduction + - this.chastise.totalCooldownReduction - ); - } - - get reductionBySpellNonApparatus() { - let totalReductionBySpell: { [spellID: string]: { [otherSpellID: string]: number } } = {}; - - totalReductionBySpell = this.sumCooldown( - totalReductionBySpell, - this.sanctify.totalHolyWordReductionPerSpellPerTalent, - ); - totalReductionBySpell = this.sumCooldown( - totalReductionBySpell, - this.serenity.totalHolyWordReductionPerSpellPerTalent, - ); - totalReductionBySpell = this.sumCooldown( - totalReductionBySpell, - this.chastise.totalHolyWordReductionPerSpellPerTalent, - ); - totalReductionBySpell = this.sumCooldown( - totalReductionBySpell, - this.salvation.totalHolyWordReductionPerSpellPerTalent, - ); - - return totalReductionBySpell; - } - - get reductionBySpellApparatus() { - let totalReductionBySpell: { [spellID: string]: { [otherSpellID: string]: number } } = {}; - - totalReductionBySpell = this.sumCooldown( - totalReductionBySpell, - this.sanctify.totalHolyWordReductionPerSpellPerTalent, - ); - totalReductionBySpell = this.sumCooldown( - totalReductionBySpell, - this.serenity.totalHolyWordReductionPerSpellPerTalent, - ); - totalReductionBySpell = this.sumCooldown( - totalReductionBySpell, - this.chastise.totalHolyWordReductionPerSpellPerTalent, - ); - totalReductionBySpell = this.sumCooldown( - totalReductionBySpell, - this.salvation.totalHolyWordReductionPerSpellPerTalent, - ); - - return totalReductionBySpell; - } - - sumCooldown( - currentList: { [spellID: string]: { [otherSpellID: string]: number } }, - newList: { [spellID: string]: { [otherSpellID: string]: number } }, - ) { - for (const [key, value] of Object.entries(newList)) { - if (currentList[key] == null) { - if ( - key === String(TALENTS.CIRCLE_OF_HEALING_TALENT.id) || - key === String(SPELLS.HOLY_FIRE.id) || - key === String(TALENTS.PRAYER_OF_MENDING_TALENT.id) - ) { - newList[key].apparatus = newList[key].base; - newList[key].base = 0; - } else { - newList[key].apparatus = 0; - } - currentList[key] = value; - } else { - for (const [innerKey, innerValue] of Object.entries(value)) { - currentList[key][innerKey] = currentList[key][innerKey] || 0; - currentList[key][innerKey] += innerValue; - } - } - } - return currentList; - } - - statistic() { - const reductionRatio = this.totalReduction / (this.owner.fightDuration + this.totalReduction); - const reductionBySpell = this.reductionBySpellNonApparatus; - - return ( - - This % shows the percentage of total CD reduction for your Holy Words throughout the - fight -
- that your Holy Words passive has contributed (The other contributor being time). -
-
- Talents like Light of the Naaru, Harmonious Apparatus, - and Apotheosis which provide further CD reduction are taken into - account when calculating these numbers. -
-
- If you have talented Holy Word Salvation both{' '} - Holy Word Sanctify - and Serenity will show since they provide CD reduction for{' '} - Holy World Salvation. - - } - dropdown={ - <> - - - - - - {this.apotheosisActive && } - {this.lightOfTheNaaruActive && } - {this.voiceOfHarmonyActive && } - - - - {Object.keys(reductionBySpell).map((e, i) => ( - - - - {this.apotheosisActive && ( - - )} - {this.lightOfTheNaaruActive && ( - - )} - {this.voiceOfHarmonyActive && ( - - )} - - ))} - -
SpellBaseApotheosisLight of the NaaruHarmonious apparatus
- {SPELLS[Number(e)].name} - {Math.ceil(reductionBySpell[e].base / 1000)}s{Math.ceil(reductionBySpell[e].apotheosis / 1000)}s{Math.ceil(reductionBySpell[e].lightOfTheNaaru / 1000)}s{Math.ceil(reductionBySpell[e].apparatus / 1000)}s
- - } - > - - {formatPercentage(reductionRatio)}% Total Holy Word reduction - -
- ); - } -} - -export default HolyWordsReductionBySpell; diff --git a/src/analysis/retail/priest/holy/modules/spells/holyword/HolyWordBase.tsx b/src/analysis/retail/priest/holy/modules/spells/holyword/HolyWordBase.tsx deleted file mode 100644 index a52dc2bf4a7..00000000000 --- a/src/analysis/retail/priest/holy/modules/spells/holyword/HolyWordBase.tsx +++ /dev/null @@ -1,258 +0,0 @@ -import SpellUsable from 'analysis/retail/priest/holy/modules/features/SpellUsable'; -import TALENTS from 'common/TALENTS/priest'; -import Analyzer, { Options, SELECTED_PLAYER } from 'parser/core/Analyzer'; -import Events, { ApplyBuffEvent, CastEvent, HealEvent, RemoveBuffEvent } from 'parser/core/Events'; - -const LIGHT_OF_THE_NAARU_REDUCTION_PER_RANK = 0.1; -const HARMONIOUS_APPARATUS_REDUCTION_PER_RANK = 2000; -class HolyWordBase extends Analyzer { - static dependencies = { - spellUsable: SpellUsable, - }; - spellId = 0; - manaCost = 0; - baseCooldown = 60000; - apparatusReduction = - HARMONIOUS_APPARATUS_REDUCTION_PER_RANK * - this.selectedCombatant.getTalentRank(TALENTS.VOICE_OF_HARMONY_TALENT); - remainingCooldown = 0; - serendipityProccers: { - [spellID: string]: { - baseReduction: () => number; - lightOfTheNaaruReduction: () => number; - apotheosisReduction: () => number; - lightOfTheNaaruAndApotheosisReduction: () => number; - }; - } = {}; - holyWordHealing = 0; - holyWordOverhealing = 0; - holyWordCasts = 0; - holyWordWastedCooldown = 0; - baseHolyWordReductionBySpell: { [spellID: string]: number } = {}; - - lightOfTheNaruActive = false; - lightOfTheNaruMultiplier = 1; - lightOfTheNaruReductionBySpell: { [spellID: string]: number } = {}; - - apotheosisCasts = 0; - apotheosisActive = false; - apotheosisMultiplier = 3; - //Not constant because it is used in other holy word files - apotheosisReductionBySpell: { [spellID: string]: number } = {}; - apotheosisManaReduction = 0; - holyWordApotheosisCasts = 0; - holyWordHealingDuringApotheosis = 0; - holyWordOverhealingDuringApotheosis = 0; - - harmoniousApparatusActive = false; - - protected spellUsable!: SpellUsable; - - constructor(options: Options) { - super(options); - - // Set up proper serendipity reduction values - if (this.selectedCombatant.hasTalent(TALENTS.LIGHT_OF_THE_NAARU_TALENT)) { - this.lightOfTheNaruActive = true; - this.lightOfTheNaruMultiplier = - this.selectedCombatant.getTalentRank(TALENTS.LIGHT_OF_THE_NAARU_TALENT) * - LIGHT_OF_THE_NAARU_REDUCTION_PER_RANK + - 1; - } - - if (this.selectedCombatant.hasTalent(TALENTS.VOICE_OF_HARMONY_TALENT)) { - this.harmoniousApparatusActive = true; - } - - this.addEventListener(Events.cast.by(SELECTED_PLAYER), this.onCast); - this.addEventListener(Events.heal.by(SELECTED_PLAYER), this.onHeal); - this.addEventListener( - Events.applybuff.by(SELECTED_PLAYER).spell(TALENTS.APOTHEOSIS_TALENT), - this.onApplyBuff, - ); - this.addEventListener( - Events.removebuff.by(SELECTED_PLAYER).spell(TALENTS.APOTHEOSIS_TALENT), - this.onRemoveBuff, - ); - } - - get baseCooldownReduction() { - let totalCDR = 0; - - for (const key of Object.entries(this.baseHolyWordReductionBySpell)) { - totalCDR += key[1]; - } - return totalCDR; - } - - get lightOfTheNaaruCooldownReduction() { - let lotnCDR = 0; - - for (const key of Object.entries(this.lightOfTheNaruReductionBySpell)) { - lotnCDR += key[1]; - } - return lotnCDR; - } - - get apotheosisCooldownReduction() { - let apothCDR = 0; - - for (const key of Object.entries(this.apotheosisReductionBySpell)) { - apothCDR += key[1]; - } - return apothCDR; - } - - get totalHolyWordReductionPerSpell() { - const totalReduction: any = {}; - for (const [key, value] of Object.entries(this.baseHolyWordReductionBySpell)) { - totalReduction[key] = totalReduction[key] || 0; - totalReduction[key] += value; - } - - for (const [key, value] of Object.entries(this.apotheosisReductionBySpell)) { - totalReduction[key] = totalReduction[key] || 0; - totalReduction[key] += value; - } - - for (const [key, value] of Object.entries(this.lightOfTheNaruReductionBySpell)) { - totalReduction[key] = totalReduction[key] || 0; - totalReduction[key] += value; - } - - return totalReduction; - } - - get totalHolyWordReductionPerSpellPerTalent() { - const totalReduction: { - [spellID: string]: { - [otherSpellID: string]: number; - }; - } = {}; - for (const [key, value] of Object.entries(this.baseHolyWordReductionBySpell)) { - totalReduction[key] = totalReduction[key] || {}; - totalReduction[key].base = totalReduction[key].base || 0; - totalReduction[key].base += value; - } - - for (const [key, value] of Object.entries(this.apotheosisReductionBySpell)) { - totalReduction[key] = totalReduction[key] || {}; - totalReduction[key].apotheosis = totalReduction[key].apotheosis || 0; - totalReduction[key].apotheosis += value; - } - - for (const [key, value] of Object.entries(this.lightOfTheNaruReductionBySpell)) { - totalReduction[key] = totalReduction[key] || {}; - totalReduction[key].lightOfTheNaaru = totalReduction[key].lightOfTheNaaru || 0; - totalReduction[key].lightOfTheNaaru += value; - } - - return totalReduction; - } - - get totalCooldownReduction() { - return ( - this.baseCooldownReduction + - this.lightOfTheNaaruCooldownReduction + - this.apotheosisCooldownReduction - ); - } - - onCast(event: CastEvent) { - const spellId = event.ability.guid; - if (spellId === this.spellId) { - this.holyWordCasts += 1; - this.remainingCooldown = this.baseCooldown; - - if (this.apotheosisActive) { - this.apotheosisManaReduction += this.manaCost; - this.holyWordApotheosisCasts += 1; - } - } else if (this.serendipityProccers[spellId] != null) { - const reductionAmount = this.parseSerendipityCast(spellId); - this.remainingCooldown -= reductionAmount; - - if (this.spellUsable.isOnCooldown(this.spellId)) { - this.spellUsable.reduceCooldown(this.spellId, reductionAmount, event.timestamp); - } - - if (this.remainingCooldown < 0) { - this.holyWordWastedCooldown += this.remainingCooldown * -1; - this.remainingCooldown = 0; - } - } - } - - parseSerendipityCast(spellId: number) { - // We are casting a spell that will reduce a Holy Word Cooldown. - // Get the base amount of the reduction - const baseReductionAmount = this.serendipityProccers[spellId].baseReduction(); - this.baseHolyWordReductionBySpell[spellId] = this.baseHolyWordReductionBySpell[spellId] || 0; - this.baseHolyWordReductionBySpell[spellId] += baseReductionAmount; - - // Get the modified reduction by spell - if (this.lightOfTheNaruActive && this.apotheosisActive) { - //When both LightOfTheNaaru and Apotheosis is active the total reduction is higher than each reduction individually - //Here each gets reduction equal to their individual contribution and the additional total reduction is split 50% - //Cooldown gained from harmonious apparatus is treated as being base reduction and thus does not gain any from other bonusses - const totalReduction = - this.serendipityProccers[spellId].lightOfTheNaaruAndApotheosisReduction() - - baseReductionAmount; - const lightOfTheNaaruReduction = - this.serendipityProccers[spellId].lightOfTheNaaruReduction() - baseReductionAmount; - const apotheosisReduction = - this.serendipityProccers[spellId].apotheosisReduction() - baseReductionAmount; - const additionalReduction = totalReduction - (lightOfTheNaaruReduction + apotheosisReduction); - - this.lightOfTheNaruReductionBySpell[spellId] = - this.lightOfTheNaruReductionBySpell[spellId] || 0; //Setting the value to zero if its the first time the spell is cast - this.apotheosisReductionBySpell[spellId] = this.apotheosisReductionBySpell[spellId] || 0; //Setting the value to zero if its the first time the spell is cast - - this.lightOfTheNaruReductionBySpell[spellId] += - lightOfTheNaaruReduction + additionalReduction; - this.apotheosisReductionBySpell[spellId] += apotheosisReduction + additionalReduction; - - return totalReduction + baseReductionAmount; - } - - if (this.lightOfTheNaruActive) { - const lightOfTheNaaruReduction = this.serendipityProccers[spellId].lightOfTheNaaruReduction(); - this.lightOfTheNaruReductionBySpell[spellId] = - this.lightOfTheNaruReductionBySpell[spellId] || 0; //Setting the value to zero if its the first time the spell is cast - this.lightOfTheNaruReductionBySpell[spellId] += - lightOfTheNaaruReduction - baseReductionAmount; - return lightOfTheNaaruReduction; - } else if (this.apotheosisActive) { - const apotheosisReduction = this.serendipityProccers[spellId].apotheosisReduction(); - this.apotheosisReductionBySpell[spellId] = this.apotheosisReductionBySpell[spellId] || 0; //Setting the value to zero if its the first time the spell is cast - this.apotheosisReductionBySpell[spellId] += apotheosisReduction - baseReductionAmount; - return apotheosisReduction; - } else { - return baseReductionAmount; - } - } - - onHeal(event: HealEvent) { - const spellId = event.ability.guid; - if (spellId === this.spellId) { - this.holyWordHealing += event.amount || 0; - this.holyWordOverhealing += event.overheal || 0; - - if (this.apotheosisActive) { - this.holyWordHealingDuringApotheosis += event.amount || 0; - this.holyWordOverhealingDuringApotheosis += event.overheal || 0; - } - } - } - - onApplyBuff(event: ApplyBuffEvent) { - this.apotheosisCasts += 1; - this.apotheosisActive = true; - } - - onRemoveBuff(event: RemoveBuffEvent) { - this.apotheosisActive = false; - } -} - -export default HolyWordBase; diff --git a/src/analysis/retail/priest/holy/modules/spells/holyword/HolyWordChastise.tsx b/src/analysis/retail/priest/holy/modules/spells/holyword/HolyWordChastise.tsx deleted file mode 100644 index 49eb43f35b2..00000000000 --- a/src/analysis/retail/priest/holy/modules/spells/holyword/HolyWordChastise.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import SPELLS from 'common/SPELLS'; -import TALENTS from 'common/TALENTS/priest'; -import { Options } from 'parser/core/Analyzer'; - -import HolyWordBase from './HolyWordBase'; - -const SMITE_SERENDIPITY_REDUCTION = 4000; - -class HolyWordSanctify extends HolyWordBase { - constructor(options: Options) { - super(options); - - this.spellId = TALENTS.HOLY_WORD_CHASTISE_TALENT.id; - this.manaCost = 1500; - this.serendipityProccers = { - [SPELLS.SMITE.id]: { - baseReduction: () => SMITE_SERENDIPITY_REDUCTION, - lightOfTheNaaruReduction: () => SMITE_SERENDIPITY_REDUCTION * this.lightOfTheNaruMultiplier, - apotheosisReduction: () => SMITE_SERENDIPITY_REDUCTION * this.apotheosisMultiplier, - lightOfTheNaaruAndApotheosisReduction: () => - SMITE_SERENDIPITY_REDUCTION * this.apotheosisMultiplier * this.lightOfTheNaruMultiplier, - }, - }; - - if (this.harmoniousApparatusActive) { - this.serendipityProccers[SPELLS.HOLY_FIRE.id] = { - baseReduction: () => this.apparatusReduction, - lightOfTheNaaruReduction: () => this.apparatusReduction * this.lightOfTheNaruMultiplier, - apotheosisReduction: () => this.apparatusReduction * this.apotheosisMultiplier, - lightOfTheNaaruAndApotheosisReduction: () => - this.apparatusReduction * this.lightOfTheNaruMultiplier * this.apotheosisMultiplier, - }; - } - } -} - -export default HolyWordSanctify; diff --git a/src/analysis/retail/priest/holy/modules/spells/holyword/HolyWordSalvation.tsx b/src/analysis/retail/priest/holy/modules/spells/holyword/HolyWordSalvation.tsx deleted file mode 100644 index 9831c770b77..00000000000 --- a/src/analysis/retail/priest/holy/modules/spells/holyword/HolyWordSalvation.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import TALENTS from 'common/TALENTS/priest'; -import { Options } from 'parser/core/Analyzer'; - -import HolyWordBase from './HolyWordBase'; - -const HOLY_WORD_SERENDIPITY_REDUCTION = 15000; - -class HolyWordSalvation extends HolyWordBase { - constructor(options: Options) { - super(options); - - this.spellId = TALENTS.HOLY_WORD_SALVATION_TALENT.id; - this.manaCost = 15000; - this.serendipityProccers = { - [TALENTS.HOLY_WORD_SERENITY_TALENT.id]: { - baseReduction: () => HOLY_WORD_SERENDIPITY_REDUCTION, - lightOfTheNaaruReduction: () => - HOLY_WORD_SERENDIPITY_REDUCTION * this.lightOfTheNaruMultiplier, - apotheosisReduction: () => HOLY_WORD_SERENDIPITY_REDUCTION, - lightOfTheNaaruAndApotheosisReduction: () => - HOLY_WORD_SERENDIPITY_REDUCTION * this.lightOfTheNaruMultiplier, - }, - [TALENTS.HOLY_WORD_SANCTIFY_TALENT.id]: { - baseReduction: () => HOLY_WORD_SERENDIPITY_REDUCTION, - lightOfTheNaaruReduction: () => - HOLY_WORD_SERENDIPITY_REDUCTION * this.lightOfTheNaruMultiplier, - apotheosisReduction: () => HOLY_WORD_SERENDIPITY_REDUCTION, - lightOfTheNaaruAndApotheosisReduction: () => - HOLY_WORD_SERENDIPITY_REDUCTION * this.lightOfTheNaruMultiplier, - }, - }; - } -} - -export default HolyWordSalvation; diff --git a/src/analysis/retail/priest/holy/modules/spells/holyword/HolyWordSanctify.tsx b/src/analysis/retail/priest/holy/modules/spells/holyword/HolyWordSanctify.tsx deleted file mode 100644 index 9411b6e6f8e..00000000000 --- a/src/analysis/retail/priest/holy/modules/spells/holyword/HolyWordSanctify.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import TALENTS from 'common/TALENTS/priest'; -import { Options } from 'parser/core/Analyzer'; - -import HolyWordBase from './HolyWordBase'; - -const PRAYER_OF_HEALING_SERENDIPITY_REDUCTION = 6000; -const RENEW_SERENDIPITY_REDUCTION = 2000; - -class HolyWordSanctify extends HolyWordBase { - constructor(options: Options) { - super(options); - - this.spellId = TALENTS.HOLY_WORD_SANCTIFY_TALENT.id; - this.manaCost = 8750; - this.serendipityProccers = { - [TALENTS.PRAYER_OF_HEALING_TALENT.id]: { - baseReduction: () => PRAYER_OF_HEALING_SERENDIPITY_REDUCTION, - lightOfTheNaaruReduction: () => - PRAYER_OF_HEALING_SERENDIPITY_REDUCTION * this.lightOfTheNaruMultiplier, - apotheosisReduction: () => - PRAYER_OF_HEALING_SERENDIPITY_REDUCTION * this.apotheosisMultiplier, - lightOfTheNaaruAndApotheosisReduction: () => - PRAYER_OF_HEALING_SERENDIPITY_REDUCTION * - this.lightOfTheNaruMultiplier * - this.apotheosisMultiplier, - }, - [TALENTS.RENEW_TALENT.id]: { - baseReduction: () => RENEW_SERENDIPITY_REDUCTION, - lightOfTheNaaruReduction: () => RENEW_SERENDIPITY_REDUCTION * this.lightOfTheNaruMultiplier, - apotheosisReduction: () => RENEW_SERENDIPITY_REDUCTION * this.apotheosisMultiplier, - lightOfTheNaaruAndApotheosisReduction: () => - RENEW_SERENDIPITY_REDUCTION * this.lightOfTheNaruMultiplier * this.apotheosisMultiplier, - }, - }; - if (this.harmoniousApparatusActive) { - this.serendipityProccers[TALENTS.CIRCLE_OF_HEALING_TALENT.id] = { - baseReduction: () => this.apparatusReduction, - lightOfTheNaaruReduction: () => this.apparatusReduction * this.lightOfTheNaruMultiplier, - apotheosisReduction: () => this.apparatusReduction * this.apotheosisMultiplier, - lightOfTheNaaruAndApotheosisReduction: () => - this.apparatusReduction * this.lightOfTheNaruMultiplier * this.apotheosisMultiplier, - }; - } - } -} - -export default HolyWordSanctify; diff --git a/src/analysis/retail/priest/holy/modules/spells/holyword/HolyWordSerenity.tsx b/src/analysis/retail/priest/holy/modules/spells/holyword/HolyWordSerenity.tsx deleted file mode 100644 index ad4b87613f0..00000000000 --- a/src/analysis/retail/priest/holy/modules/spells/holyword/HolyWordSerenity.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import TALENTS from 'common/TALENTS/priest'; -import SPELLS from 'common/SPELLS'; -import { Options } from 'parser/core/Analyzer'; - -import HolyWordBase from './HolyWordBase'; - -const GREATER_HEAL_SERENDIPITY_REDUCTION = 6000; -const FLASH_HEAL_SERENDIPITY_REDUCTION = 6000; - -class HolyWordSerenity extends HolyWordBase { - constructor(options: Options) { - super(options); - - this.spellId = TALENTS.HOLY_WORD_SERENITY_TALENT.id; - this.manaCost = 6250; - this.serendipityProccers = { - [SPELLS.GREATER_HEAL.id]: { - baseReduction: () => GREATER_HEAL_SERENDIPITY_REDUCTION, - lightOfTheNaaruReduction: () => - GREATER_HEAL_SERENDIPITY_REDUCTION * this.lightOfTheNaruMultiplier, - apotheosisReduction: () => GREATER_HEAL_SERENDIPITY_REDUCTION * this.apotheosisMultiplier, - lightOfTheNaaruAndApotheosisReduction: () => - GREATER_HEAL_SERENDIPITY_REDUCTION * - this.lightOfTheNaruMultiplier * - this.apotheosisMultiplier, - }, - [SPELLS.FLASH_HEAL.id]: { - baseReduction: () => FLASH_HEAL_SERENDIPITY_REDUCTION, - lightOfTheNaaruReduction: () => - FLASH_HEAL_SERENDIPITY_REDUCTION * this.lightOfTheNaruMultiplier, - apotheosisReduction: () => FLASH_HEAL_SERENDIPITY_REDUCTION * this.apotheosisMultiplier, - lightOfTheNaaruAndApotheosisReduction: () => - GREATER_HEAL_SERENDIPITY_REDUCTION * - this.lightOfTheNaruMultiplier * - this.apotheosisMultiplier, - }, - }; - - if (this.harmoniousApparatusActive) { - this.serendipityProccers[TALENTS.PRAYER_OF_MENDING_TALENT.id] = { - baseReduction: () => this.apparatusReduction, - lightOfTheNaaruReduction: () => this.apparatusReduction * this.lightOfTheNaruMultiplier, - apotheosisReduction: () => this.apparatusReduction * this.apotheosisMultiplier, - lightOfTheNaaruAndApotheosisReduction: () => - this.apparatusReduction * this.lightOfTheNaruMultiplier * this.apotheosisMultiplier, - }; - } - } -} - -export default HolyWordSerenity; diff --git a/src/analysis/retail/priest/holy/modules/talents/Archon/EnergyCycleHoly.tsx b/src/analysis/retail/priest/holy/modules/talents/Archon/EnergyCycleHoly.tsx deleted file mode 100644 index 0617e4f3310..00000000000 --- a/src/analysis/retail/priest/holy/modules/talents/Archon/EnergyCycleHoly.tsx +++ /dev/null @@ -1,133 +0,0 @@ -import Analyzer, { SELECTED_PLAYER } from 'parser/core/Analyzer'; -import { Options } from 'parser/core/Module'; -import Combatants from 'parser/shared/modules/Combatants'; -import Statistic from 'parser/ui/Statistic'; -import STATISTIC_CATEGORY from 'parser/ui/STATISTIC_CATEGORY'; -import STATISTIC_ORDER from 'parser/ui/STATISTIC_ORDER'; -import { formatNumber } from 'common/format'; -import BoringSpellValueText from 'parser/ui/BoringSpellValueText'; - -import { TALENTS_PRIEST } from 'common/TALENTS'; -import SpellUsable from 'parser/shared/modules/SpellUsable'; -import { TIERS } from 'game/TIERS'; -import Events, { RemoveBuffEvent, RemoveBuffStackEvent } from 'parser/core/Events'; -import { buffedBySurgeOfLight, getHealFromSurge } from '../../../normalizers/CastLinkNormalizer'; -import SPELLS from 'common/SPELLS'; -import SpellLink from 'interface/SpellLink'; -import { - APOTH_MULTIPIER, - ENERGY_CYCLE_CDR, - LIGHT_OF_THE_NAARU_REDUCTION_PER_RANK, - TWW_TIER1_2PC_CDR, -} from '../../../constants'; - -class EnergyCycleHoly extends Analyzer { - static dependencies = { - combatants: Combatants, - spellUsable: SpellUsable, - }; - - protected combatants!: Combatants; - protected spellUsable!: SpellUsable; - - //Energy Cycle Ideal is no lost CDR from overcapping CD - energyCycleCDRIdeal = 0; - energyCycleCDRActual = 0; - - private baseHolyWordCDR = 1; - private modHolyWordCDR = 1; - private apothBuffActive = false; - - constructor(options: Options) { - super(options); - - this.active = this.selectedCombatant.hasTalent(TALENTS_PRIEST.ENERGY_CYCLE_TALENT); - - if (this.selectedCombatant.hasTalent(TALENTS_PRIEST.LIGHT_OF_THE_NAARU_TALENT)) { - this.baseHolyWordCDR = - this.selectedCombatant.getTalentRank(TALENTS_PRIEST.LIGHT_OF_THE_NAARU_TALENT) * - LIGHT_OF_THE_NAARU_REDUCTION_PER_RANK + - 1; - } - if (this.selectedCombatant.has2PieceByTier(TIERS.TWW1)) { - this.baseHolyWordCDR *= TWW_TIER1_2PC_CDR + 1; - } - - //these are used to check if CDR needs to be scaled for Energy Cycle - this.addEventListener( - Events.applybuff.by(SELECTED_PLAYER).spell(TALENTS_PRIEST.APOTHEOSIS_TALENT), - this.applyApoth, - ); - - this.addEventListener( - Events.removebuff.by(SELECTED_PLAYER).spell(TALENTS_PRIEST.APOTHEOSIS_TALENT), - this.removeApoth, - ); - - //tracks spending of Surge of Light - this.addEventListener( - Events.removebuff.by(SELECTED_PLAYER).spell(SPELLS.SURGE_OF_LIGHT_BUFF), - this.onSurgeOfLightHeal, - ); - - this.addEventListener( - Events.removebuffstack.by(SELECTED_PLAYER).spell(SPELLS.SURGE_OF_LIGHT_BUFF), - this.onSurgeOfLightHeal, - ); - } - - onSurgeOfLightHeal(event: RemoveBuffEvent | RemoveBuffStackEvent) { - const healEvent = getHealFromSurge(event); - - if (healEvent) { - if (buffedBySurgeOfLight(event)) { - this.modHolyWordCDR = this.baseHolyWordCDR; - if (this.apothBuffActive) { - this.modHolyWordCDR *= APOTH_MULTIPIER; - } - - this.energyCycleCDRIdeal += ENERGY_CYCLE_CDR * this.modHolyWordCDR; - - this.energyCycleCDRActual += this.spellUsable.reduceCooldown( - SPELLS.HOLY_WORD_SANCTIFY.id, - ENERGY_CYCLE_CDR * this.modHolyWordCDR, - ); - } - } - } - - applyApoth() { - this.apothBuffActive = true; - } - removeApoth() { - this.apothBuffActive = false; - } - - get passWastedEnergyCycleCDR(): number { - return this.energyCycleCDRIdeal - this.energyCycleCDRActual; - } - - statistic() { - return ( - - -
- {formatNumber(this.energyCycleCDRActual)}s - - {' '} - reduced from - {' '} -
- {this.passWastedEnergyCycleCDR}s wasted
-
-
-
- ); - } -} - -export default EnergyCycleHoly; diff --git a/src/analysis/retail/priest/holy/modules/talents/Archon/index.tsx b/src/analysis/retail/priest/holy/modules/talents/Archon/index.tsx index 6deec90fbe0..1704efac98c 100644 --- a/src/analysis/retail/priest/holy/modules/talents/Archon/index.tsx +++ b/src/analysis/retail/priest/holy/modules/talents/Archon/index.tsx @@ -2,7 +2,6 @@ import PerfectedFormHoly from './PerfectedFormHoly'; import ResonantEnergyHoly from './ResonantEnergyHoly'; import ManifestedPowerHoly from './ManifestedPowerHoly'; import EmpoweredSurgesHoly from './EmpoweredSurgesHoly'; -import EnergyCycleHoly from './EnergyCycleHoly'; import EnergyCompressionHoly from './EnergyCompressionHoly'; import PowerSurgeAndDivineHaloHoly from './PowerSurgeAndDivineHaloHoly'; @@ -11,7 +10,6 @@ export { ResonantEnergyHoly, ManifestedPowerHoly, EmpoweredSurgesHoly, - EnergyCycleHoly, EnergyCompressionHoly, PowerSurgeAndDivineHaloHoly, }; diff --git a/src/analysis/retail/priest/holy/modules/talents/BottomRow/Apotheosis.tsx b/src/analysis/retail/priest/holy/modules/talents/BottomRow/Apotheosis.tsx index db0b39c58bb..3c7ddf47f19 100644 --- a/src/analysis/retail/priest/holy/modules/talents/BottomRow/Apotheosis.tsx +++ b/src/analysis/retail/priest/holy/modules/talents/BottomRow/Apotheosis.tsx @@ -1,49 +1,50 @@ -import HolyWordChastise from 'analysis/retail/priest/holy/modules/spells/holyword/HolyWordChastise'; -import HolyWordSanctify from 'analysis/retail/priest/holy/modules/spells/holyword/HolyWordSanctify'; -import HolyWordSerenity from 'analysis/retail/priest/holy/modules/spells/holyword/HolyWordSerenity'; -import { formatNumber } from 'common/format'; import TALENTS from 'common/TALENTS/priest'; import Analyzer, { Options, SELECTED_PLAYER } from 'parser/core/Analyzer'; -import Events, { ApplyBuffEvent, RemoveBuffEvent } from 'parser/core/Events'; +import Events, { CastEvent } from 'parser/core/Events'; import BoringSpellValueText from 'parser/ui/BoringSpellValueText'; import ItemManaGained from 'parser/ui/ItemManaGained'; import Statistic from 'parser/ui/Statistic'; import STATISTIC_CATEGORY from 'parser/ui/STATISTIC_CATEGORY'; import STATISTIC_ORDER from 'parser/ui/STATISTIC_ORDER'; +import { HOLY_WORD_LIST } from '../../../constants'; +import SpellManaCost from 'parser/shared/modules/SpellManaCost'; +import PRIEST_TALENTS from 'common/TALENTS/priest'; +import SpellLink from 'interface/SpellLink'; // Example Log: /report/NfFqTvxrQ8GLWDpY/12-Normal+Fetid+Devourer+-+Kill+(1:25)/6-Yrret class Apotheosis extends Analyzer { static dependencies = { - sanctify: HolyWordSanctify, - serenity: HolyWordSerenity, - chastise: HolyWordChastise, + spellManaCost: SpellManaCost, }; + protected spellManaCost!: SpellManaCost; apotheosisCasts = 0; apotheosisActive = false; - protected sanctify!: HolyWordSanctify; - protected serenity!: HolyWordSerenity; - protected chastise!: HolyWordChastise; + manaSavedFromSerenity = 0; + manaSavedFromSanctify = 0; + manaSavedFromChastise = 0; constructor(options: Options) { super(options); - this.active = this.selectedCombatant.hasTalent(TALENTS.APOTHEOSIS_TALENT); - this.addEventListener( - Events.applybuff.by(SELECTED_PLAYER).spell(TALENTS.APOTHEOSIS_TALENT), - this.onApplyBuff, - ); - this.addEventListener( - Events.removebuff.by(SELECTED_PLAYER).spell(TALENTS.APOTHEOSIS_TALENT), - this.onRemoveBuff, - ); + this.active = + this.selectedCombatant.hasTalent(TALENTS.APOTHEOSIS_TALENT) || + this.selectedCombatant.hasTalent(TALENTS.ANSWERED_PRAYERS_TALENT); + this.addEventListener(Events.cast.by(SELECTED_PLAYER).spell(HOLY_WORD_LIST), this.handleCast); } - onApplyBuff(event: ApplyBuffEvent) { - this.apotheosisCasts += 1; - this.apotheosisActive = true; + handleCast(event: CastEvent) { + if (this.selectedCombatant.hasBuff(TALENTS.APOTHEOSIS_TALENT)) { + if (event.ability.guid === TALENTS.HOLY_WORD_SERENITY_TALENT.id) { + this.manaSavedFromSerenity += this.spellManaCost.getRawResourceCost(event); + } else if (event.ability.guid === TALENTS.HOLY_WORD_SANCTIFY_TALENT.id) { + this.manaSavedFromSanctify += this.spellManaCost.getRawResourceCost(event); + } else if (event.ability.guid === TALENTS.HOLY_WORD_CHASTISE_TALENT.id) { + this.manaSavedFromChastise += this.spellManaCost.getRawResourceCost(event); + } + } } - onRemoveBuff(event: RemoveBuffEvent) { - this.apotheosisActive = false; + get totalManaSaved() { + return this.manaSavedFromChastise + this.manaSavedFromSanctify + this.manaSavedFromSerenity; } statistic() { @@ -51,12 +52,16 @@ class Apotheosis extends Analyzer { - Serenity: {this.sanctify.apotheosisCooldownReduction / 1000}s CDR |{' '} - {this.sanctify.apotheosisManaReduction} Mana saved
- Sanctify: {this.serenity.apotheosisCooldownReduction / 1000}s CDR |{' '} - {this.serenity.apotheosisManaReduction} Mana saved
- Chastise: {this.chastise.apotheosisCooldownReduction / 1000}s CDR |{' '} - {this.chastise.apotheosisManaReduction} Mana saved + For Holy Word CDR see the Holy Word module at the top. +
+
+ This includes .
+ :{' '} + {this.manaSavedFromSerenity} Mana saved
+ :{' '} + {this.manaSavedFromSanctify} Mana saved
+ :{' '} + {this.manaSavedFromChastise} Mana saved } size="flexible" @@ -65,21 +70,7 @@ class Apotheosis extends Analyzer { > <> - -
- {formatNumber( - (this.sanctify.apotheosisCooldownReduction + - this.serenity.apotheosisCooldownReduction + - this.chastise.apotheosisCooldownReduction) / - 1000, - )} - s Cooldown Reduction +
diff --git a/src/analysis/retail/priest/holy/modules/talents/BottomRow/HarmoniousApparatus.tsx b/src/analysis/retail/priest/holy/modules/talents/BottomRow/HarmoniousApparatus.tsx deleted file mode 100644 index cf6c18ace4d..00000000000 --- a/src/analysis/retail/priest/holy/modules/talents/BottomRow/HarmoniousApparatus.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import HolyWordChastise from 'analysis/retail/priest/holy/modules/spells/holyword/HolyWordChastise'; -import HolyWordSanctify from 'analysis/retail/priest/holy/modules/spells/holyword/HolyWordSanctify'; -import HolyWordSerenity from 'analysis/retail/priest/holy/modules/spells/holyword/HolyWordSerenity'; -import SPELLS from 'common/SPELLS'; -import Analyzer, { Options } from 'parser/core/Analyzer'; -import BoringSpellValueText from 'parser/ui/BoringSpellValueText'; -import Statistic from 'parser/ui/Statistic'; -import STATISTIC_CATEGORY from 'parser/ui/STATISTIC_CATEGORY'; -import STATISTIC_ORDER from 'parser/ui/STATISTIC_ORDER'; -import TALENTS from 'common/TALENTS/priest'; - -class HarmoniousApparatus extends Analyzer { - static dependencies = { - sanctify: HolyWordSanctify, - serenity: HolyWordSerenity, - chastise: HolyWordChastise, - }; - protected sanctify!: HolyWordSanctify; - protected serenity!: HolyWordSerenity; - protected chastise!: HolyWordChastise; - - constructor(options: Options) { - super(options); - this.active = this.selectedCombatant.hasTalent(TALENTS.VOICE_OF_HARMONY_TALENT); - } - - get reductionForAllSpells() { - let totalReductionBySpell: { [spellID: string]: { [otherSpellID: string]: number } } = {}; - - totalReductionBySpell = this.sumCooldown( - totalReductionBySpell, - this.sanctify.totalHolyWordReductionPerSpellPerTalent, - ); - totalReductionBySpell = this.sumCooldown( - totalReductionBySpell, - this.serenity.totalHolyWordReductionPerSpellPerTalent, - ); - totalReductionBySpell = this.sumCooldown( - totalReductionBySpell, - this.chastise.totalHolyWordReductionPerSpellPerTalent, - ); - - return totalReductionBySpell; - } - - sumCooldown( - currentList: { [spellID: string]: { [otherSpellID: string]: number } }, - newList: { [spellID: string]: { [otherSpellID: string]: number } }, - ) { - for (const [key, value] of Object.entries(newList)) { - if (currentList[key] == null) { - currentList[key] = value; - } else { - for (const [innerKey, innerValue] of Object.entries(value)) { - currentList[key][innerKey] = currentList[key][innerKey] || 0; - currentList[key][innerKey] += innerValue; - } - } - } - return currentList; - } - - reductionForSpell(spellId: number) { - const reduction = this.reductionForAllSpells[spellId]; - if (reduction && reduction.base) { - return reduction.base; - } - return 0; - } - - statistic() { - const totalHealingSpellReduction = - this.reductionForSpell(TALENTS.CIRCLE_OF_HEALING_TALENT.id) + - this.reductionForSpell(TALENTS.PRAYER_OF_MENDING_TALENT.id); - - return ( - - Serenity:{' '} - {Math.ceil(this.reductionForSpell(TALENTS.CIRCLE_OF_HEALING_TALENT.id) / 1000)} - s CDR -
- Sanctify:{' '} - {Math.ceil(this.reductionForSpell(TALENTS.PRAYER_OF_MENDING_TALENT.id) / 1000)}s CDR -
- Chastise : {Math.ceil(this.reductionForSpell(SPELLS.HOLY_FIRE.id) / 1000)}s CDR -
- - } - > - - {Math.ceil(totalHealingSpellReduction / 1000)}s{' '} - Healing Spell Cooldown Reduction - -
- ); - } -} - -export default HarmoniousApparatus; diff --git a/src/analysis/retail/priest/holy/modules/talents/BottomRow/LightOfTheNaaru.tsx b/src/analysis/retail/priest/holy/modules/talents/BottomRow/LightOfTheNaaru.tsx deleted file mode 100644 index d393bd8ed87..00000000000 --- a/src/analysis/retail/priest/holy/modules/talents/BottomRow/LightOfTheNaaru.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import HolyWordChastise from 'analysis/retail/priest/holy/modules/spells/holyword/HolyWordChastise'; -import HolyWordSanctify from 'analysis/retail/priest/holy/modules/spells/holyword/HolyWordSanctify'; -import HolyWordSerenity from 'analysis/retail/priest/holy/modules/spells/holyword/HolyWordSerenity'; -import TALENTS from 'common/TALENTS/priest'; -import Analyzer, { Options } from 'parser/core/Analyzer'; -import BoringSpellValueText from 'parser/ui/BoringSpellValueText'; -import Statistic from 'parser/ui/Statistic'; -import STATISTIC_CATEGORY from 'parser/ui/STATISTIC_CATEGORY'; -import STATISTIC_ORDER from 'parser/ui/STATISTIC_ORDER'; - -// Example Log: /report/Gvxt7CgLya2W1TYj/5-Normal+Zek'voz+-+Kill+(3:57)/13-弥砂丶 -class LightOfTheNaaru extends Analyzer { - static dependencies = { - sanctify: HolyWordSanctify, - serenity: HolyWordSerenity, - chastise: HolyWordChastise, - }; - protected sanctify!: HolyWordSanctify; - protected serenity!: HolyWordSerenity; - protected chastise!: HolyWordChastise; - - constructor(options: Options) { - super(options); - this.active = this.selectedCombatant.hasTalent(TALENTS.LIGHT_OF_THE_NAARU_TALENT); - } - - statistic() { - return ( - - Serenity: {Math.ceil(this.serenity.lightOfTheNaaruCooldownReduction / 1000)}s CDR -
- Sanctify: {Math.ceil(this.sanctify.lightOfTheNaaruCooldownReduction / 1000)}s CDR -
- Chastise: {Math.ceil(this.chastise.lightOfTheNaaruCooldownReduction / 1000)}s CDR - - } - size="flexible" - category={STATISTIC_CATEGORY.TALENTS} - position={STATISTIC_ORDER.OPTIONAL(7)} - > - - {Math.ceil( - (this.sanctify.lightOfTheNaaruCooldownReduction + - this.serenity.lightOfTheNaaruCooldownReduction) / - 1000, - )} - s Healing Spell Cooldown Reduction - -
- ); - } -} - -export default LightOfTheNaaru; diff --git a/src/analysis/retail/priest/holy/modules/talents/BottomRow/index.tsx b/src/analysis/retail/priest/holy/modules/talents/BottomRow/index.tsx index 02913d41ea9..55d7467f8e4 100644 --- a/src/analysis/retail/priest/holy/modules/talents/BottomRow/index.tsx +++ b/src/analysis/retail/priest/holy/modules/talents/BottomRow/index.tsx @@ -3,9 +3,7 @@ import Apotheosis from './Apotheosis'; import DesperateTimes from './DesperateTimes'; import DivineImage from './DivineImage'; import DivineWord from './DivineWord'; -import HarmoniousApparatus from './HarmoniousApparatus'; import HolyWordSalvation from './HolyWordSalvation'; -import LightOfTheNaaru from './LightOfTheNaaru'; import Lightweaver from './Lightweaver'; import ResonantWords from './ResonantWords'; import Pontifex from './Pontifex'; @@ -18,9 +16,7 @@ export { DesperateTimes, DivineImage, DivineWord, - HarmoniousApparatus, HolyWordSalvation, - LightOfTheNaaru, Lightweaver, ResonantWords, Pontifex, diff --git a/src/analysis/retail/priest/holy/normalizers/CastLinkNormalizer.ts b/src/analysis/retail/priest/holy/normalizers/CastLinkNormalizer.ts index 6b2b0e99356..322dc98d263 100644 --- a/src/analysis/retail/priest/holy/normalizers/CastLinkNormalizer.ts +++ b/src/analysis/retail/priest/holy/normalizers/CastLinkNormalizer.ts @@ -30,6 +30,7 @@ const SANCTIFY_CAST = 'HolyWordSanctifyCast'; const SALVATION_CAST = 'HolyWordSalvationCast'; const CHASTISE_CAST = 'HolyWordChastiseCast'; export const BUFFED_BY_SURGE_OF_LIGHT = 'BuffedBySurgeOfLight'; +export const BUFFED_BY_SURGE_OF_LIGHT_CAST = 'BuffedBySurgeOfLightCast'; const SURGE_OF_LIGHT_APPLIED_BY_HALO = 'SurgeOfLightAppliedByHalo'; const HALO_LINKED_TO_SURGE_OF_LIGHT = 'HaloLinkedtoSurgeOfLight'; const SPELL_SPENDS_INSIGHT_CHARGE = 'SpellSpendsInsightCharge'; @@ -279,6 +280,17 @@ const EVENT_LINKS: EventLink[] = [ forwardBufferMs: CAST_BUFFER_MS, backwardBufferMs: CAST_BUFFER_MS, }, + { + linkRelation: BUFFED_BY_SURGE_OF_LIGHT_CAST, + reverseLinkRelation: BUFFED_BY_SURGE_OF_LIGHT_CAST, + linkingEventId: SPELLS.FLASH_HEAL.id, + linkingEventType: EventType.Cast, + referencedEventId: [SPELLS.SURGE_OF_LIGHT_BUFF.id], + referencedEventType: [EventType.RemoveBuff, EventType.RemoveBuffStack, EventType.RefreshBuff], + anyTarget: true, + forwardBufferMs: CAST_BUFFER_MS, + backwardBufferMs: CAST_BUFFER_MS, + }, { linkRelation: SURGE_OF_LIGHT_APPLIED_BY_HALO, reverseLinkRelation: HALO_LINKED_TO_SURGE_OF_LIGHT, @@ -387,4 +399,14 @@ export function isPWSHardCast(event: AbsorbedEvent): boolean { return HasRelatedEvent(event, HARDCAST_POWER_WORD_SHIELD); } +export function getSOLFlashCast( + event: RemoveBuffEvent | RemoveBuffStackEvent, +): CastEvent | undefined { + return GetRelatedEvents( + event, + BUFFED_BY_SURGE_OF_LIGHT_CAST, + (e): e is CastEvent => e.type === EventType.Cast, + ).pop(); +} + export default CastLinkNormalizer;