Skip to main content

wowlab_engine_combat/pipeline/
hooks.rs

1use 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}