wowlab_engine_combat/systems/auras/
breaks.rs1use 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}