Skip to main content

wowlab_engine_combat/systems/auras/
breaks.rs

1use super::{
2    ActorId, AuraBreakTrigger, CombatState, DenseBuffer, HookCtx, SpellAuraInterruptFlags,
3    expire_aura, expire_aura_instance, is_aura_active, refresh_aura_projections,
4};
5
6pub(crate) fn break_auras_on_actor(
7    ctx: &mut HookCtx<'_>,
8    actor: ActorId,
9    trigger: AuraBreakTrigger,
10) {
11    let keys: Vec<_> = ctx
12        .buf
13        .aura_keys()
14        .filter(|key| key.affected() == actor)
15        .filter(|key| {
16            let Some(local) = ctx.state.aura_local_for_key(*key) else {
17                return false;
18            };
19            // BOUNDS: aura_local_for_key resolves indices from this definition table.
20            let aura = ctx.state.defs.auras[local.as_usize()];
21
22            match trigger {
23                AuraBreakTrigger::Action => aura
24                    .interrupt_flags
25                    .contains(SpellAuraInterruptFlags::ACTION),
26                AuraBreakTrigger::Damage { periodic } => {
27                    aura.interrupt_flags
28                        .contains(SpellAuraInterruptFlags::DAMAGE)
29                        || (!periodic
30                            && aura
31                                .interrupt_flags
32                                .contains(SpellAuraInterruptFlags::NON_PERIODIC_DAMAGE))
33                }
34            }
35        })
36        .collect();
37
38    for key in keys {
39        let Some(local) = ctx.state.aura_local_for_key(key) else {
40            continue;
41        };
42
43        expire_aura_instance(ctx, local, key);
44    }
45}
46
47pub(crate) fn break_auras_without_telemetry(
48    state: &mut CombatState,
49    buf: &mut DenseBuffer,
50    actor: ActorId,
51    periodic_damage: bool,
52) {
53    let keys: Vec<_> = buf
54        .aura_keys()
55        .filter(|key| key.affected() == actor)
56        .filter(|key| {
57            let Some(local) = state.aura_local_for_key(*key) else {
58                return false;
59            };
60            // BOUNDS: aura_local_for_key resolves indices from this definition table.
61            let aura = state.defs.auras[local.as_usize()];
62
63            aura.interrupt_flags
64                .contains(SpellAuraInterruptFlags::DAMAGE)
65                || (!periodic_damage
66                    && aura
67                        .interrupt_flags
68                        .contains(SpellAuraInterruptFlags::NON_PERIODIC_DAMAGE))
69        })
70        .collect();
71
72    for key in keys {
73        if let Some(slot) = buf.aura_mut(key) {
74            slot.remove();
75        }
76
77        state
78            .runtime
79            .health
80            .active_absorbs
81            .retain(|absorb| absorb.key != key);
82    }
83
84    refresh_aura_projections(buf, state.current_target());
85}
86
87pub(crate) fn break_stealth_if_active(ctx: &mut HookCtx<'_>) {
88    let Some(stealth_aura_id) = ctx.state.defs.stealth_aura_id else {
89        return;
90    };
91    let Some(&local) = ctx.state.index.aura_by_id.get(&stealth_aura_id) else {
92        return;
93    };
94
95    if is_aura_active(&ctx.view().combat(), local, ctx.source, ctx.target) {
96        expire_aura(ctx, local);
97        ctx.buf.player_mut().stealthed = 0;
98    }
99}