Skip to main content

wowlab_engine_combat/systems/auras/
breaks.rs

1use super::{
2    ActorId, AuraBreakTrigger, CombatInstance, HookCtx, expire_aura, expire_aura_instance,
3    is_aura_active,
4};
5use crate::state::AuraMutation;
6
7pub(crate) fn break_auras_on_actor(
8    ctx: &mut HookCtx<'_>,
9    actor: ActorId,
10    trigger: AuraBreakTrigger,
11) {
12    #[cfg(feature = "rotation-optimizations")]
13    {
14        let plan = match trigger {
15            AuraBreakTrigger::Action => ctx.program.effects.action_break_aura_plan(),
16            AuraBreakTrigger::Damage { periodic } => {
17                ctx.program.effects.damage_break_aura_plan(periodic)
18            }
19        };
20
21        while let Some(binding) = ctx
22            .instance
23            .first_active_aura_intersection(actor, plan.relevant_locals())
24        {
25            expire_aura_instance(ctx, binding.local, binding.key);
26        }
27
28        return;
29    }
30
31    #[cfg(not(feature = "rotation-optimizations"))]
32    {
33        let entries = match trigger {
34            AuraBreakTrigger::Action => ctx.program.effects.action_break_auras(actor),
35            AuraBreakTrigger::Damage { periodic } => {
36                ctx.program.effects.damage_break_auras(actor, periodic)
37            }
38        };
39        let entry_count = entries.len();
40
41        for index in 0..entry_count {
42            let entry = entries
43                .get(index)
44                .copied()
45                .expect("bounded actor-effect index");
46
47            if !ctx.instance.buffer.aura_at(entry.handle).is_occupied() {
48                continue;
49            }
50
51            expire_aura_instance(ctx, entry.local, entry.key);
52        }
53    }
54}
55
56pub(crate) fn break_auras_without_telemetry(
57    program: &crate::state::CombatProgram,
58    state: &mut CombatInstance,
59    actor: ActorId,
60    periodic_damage: bool,
61    now: wowlab_types::sim::SimTime,
62) {
63    #[cfg(feature = "rotation-optimizations")]
64    {
65        let plan = program.effects.damage_break_aura_plan(periodic_damage);
66
67        while let Some(binding) =
68            state.first_active_aura_intersection(actor, plan.relevant_locals())
69        {
70            program.mutate_aura(state, binding, AuraMutation::Expire, now);
71            state
72                .runtime
73                .health
74                .active_absorbs
75                .retain(|absorb| absorb.key != binding.key);
76        }
77
78        return;
79    }
80
81    #[cfg(not(feature = "rotation-optimizations"))]
82    {
83        let entries = program.effects.damage_break_auras(actor, periodic_damage);
84        let entry_count = entries.len();
85
86        for index in 0..entry_count {
87            let entry = entries
88                .get(index)
89                .copied()
90                .expect("bounded actor-effect index");
91
92            if !state.buffer.aura_at(entry.handle).is_occupied() {
93                continue;
94            }
95
96            if let Some(binding) = program.aura_binding(entry.local, entry.key) {
97                program.mutate_aura(state, binding, AuraMutation::Expire, now);
98            }
99
100            state
101                .runtime
102                .health
103                .active_absorbs
104                .retain(|absorb| absorb.key != entry.key);
105        }
106    }
107}
108
109pub(crate) fn break_stealth_if_active(ctx: &mut HookCtx<'_>) {
110    let Some(local) = ctx.program.stealth_aura else {
111        return;
112    };
113
114    if is_aura_active(&ctx.view().combat(), local, ctx.source, ctx.target) {
115        expire_aura(ctx, local);
116        ctx.instance.buffer.player_mut().stealthed = 0;
117    }
118}