wowlab_engine_combat/systems/periodic/
schedule.rs1use 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 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 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}