Skip to main content

wowlab_engine_combat/pipeline/
hooks.rs

1use super::{
2    CombatCtx, DamageFlags, EffectExecution, FreeActionSource, HookCtx, LocalAuraIdx,
3    LocalSpellIdx, ScratchDisposition, SpellData, apply_aura, apply_aura_with_duration,
4    consume_aura_stack_by_id, drain_deferred_work, execute_effect_range, with_scratch,
5};
6
7pub(super) fn consume_cast_auras(
8    ctx: &mut CombatCtx<'_>,
9    override_aura_id: Option<u32>,
10    cooldown_bypass_aura_id: Option<u32>,
11    consume_aura_on_cast_id: u32,
12) {
13    if let Some(aura_id) = override_aura_id {
14        consume_aura_stack_by_id(&mut ctx.hook_ctx(), aura_id);
15    }
16
17    if let Some(aura_id) = cooldown_bypass_aura_id {
18        consume_aura_stack_by_id(&mut ctx.hook_ctx(), aura_id);
19    }
20
21    if consume_aura_on_cast_id != 0 {
22        consume_aura_stack_by_id(&mut ctx.hook_ctx(), consume_aura_on_cast_id);
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: &SpellData,
50    flags: DamageFlags,
51) {
52    execute_effect_range(
53        ctx,
54        EffectExecution {
55            range: spell.followup_effects,
56            profile_spell_id: spell.spell_id,
57            flags,
58        },
59    );
60}
61
62pub(super) fn fire_expire_hooks(ctx: &mut CombatCtx<'_>) {
63    let CombatCtx {
64        state,
65        buf,
66        sink,
67        now,
68        rng,
69        source,
70        target,
71    } = ctx;
72    let mut aura_ctx = HookCtx::new(
73        crate::context::HookCtxServices {
74            state,
75            buf,
76            sink,
77            rng: *rng,
78        },
79        crate::context::HookCtxRequest::for_target(*now, *target).with_source(*source),
80    );
81
82    drain_deferred_work(&mut aura_ctx);
83}
84
85pub(super) fn fire_cast_hook(
86    ctx: &mut CombatCtx<'_>,
87    spell_local: LocalSpellIdx,
88    empower_rank: u8,
89) {
90    fire_cast_hook_with_source(ctx, spell_local, empower_rank, None);
91}
92
93pub(super) fn fire_cast_hook_with_source(
94    ctx: &mut CombatCtx<'_>,
95    spell_local: LocalSpellIdx,
96    empower_rank: u8,
97    free_action_source: Option<FreeActionSource>,
98) {
99    let hook = ctx
100        .state
101        .defs
102        .cast_hooks
103        .get(spell_local.as_usize())
104        .copied()
105        .flatten();
106    let driver_spell_id = ctx
107        .state
108        .defs
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                state: ctx.state,
117                buf: ctx.buf,
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    let original = std::mem::take(&mut ctx.state.defs.player_cast_hooks);
137    let scratch = std::mem::take(&mut ctx.state.runtime.scratch.hooks);
138    let (original, scratch) = with_scratch(
139        original,
140        scratch,
141        ScratchDisposition::PreserveSource,
142        |entries| {
143            for hook_fn in entries.iter().copied() {
144                let mut hook_ctx = HookCtx::new(
145                    crate::context::HookCtxServices {
146                        state: ctx.state,
147                        buf: ctx.buf,
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
162    ctx.state.defs.player_cast_hooks = original;
163    ctx.state.runtime.scratch.hooks = scratch;
164}