wowlab_engine_combat/pipeline/
hooks.rs1use super::{
2 CombatCtx, DamageFlags, EffectExecution, FreeActionSource, HookCtx, LocalAuraIdx,
3 LocalSpellIdx, SpellData, apply_aura, apply_aura_with_duration, consume_aura_stack,
4 drain_deferred_work, execute_effect_range, with_scratch,
5};
6
7pub(super) fn consume_cast_auras(
8 ctx: &mut CombatCtx<'_>,
9 override_aura: Option<LocalAuraIdx>,
10 cooldown_bypass_aura: Option<LocalAuraIdx>,
11 consume_aura_on_cast: Option<LocalAuraIdx>,
12) {
13 if let Some(aura) = override_aura {
14 consume_aura_stack(&mut ctx.hook_ctx(), aura);
15 }
16
17 if let Some(aura) = cooldown_bypass_aura {
18 consume_aura_stack(&mut ctx.hook_ctx(), aura);
19 }
20
21 if let Some(aura) = consume_aura_on_cast {
22 consume_aura_stack(&mut ctx.hook_ctx(), aura);
23 }
24}
25
26pub(super) fn apply_cast_aura(
27 ctx: &mut CombatCtx<'_>,
28 aura_local: LocalAuraIdx,
29 duration_ms: Option<u32>,
30 flags: DamageFlags,
31) {
32 let source = ctx.source;
33 let hook = ctx.hook_ctx();
34 let mut hook = if flags.contains(DamageFlags::REFLECTED) {
35 hook.with_effect_target(source)
36 } else {
37 hook
38 };
39
40 if let Some(duration_ms) = duration_ms {
41 apply_aura_with_duration(&mut hook, aura_local, Some(duration_ms));
42 } else {
43 apply_aura(&mut hook, aura_local);
44 }
45}
46
47pub(super) fn process_followup_effects(
48 ctx: &mut CombatCtx<'_>,
49 spell_local: LocalSpellIdx,
50 spell: &SpellData,
51 flags: DamageFlags,
52) {
53 execute_effect_range(
54 ctx,
55 EffectExecution {
56 range: spell.followup_effects,
57 profile_spell_id: spell.spell_id,
58 profile_action: ctx.program.damage.spell_action(spell_local),
59 flags,
60 },
61 );
62}
63
64pub(super) fn fire_expire_hooks(ctx: &mut CombatCtx<'_>) {
65 let CombatCtx {
66 program,
67 instance: state,
68 sink,
69 now,
70 rng,
71 source,
72 target,
73 } = ctx;
74 let mut aura_ctx = HookCtx::new(
75 crate::context::HookCtxServices {
76 program,
77 instance: state,
78 sink,
79 rng: *rng,
80 },
81 crate::context::HookCtxRequest::for_target(*now, *target).with_source(*source),
82 );
83
84 drain_deferred_work(&mut aura_ctx);
85}
86
87pub(super) fn fire_cast_hook(
88 ctx: &mut CombatCtx<'_>,
89 spell_local: LocalSpellIdx,
90 empower_rank: u8,
91) {
92 fire_cast_hook_with_source(ctx, spell_local, empower_rank, None);
93}
94
95pub(super) fn fire_cast_hook_with_source(
96 ctx: &mut CombatCtx<'_>,
97 spell_local: LocalSpellIdx,
98 empower_rank: u8,
99 free_action_source: Option<FreeActionSource>,
100) {
101 let hook = ctx
102 .program
103 .cast_hooks
104 .get(spell_local.as_usize())
105 .copied()
106 .flatten();
107 let driver_spell_id = ctx
108 .program
109 .spells
110 .get(spell_local.as_usize())
111 .map_or(0, |spell| spell.spell_id);
112
113 if let Some(hook_fn) = hook {
114 let hook_ctx = HookCtx::new(
115 crate::context::HookCtxServices {
116 program: ctx.program,
117 instance: ctx.instance,
118 sink: ctx.sink,
119 rng: ctx.rng,
120 },
121 crate::context::HookCtxRequest::for_target(ctx.now, ctx.target).with_source(ctx.source),
122 )
123 .with_driver_spell(driver_spell_id)
124 .with_empower_rank(empower_rank);
125 let mut hook_ctx = if let Some(source) = free_action_source {
126 hook_ctx.with_free_action_source(source)
127 } else {
128 hook_ctx
129 };
130
131 hook_fn(&mut hook_ctx);
132 }
133}
134
135pub(super) fn fire_player_cast_hooks(ctx: &mut CombatCtx<'_>, spell_id: u32, empower_rank: u8) {
136 if ctx.instance.runtime.procs.player_cast_hooks_dispatching {
137 return;
138 }
139
140 ctx.instance.runtime.procs.player_cast_hooks_dispatching = true;
141 let scratch = std::mem::take(&mut ctx.instance.runtime.scratch.hooks);
142 let scratch = with_scratch(&ctx.program.player_cast_hooks, scratch, |entries| {
143 for hook_fn in entries.iter().copied() {
144 let mut hook_ctx = HookCtx::new(
145 crate::context::HookCtxServices {
146 program: ctx.program,
147 instance: ctx.instance,
148 sink: ctx.sink,
149 rng: ctx.rng,
150 },
151 crate::context::HookCtxRequest::for_target(ctx.now, ctx.target)
152 .with_source(ctx.source),
153 )
154 .with_driver_spell(spell_id)
155 .with_empower_rank(empower_rank);
156
157 hook_fn(&mut hook_ctx);
158 }
159 });
160
161 ctx.instance.runtime.scratch.hooks = scratch;
162 ctx.instance.runtime.procs.player_cast_hooks_dispatching = false;
163}