wowlab_engine_content/hooks/retribution_paladin/
templar.rs1use wowlab_engine_combat::DamageOps as _;
2use wowlab_engine_combat::SchedulingOps as _;
3use wowlab_engine_combat::AuraOps as _;
4use wowlab_engine_combat::CooldownOps as _;
5use wowlab_engine_combat::{DamageFlags, HookCtx};
6
7use super::{
8 config::retribution_config,
9 hooks::{
10 apply_judgment_debuff, crusade_hammer_of_light_stacks, crusade_spender_stacks,
11 divine_purpose_cycle, mid2_spender, righteous_cause_roll, SpenderKind,
12 },
13};
14use crate::generated::specs::retribution_paladin::{
15 AURA_DIVINE_HAMMER, AURA_HAMMER_OF_LIGHT, AURA_HAMMER_OF_LIGHT_FREE, AURA_JUDGMENT_DEBUFF,
16 AURA_LIGHTS_DELIVERANCE, AURA_SHAKE_THE_HEAVENS, AURA_UNDISPUTED_RULING,
17 SPELL_WAKE_OF_ASHES,
18};
19
20const HAMMERFALL_STH_COUNT: u32 = 2;
21
22pub(super) fn empyrean_hammer_fire_hook(ctx: &mut HookCtx<'_>) {
24 let cfg = retribution_config(ctx);
25 let coef = ctx.effect_ap_coefficient(crate::generated::specs::retribution_paladin::DAMAGE_EFFECT::EMPYREAN_HAMMER_DAMAGE);
26
27 ctx.deal_damage_ap(crate::generated::specs::retribution_paladin::DAMAGE_EFFECT::EMPYREAN_HAMMER_DAMAGE.action(), coef, DamageFlags::empty());
28
29 if cfg.lights_deliverance {
30 gain_lights_deliverance(ctx);
31 }
32}
33
34pub(crate) fn queue_empyrean_hammers(ctx: &mut HookCtx<'_>, count: u32, initial_delay_ms: u32) {
35 let stagger_ms = retribution_config(ctx).empyrean_hammer_stagger_ms;
36
37 for i in 0..count {
38 ctx.schedule_hook(initial_delay_ms + i * stagger_ms, empyrean_hammer_fire_hook);
39 }
40}
41
42pub(crate) fn gain_lights_deliverance(ctx: &mut HookCtx<'_>) {
43 ctx.apply_aura(AURA_LIGHTS_DELIVERANCE);
44 lights_deliverance_try_convert(ctx);
45}
46
47pub(crate) fn lights_deliverance_try_convert(ctx: &mut HookCtx<'_>) {
48 if !retribution_config(ctx).lights_deliverance {
49 return;
50 }
51
52 let cfg = retribution_config(ctx);
53
54 if !lights_deliverance_threshold_reached(
55 ctx.aura_stacks(AURA_LIGHTS_DELIVERANCE),
56 cfg.lights_deliverance_max_stacks,
57 ) {
58 return;
59 }
60
61 let wake_ready = ctx.spell_ready(SPELL_WAKE_OF_ASHES);
62
63 if wake_ready {
64 return;
65 }
66
67 if ctx.is_aura_active(AURA_HAMMER_OF_LIGHT) {
68 return;
69 }
70
71 ctx.apply_aura(AURA_HAMMER_OF_LIGHT_FREE);
72 ctx.apply_aura(AURA_HAMMER_OF_LIGHT);
73 ctx.expire_aura(AURA_LIGHTS_DELIVERANCE);
74}
75
76const fn lights_deliverance_threshold_reached(stacks: i32, max_stacks: i32) -> bool {
77 max_stacks > 0 && stacks >= max_stacks
78}
79
80pub(crate) fn hammerfall(ctx: &mut HookCtx<'_>) {
81 let cfg = retribution_config(ctx);
82
83 if !cfg.hammerfall {
84 return;
85 }
86
87 let count = if ctx.is_aura_active(AURA_SHAKE_THE_HEAVENS) {
88 HAMMERFALL_STH_COUNT
89 } else {
90 1
91 };
92
93 queue_empyrean_hammers(ctx, count, 0);
94}
95
96pub(crate) fn higher_calling_extend(ctx: &mut HookCtx<'_>, ms: u32) {
97 if ms > 0 && ctx.is_aura_active(AURA_SHAKE_THE_HEAVENS) {
98 ctx.extend_aura(AURA_SHAKE_THE_HEAVENS, ms);
99 }
100}
101
102pub(crate) fn hammer_of_light_hook(ctx: &mut HookCtx<'_>) {
104 let cfg = retribution_config(ctx);
105
106 crusade_hammer_of_light_stacks(ctx);
107
108 if cfg.undisputed_ruling && cfg.greater_judgment {
109 apply_judgment_debuff(ctx);
110 }
111
112 let coef = ctx.effect_ap_coefficient(crate::generated::specs::retribution_paladin::DAMAGE_EFFECT::HAMMER_OF_LIGHT_DAMAGE);
113
114 ctx.deal_damage_ap(crate::generated::specs::retribution_paladin::DAMAGE_SPELL::HAMMER_OF_LIGHT, coef, DamageFlags::empty());
115 ctx.consume_aura_stack(AURA_JUDGMENT_DEBUFF);
116 righteous_cause_roll(ctx);
117 ctx.expire_aura(AURA_HAMMER_OF_LIGHT_FREE);
118 ctx.expire_aura(AURA_HAMMER_OF_LIGHT);
119 queue_empyrean_hammers(ctx, cfg.zealous_vindication_count, 0);
120 queue_empyrean_hammers(ctx, cfg.lights_guidance_count, cfg.lights_guidance_delay_ms);
121
122 if cfg.shake_the_heavens {
123 ctx.apply_aura(AURA_SHAKE_THE_HEAVENS);
125 }
126
127 if cfg.undisputed_ruling {
128 ctx.apply_aura(AURA_UNDISPUTED_RULING);
129 }
130
131 let consumed_divine_purpose = divine_purpose_cycle(ctx);
132
133 mid2_spender(ctx, SpenderKind::HammerOfLight, consumed_divine_purpose);
134}
135
136pub(crate) fn sth_pulse_hook(ctx: &mut HookCtx<'_>) {
137 queue_empyrean_hammers(ctx, 1, 0);
138}
139
140pub(crate) fn lights_deliverance_check_hook(ctx: &mut HookCtx<'_>) {
142 lights_deliverance_try_convert(ctx);
143}
144
145pub(crate) fn holy_power_spender(ctx: &mut HookCtx<'_>, kind: SpenderKind) {
147 crusade_spender_stacks(ctx);
148 righteous_cause_roll(ctx);
149 let consumed_divine_purpose = divine_purpose_cycle(ctx);
150
151 mid2_spender(ctx, kind, consumed_divine_purpose);
152 hammerfall(ctx);
153}
154
155pub(crate) fn wake_of_ashes_hook(ctx: &mut HookCtx<'_>) {
157 let cfg = retribution_config(ctx);
158
159 if cfg.templar {
160 ctx.apply_aura(AURA_HAMMER_OF_LIGHT);
161 }
162}
163
164pub(crate) fn start_divine_hammer(ctx: &mut HookCtx<'_>) {
165 if !retribution_config(ctx).divine_hammer {
166 return;
167 }
168
169 ctx.apply_aura(AURA_DIVINE_HAMMER);
170}
171
172pub(crate) fn divine_hammer_pulse_hook(_ctx: &mut HookCtx<'_>) {}
174
175#[cfg(test)]
176mod tests {
177 use googletest::prelude::*;
178
179 use super::lights_deliverance_threshold_reached;
180
181 #[gtest]
182 fn lights_deliverance_converts_at_passively_modified_cap() -> Result<()> {
183 verify_false!(lights_deliverance_threshold_reached(49, 50))?;
184 verify_true!(lights_deliverance_threshold_reached(50, 50))?;
185 verify_true!(lights_deliverance_threshold_reached(51, 50))?;
186
187 verify_false!(lights_deliverance_threshold_reached(60, 0))
188 }
189}