wowlab_engine_content/hooks/destruction_warlock/
handler.rs1use wowlab_engine_combat::GuardianOps as _;
2use wowlab_engine_combat::{
3 CombatHandler, DamageFlags, DriverSpellModifier, DriverSpellModifierResult,
4 GroundEffectDefinition, GroundEffectId, GuardianAbility, GuardianSpec,
5};
6use wowlab_engine_ports::HandlerParams;
7use wowlab_types::constants::MS_PER_SECOND;
8
9use crate::generated::specs::destruction_warlock::{
10 DAMAGE_EFFECT, EFFECT, REPORTED_SPELL, SET_BONUS, SPELL,
11};
12use wowlab_engine_domain::dbc::ResolvedGameDataEffectExt as _;
13use wowlab_types::constants::HUNDRED;
14
15static MID2_INCINERATE_SPELLS: [u32; 1] = [SPELL::INCINERATE];
16pub(super) const BASE_ECHO_CHANCE: f64 = 0.10;
17
18use super::hooks::{SAYAAD_ACTION_MS, sayaad_action};
19
20#[derive(Clone, Copy, Debug)]
21pub(super) struct DestructionConfig {
22 pub(super) rain_of_fire_ground: Option<GroundEffectId>,
23 pub(super) echo_chance: f64,
24 pub(super) mid2_4pc: bool,
25 pub(super) incinerate_mult: f64,
26}
27
28#[derive(Debug)]
29pub(crate) struct DestructionHandler {
31 inner: CombatHandler,
32 fight_duration_ms: u32,
33}
34
35impl DestructionHandler {
36 pub(crate) fn new(
38 parts: crate::composition::ContentHandlerParts,
39 params: &HandlerParams<'_>,
40 ) -> Result<Self, wowlab_engine_ports::EngineError> {
41 let mut parts = parts;
42 let built = parts.draft_mut();
43
44 let has_mid2_2pc = params
45 .set_bonus_auras
46 .contains(&SET_BONUS::MID2_DESTRUCTION_2PC);
47 let mid2_4pc = params
48 .set_bonus_auras
49 .contains(&SET_BONUS::MID2_DESTRUCTION_4PC);
50
51 let incinerate_mult = if has_mid2_2pc {
52 1.0 + params
53 .game_data
54 .effect_base_points(EFFECT::MID2_DESTRUCTION_2PC_INCINERATE)
55 / HUNDRED
56 } else {
57 1.0
58 };
59 let echo_chance = BASE_ECHO_CHANCE
60 + if has_mid2_2pc {
61 params
62 .game_data
63 .effect_base_points(EFFECT::MID2_DESTRUCTION_2PC_ECHO_CHANCE)
64 / HUNDRED
65 } else {
66 0.0
67 };
68
69 if has_mid2_2pc {
70 built.register_driver_spell_modifier(DriverSpellModifier::new(
71 SET_BONUS::MID2_DESTRUCTION_2PC,
72 &MID2_INCINERATE_SPELLS,
73 mid2_incinerate_modifier,
74 ));
75 }
76
77 if let Some(local) = built.spell_local(SPELL::CHANNEL_DEMONFIRE) {
78 built.patch_spell(local, |spell| {
79 spell.requirements.caster_aura_spell = 0;
81 });
82 }
83
84 if let Some(local) = built.spell_local(SPELL::CORRUPTION) {
85 built.patch_spell(local, |spell| {
86 spell.requirements.exclude_caster_aura_spell = 0;
88 });
89 }
90
91 let rain_of_fire_ground = (!params.game_data.is_empty())
92 .then(|| {
93 built.register_ground_effect(
94 GroundEffectDefinition::from_driver(
95 DAMAGE_EFFECT::RAIN_OF_FIRE_TIMING.action(),
96 DAMAGE_EFFECT::RAIN_OF_FIRE_TIMING,
97 DAMAGE_EFFECT::RAIN_OF_FIRE_DAMAGE,
98 DAMAGE_EFFECT::RAIN_OF_FIRE_DAMAGE,
99 )
100 .flags(DamageFlags::PERIODIC),
101 )
102 })
103 .transpose()?;
104
105 built.set_spec_config(DestructionConfig {
106 rain_of_fire_ground,
107 echo_chance,
108 mid2_4pc,
109 incinerate_mult,
110 });
111
112 let built = parts.finalize()?;
113
114 Ok(Self {
115 inner: CombatHandler::from_built(built),
116 fight_duration_ms: wowlab_types::numeric::f64_to_u32_saturating_trunc(
117 params.fight_duration_secs * MS_PER_SECOND,
118 ),
119 })
120 }
121}
122
123fn mid2_incinerate_modifier(view: wowlab_engine_combat::HookView<'_>) -> DriverSpellModifierResult {
124 DriverSpellModifierResult::damage_mult(
125 view.program()
126 .spec_config::<DestructionConfig>()
127 .map_or(1.0, |cfg| cfg.incinerate_mult),
128 )
129}
130
131crate::hooks::define_spec_handler! {
132impl DestructionHandler(self, inner) {
133 fn on_sim_start(&mut self, ctx: &mut wowlab_engine_ports::SimContext) {
134 self.inner.on_sim_start(ctx);
135 self.inner
136 .with_hook_ctx(&mut *ctx.telemetry, wowlab_engine_combat::HookCtxRequest::targetless(wowlab_types::sim::SimTime::ZERO), |precombat| {
137 let _ = precombat.summon_guardian(
138 GuardianSpec::new_inheriting_owner(
139 REPORTED_SPELL::LASH_OF_PAIN,
140 self.fight_duration_ms.saturating_add(1),
141 )
142 .ability(
143 GuardianAbility::new(SAYAAD_ACTION_MS, sayaad_action)
144 .first_action_delay(SAYAAD_ACTION_MS),
145 ),
146 );
147 });
148 }
149
150}
151}