Skip to main content

wowlab_engine_combat/systems/auras/
deferred.rs

1use super::{
2    ActorId, EffectExecution, EnemyIdx, HookCtx, MAX_DEFERRED_WORK_ITEMS,
3    execute_effect_range_for_actor, execute_proc_effect_range_for_actor,
4    fire_aura_application_procs,
5};
6
7/// Drains deferred effect programs and expiry hooks, including work queued while draining.
8pub(crate) fn drain_deferred_work(ctx: &mut HookCtx<'_>) {
9    let mut processed = 0;
10
11    while let Some(work) = ctx.state.runtime.deferred_work.pop_next() {
12        processed += 1;
13
14        if processed > MAX_DEFERRED_WORK_ITEMS {
15            ctx.state.runtime.deferred_work.clear();
16            break;
17        }
18
19        match work {
20            crate::state::DeferredWork::Effect(program) => {
21                for _ in 0..program.repeat {
22                    let target = program
23                        .target
24                        .or_else(|| ctx.state.current_target())
25                        .unwrap_or(EnemyIdx::PRIMARY);
26                    let mut combat_ctx = ctx.combat_ctx_with_source(program.source, target);
27
28                    let execution = EffectExecution {
29                        range: program.effects,
30                        profile_spell_id: program.profile_spell_id,
31                        flags: program.flags,
32                    };
33
34                    if program.originating_proc_driver_spell_id == 0 {
35                        execute_effect_range_for_actor(
36                            &mut combat_ctx,
37                            execution,
38                            program.effect_target,
39                        );
40                    } else {
41                        execute_proc_effect_range_for_actor(
42                            &mut combat_ctx,
43                            execution,
44                            program.effect_target,
45                            program.originating_proc_driver_spell_id,
46                        );
47                    }
48                }
49            }
50            crate::state::DeferredWork::AuraApplication(event) => {
51                let target = match event.affected {
52                    ActorId::Enemy(target) => target,
53                    _ => event
54                        .target
55                        .or_else(|| ctx.state.current_target())
56                        .unwrap_or(EnemyIdx::PRIMARY),
57                };
58                let mut combat_ctx = ctx.combat_ctx_with_source(event.source, target);
59
60                fire_aura_application_procs(&mut combat_ctx, event);
61            }
62            crate::state::DeferredWork::AuraExpire(expiry) => {
63                // #t(rust_unchecked_indexing) local is validated at build time
64                let aura = ctx.state.defs.auras[expiry.aura.as_usize()];
65
66                if let Some(hook_fn) = aura.on_expire {
67                    let mut hook_ctx = HookCtx::new(
68                        crate::context::HookCtxServices {
69                            state: ctx.state,
70                            buf: ctx.buf,
71                            sink: ctx.sink,
72                            rng: ctx.rng,
73                        },
74                        crate::context::HookCtxRequest::for_optional_target(ctx.now, expiry.target)
75                            .with_source(expiry.key.source()),
76                    )
77                    .with_driver_spell(aura.aura_id)
78                    .with_effect_target(expiry.key.affected());
79
80                    hook_fn(&mut hook_ctx);
81                }
82            }
83        }
84    }
85}