Skip to content

Commit

Permalink
Holy word cdr rewrite (WoWAnalyzer#7009)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Liavre authored Sep 10, 2024
1 parent f4d4a12 commit 767567c
Show file tree
Hide file tree
Showing 19 changed files with 514 additions and 1,028 deletions.
1 change: 1 addition & 0 deletions src/analysis/retail/priest/holy/CHANGELOG.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
19 changes: 4 additions & 15 deletions src/analysis/retail/priest/holy/CombatLogParser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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';
Expand All @@ -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 = {
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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,

Expand Down
34 changes: 33 additions & 1 deletion src/analysis/retail/priest/holy/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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<number, baseHolyWordCDR>([
[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<number, baseHolyWordCDR>([
[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<number, baseHolyWordCDR>([
[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<number, baseHolyWordCDR>([
[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<number, baseHolyWordCDR>([
[TALENTS.ENERGY_CYCLE_TALENT.id, { baseCDR: 4 }],
]);

export interface baseHolyWordCDR {
baseCDR: number;
vohDependent?: boolean;
apothDisable?: boolean;
}
189 changes: 189 additions & 0 deletions src/analysis/retail/priest/holy/modules/core/HolyWordCDR.tsx
Original file line number Diff line number Diff line change
@@ -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;
Loading

0 comments on commit 767567c

Please sign in to comment.