Skip to main content

wowlab_engine_combat/handler/spec_handler/
auras.rs

1use super::{
2    ActorId, AuraEventRef, CombatCtx, CombatHandler, Event, HookCtxRequest, SimContext, SimTime,
3    SpellIdx, drain_deferred_work, process_single_aura_tick_for,
4};
5
6impl CombatHandler {
7    pub(super) fn on_aura_tick_impl(&mut self, event: AuraEventRef, ctx: &mut SimContext) {
8        let AuraEventRef { key, target } = event;
9        let now = ctx.state.current_time;
10
11        let resolved_target = crate::systems::periodic_tick_target(&self.state, key, target);
12        let deferred_target = target.or_else(|| match key.affected() {
13            ActorId::Enemy(enemy) => Some(enemy),
14            _ => None,
15        });
16
17        self.with_hook_ctx(
18            &mut *ctx.telemetry,
19            HookCtxRequest::for_optional_target(now, deferred_target).with_source(key.source()),
20            |hook| {
21                let processed = CombatCtx::for_optional_target(hook, resolved_target, |combat| {
22                    process_single_aura_tick_for(combat, key);
23                });
24
25                if processed.is_none() {
26                    drain_deferred_work(hook);
27                }
28            },
29        );
30    }
31
32    pub(super) fn on_aura_expire_impl(&mut self, event: AuraEventRef, ctx: &mut SimContext) {
33        let AuraEventRef { key, target } = event;
34        let now = ctx.state.current_time;
35
36        let Some(local) = self.state.aura_local_for_key(key) else {
37            return;
38        };
39
40        // #t(rust_unchecked_indexing) local is from aura_by_identity, always valid
41        if self.state.defs.auras[local.as_usize()].async_stacks {
42            self.with_hook_ctx(
43                &mut *ctx.telemetry,
44                HookCtxRequest::for_optional_target(now, target)
45                    .with_source(key.source())
46                    .with_effect_target(key.affected()),
47                |hook| {
48                    crate::systems::expire_async_stacks_due(hook, local, key);
49                    drain_deferred_work(hook);
50                },
51            );
52
53            return;
54        }
55
56        let should_expire = self
57            .buf
58            .aura(key)
59            .is_some_and(|a| a.expires_at > 0.0 && now >= SimTime::from_secs_f64(a.expires_at));
60
61        if should_expire {
62            let aura = self
63                .state
64                .defs
65                .auras
66                .get(local.as_usize())
67                .copied()
68                .expect("aura expiry carries a registered local index");
69
70            self.with_hook_ctx(
71                &mut *ctx.telemetry,
72                HookCtxRequest::for_optional_target(now, target)
73                    .with_source(key.source())
74                    .with_effect_target(key.affected()),
75                |hook| {
76                    let fired = CombatCtx::for_optional_target(hook, target, |combat| {
77                        crate::systems::fire_partial_tick_on_expiry_for(combat, local, key);
78                    });
79
80                    if fired.is_none() {
81                        crate::systems::fire_partial_tick_on_expiry_without_target(
82                            hook, local, key,
83                        );
84                    }
85
86                    crate::systems::expire_aura(hook, local);
87                    crate::systems::reapply_if_flagged(hook, local);
88                    drain_deferred_work(hook);
89                },
90            );
91
92            if aura.expire_trigger_spell_id != 0 {
93                let source = if aura.expire_trigger_from_caster {
94                    key.source()
95                } else {
96                    key.affected()
97                };
98
99                self.state.schedule(Event::TriggeredSpell {
100                    t: now,
101                    spell_id: SpellIdx::from_raw(aura.expire_trigger_spell_id),
102                    source,
103                    target: key.affected(),
104                });
105            }
106        }
107    }
108
109    pub(super) fn on_external_buff_impl(&mut self, index: usize, ctx: &mut SimContext) {
110        self.handle_external_buff(index, ctx);
111    }
112}