Skip to main content

wowlab_engine_combat/systems/periodic/
schedule.rs

1use super::{AuraKey, CombatCtx, DenseBuffer, HookCtx, LocalAuraIdx, SimTime, apply_aura};
2
3pub(crate) fn fire_partial_tick_on_expiry_for(
4    ctx: &mut CombatCtx<'_>,
5    local: LocalAuraIdx,
6    key: AuraKey,
7) {
8    fire_partial_tick_on_expiry_with_hook(ctx.hook_ctx(), local, key);
9}
10
11pub(crate) fn fire_partial_tick_on_expiry_without_target(
12    ctx: &mut HookCtx<'_>,
13    local: LocalAuraIdx,
14    key: AuraKey,
15) {
16    let hook = HookCtx::new(
17        crate::context::HookCtxServices {
18            state: ctx.state,
19            buf: ctx.buf,
20            sink: ctx.sink,
21            rng: ctx.rng,
22        },
23        crate::context::HookCtxRequest::for_optional_target(ctx.now, ctx.target)
24            .with_source(ctx.source),
25    );
26
27    fire_partial_tick_on_expiry_with_hook(hook, local, key);
28}
29
30fn fire_partial_tick_on_expiry_with_hook(mut ctx: HookCtx<'_>, local: LocalAuraIdx, key: AuraKey) {
31    // #t(rust_unchecked_indexing) local is validated at build time
32    let aura = ctx.state.defs.auras[local.as_usize()];
33
34    ctx.set_source(key.source());
35    ctx = ctx.with_driver_spell(aura.aura_id);
36
37    let partial = aura
38        .periodic
39        .as_ref()
40        .is_some_and(|periodic| periodic.partial_tick);
41    let (Some(hook), true) = (aura.on_tick, partial) else {
42        return;
43    };
44    let Some(slot) = ctx.buf.aura(key) else {
45        return;
46    };
47
48    if !slot.is_occupied() || slot.tick_interval <= 0.0 {
49        return;
50    }
51
52    let now_s = ctx.now.as_secs_f64();
53    let last_tick_s = if slot.next_tick > now_s {
54        slot.next_tick - slot.tick_interval
55    } else {
56        slot.next_tick
57    };
58
59    if now_s <= last_tick_s {
60        return;
61    }
62
63    hook(&mut ctx);
64}
65
66#[cfg(test)]
67pub(crate) fn fire_partial_tick_on_expiry_fixture(
68    ctx: &mut CombatCtx<'_>,
69    local: LocalAuraIdx,
70    key: AuraKey,
71) {
72    fire_partial_tick_on_expiry_for(ctx, local, key);
73}
74
75pub(crate) fn reapply_if_flagged(ctx: &mut HookCtx<'_>, local: LocalAuraIdx) {
76    // #t(rust_unchecked_indexing) local is validated at build time
77    if ctx.state.defs.auras[local.as_usize()].reapply_on_expire {
78        apply_aura(ctx, local);
79    }
80}
81
82pub(super) fn aura_tick_due(buf: &DenseBuffer, key: AuraKey, now: SimTime) -> bool {
83    let Some(a) = buf.aura(key) else {
84        return false;
85    };
86
87    a.is_occupied() && now.as_secs_f64() >= a.next_tick
88}