wowlab_engine_content/hooks/elemental_shaman/
elementals.rs1use wowlab_engine_combat::DamageOps as _;
2use wowlab_engine_combat::GuardianOps as _;
3use wowlab_engine_combat::AuraOps as _;
4use wowlab_engine_combat::{DamageFlags, GuardianAbility, GuardianEvent, GuardianSpec, HookCtx};
5use wowlab_engine_domain::dbc::ResolvedGameDataEffectExt as _;
6use wowlab_types::{constants::HUNDRED};
7
8use crate::generated::specs::elemental_shaman::{
9 AURA_FIRE_ELEMENTAL, AURA_STORM_ELEMENTAL, AURA_WIND_GUST, EFFECT, SPELL,
10};
11
12pub(super) const STORM_ELEMENTAL_GUARDIAN_TAG: u32 = 262_001;
13const FIRE_ELEMENTAL_GUARDIAN_TAG: u32 = 262_002;
14const STORM_ELEMENTAL_DURATION_MS: u32 = 14_400;
15const FIRE_ELEMENTAL_DURATION_MS: u32 = 24_000;
16const PRIMAL_ELEMENTAL_OWNER_SP_MULT: f64 = 1.8;
17const STORM_ELEMENTAL_ACTION_MS: u32 = 2_000;
18const STORM_ELEMENTAL_FIRST_ACTION_MS: u32 = 500;
19const STORM_ELEMENTAL_MAX_ACTIONS: u16 = 7;
20const STORMFURY_TICKS: i32 = 4;
21const FIRE_ELEMENTAL_ACTION_MS: u32 = 2_500;
22const FIRE_ELEMENTAL_FIRST_ACTION_MS: u32 = 100;
23const FIRE_ELEMENTAL_MAX_ACTIONS: u16 = 11;
24
25fn storm_elemental_action(ctx: &mut HookCtx<'_>, event: GuardianEvent) {
26 match event.action_index {
27 0 => {
28 let coef = ctx
29 .game_data()
30 .effect_sp_coefficient(EFFECT::CALL_LIGHTNING_DAMAGE)
31 * PRIMAL_ELEMENTAL_OWNER_SP_MULT;
32
33 ctx.deal_damage_sp(SPELL::CALL_LIGHTNING, coef, DamageFlags::PET);
34 }
35 1 => {
36 let coef = ctx
37 .game_data()
38 .effect_sp_coefficient(EFFECT::STORMFURY_DAMAGE)
39 * PRIMAL_ELEMENTAL_OWNER_SP_MULT;
40 let charged = 1.0
41 + ctx
42 .game_data()
43 .effect_base_points(EFFECT::CALL_LIGHTNING_DAMAGE_BONUS)
44 / HUNDRED;
45 let ramp = 1.0 + ctx.game_data().effect_base_points(EFFECT::STORMFURY_RAMP) / HUNDRED;
46
47 for tick in 1_i32..=STORMFURY_TICKS {
48 ctx.deal_damage_sp(
49 SPELL::STORMFURY,
50 coef * charged * ramp.powi(tick),
51 DamageFlags::PET,
52 );
53 }
54 }
55 _ => {
56 let coef = ctx
57 .game_data()
58 .effect_sp_coefficient(EFFECT::WIND_GUST_DAMAGE)
59 * PRIMAL_ELEMENTAL_OWNER_SP_MULT;
60 let charged = 1.0
61 + ctx
62 .game_data()
63 .effect_base_points(EFFECT::CALL_LIGHTNING_DAMAGE_BONUS)
64 / HUNDRED;
65
66 ctx.deal_damage_sp(SPELL::WIND_GUST, coef * charged, DamageFlags::PET);
67 }
68 }
69}
70
71fn storm_elemental_demise(ctx: &mut HookCtx<'_>, _event: GuardianEvent) {
72 ctx.expire_aura(AURA_WIND_GUST.raw());
73 ctx.expire_aura(AURA_STORM_ELEMENTAL.raw());
74}
75
76fn fire_elemental_demise(ctx: &mut HookCtx<'_>, _event: GuardianEvent) {
77 ctx.expire_aura(AURA_FIRE_ELEMENTAL.raw());
78}
79
80fn fire_elemental_action(ctx: &mut HookCtx<'_>, event: GuardianEvent) {
81 if event.action_index == 0 {
82 let meteor = ctx.game_data().effect_sp_coefficient(EFFECT::METEOR_DAMAGE)
83 * PRIMAL_ELEMENTAL_OWNER_SP_MULT;
84 let immolate = ctx
85 .game_data()
86 .effect_sp_coefficient(EFFECT::IMMOLATE_DAMAGE)
87 * PRIMAL_ELEMENTAL_OWNER_SP_MULT;
88
89 ctx.deal_damage_sp(SPELL::METEOR, meteor, DamageFlags::PET);
90 ctx.deal_damage_sp(SPELL::IMMOLATE, immolate, DamageFlags::PET);
91 } else {
92 let blast = ctx
93 .game_data()
94 .effect_sp_coefficient(EFFECT::FIRE_BLAST_DAMAGE)
95 * PRIMAL_ELEMENTAL_OWNER_SP_MULT;
96
97 ctx.deal_damage_sp(SPELL::FIRE_BLAST, blast, DamageFlags::PET);
98 }
99
100 let tick = ctx
101 .game_data()
102 .effect_sp_coefficient(EFFECT::IMMOLATE_PERIODIC_DAMAGE)
103 * PRIMAL_ELEMENTAL_OWNER_SP_MULT;
104
105 ctx.deal_damage_sp(SPELL::IMMOLATE, tick, DamageFlags::PET);
106}
107
108pub(super) fn summon_storm_elemental(ctx: &mut HookCtx<'_>) {
109 ctx.apply_aura(AURA_STORM_ELEMENTAL.raw());
110 let _ = ctx.summon_guardian(
111 GuardianSpec::new_inheriting_owner(
112 STORM_ELEMENTAL_GUARDIAN_TAG,
113 STORM_ELEMENTAL_DURATION_MS,
114 )
115 .ability(
116 GuardianAbility::new(STORM_ELEMENTAL_ACTION_MS, storm_elemental_action)
117 .first_action_delay(STORM_ELEMENTAL_FIRST_ACTION_MS)
118 .hasted_actions()
119 .max_actions(STORM_ELEMENTAL_MAX_ACTIONS)
120 .ends_guardian(),
121 )
122 .on_demise(storm_elemental_demise),
123 );
124}
125
126pub(super) fn summon_fire_elemental(ctx: &mut HookCtx<'_>) {
127 ctx.apply_aura(AURA_FIRE_ELEMENTAL.raw());
128 let _ = ctx.summon_guardian(
129 GuardianSpec::new_inheriting_owner(FIRE_ELEMENTAL_GUARDIAN_TAG, FIRE_ELEMENTAL_DURATION_MS)
130 .ability(
131 GuardianAbility::new(FIRE_ELEMENTAL_ACTION_MS, fire_elemental_action)
132 .first_action_delay(FIRE_ELEMENTAL_FIRST_ACTION_MS)
133 .hasted_actions()
134 .max_actions(FIRE_ELEMENTAL_MAX_ACTIONS)
135 .ends_guardian(),
136 )
137 .on_demise(fire_elemental_demise),
138 );
139}