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.instance.runtime.deferred_work.pop_next() {
12        processed += 1;
13
14        if processed > MAX_DEFERRED_WORK_ITEMS {
15            ctx.instance.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.instance.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                        profile_action: program.profile_action,
32                        flags: program.flags,
33                    };
34
35                    if program.originating_proc_driver_spell_id == 0 {
36                        execute_effect_range_for_actor(
37                            &mut combat_ctx,
38                            execution,
39                            program.effect_target,
40                        );
41                    } else {
42                        execute_proc_effect_range_for_actor(
43                            &mut combat_ctx,
44                            execution,
45                            program.effect_target,
46                            program.originating_proc_driver_spell_id,
47                        );
48                    }
49                }
50            }
51            crate::state::DeferredWork::AuraApplication(event) => {
52                let target = match event.affected {
53                    ActorId::Enemy(target) => target,
54                    _ => event
55                        .target
56                        .or_else(|| ctx.instance.current_target())
57                        .unwrap_or(EnemyIdx::PRIMARY),
58                };
59                let mut combat_ctx = ctx.combat_ctx_with_source(event.source, target);
60
61                fire_aura_application_procs(&mut combat_ctx, event);
62            }
63            crate::state::DeferredWork::AuraExpire(expiry) => {
64                // #t(rust_unchecked_indexing) local is validated at build time
65                let aura = ctx.program.auras[expiry.aura.as_usize()];
66
67                if let Some(hook_fn) = aura.on_expire {
68                    let mut hook_ctx = HookCtx::new(
69                        crate::context::HookCtxServices {
70                            program: ctx.program,
71                            instance: ctx.instance,
72                            sink: ctx.sink,
73                            rng: ctx.rng,
74                        },
75                        crate::context::HookCtxRequest::for_optional_target(ctx.now, expiry.target)
76                            .with_source(expiry.key.source()),
77                    )
78                    .with_driver_spell(aura.aura_id)
79                    .with_effect_target(expiry.key.affected());
80
81                    hook_fn(&mut hook_ctx);
82                }
83            }
84        }
85    }
86}