Skip to main content

wowlab_engine_combat/state/
aura_state.rs

1use wowlab_engine_domain::rotation::DenseBuffer;
2use wowlab_types::sim::{ActorId, SimTime};
3
4use super::CombatState;
5
6#[rustfmt::skip]
7pub(crate) use wowlab_engine_domain::dbc::AuraState;
8
9const WOUNDED_20_THRESHOLD: f64 = 0.20;
10const WOUNDED_25_THRESHOLD: f64 = 0.25;
11const WOUNDED_35_THRESHOLD: f64 = 0.35;
12const WOUNDED_50_THRESHOLD: f64 = 0.50;
13const HEALTHY_75_THRESHOLD: f64 = 0.75;
14const HEALTHY_80_THRESHOLD: f64 = 0.80;
15
16impl CombatState {
17    /// Resolve health-derived and aura-granted state for either friendly or hostile actors.
18    #[must_use]
19    pub fn has_aura_state(
20        &self,
21        buf: &DenseBuffer,
22        actor: ActorId,
23        required: AuraState,
24        now: SimTime,
25    ) -> bool {
26        let health = self.actor_health_fraction(actor, now);
27        let health_state = match required {
28            AuraState::Wounded20 => health.is_some_and(|value| value < WOUNDED_20_THRESHOLD),
29            AuraState::Wounded25 => health.is_some_and(|value| value < WOUNDED_25_THRESHOLD),
30            AuraState::Wounded35 => health.is_some_and(|value| value < WOUNDED_35_THRESHOLD),
31            AuraState::Wounded50 => health.is_some_and(|value| value < WOUNDED_50_THRESHOLD),
32            AuraState::Healthy75 => health.is_some_and(|value| value > HEALTHY_75_THRESHOLD),
33            AuraState::WoundHealth20OrAbove80 => health.is_some_and(|value| {
34                !(WOUNDED_20_THRESHOLD..=HEALTHY_80_THRESHOLD).contains(&value)
35            }),
36            AuraState::WoundHealth35OrAbove80 => health.is_some_and(|value| {
37                !(WOUNDED_35_THRESHOLD..=HEALTHY_80_THRESHOLD).contains(&value)
38            }),
39            _ => false,
40        };
41
42        if health_state {
43            return true;
44        }
45
46        buf.aura_keys().any(|key| {
47            key.affected() == actor
48                && buf
49                    .aura(key)
50                    .is_some_and(|slot| slot.is_active(now.as_secs_f64()))
51                && self.aura_local_for_key(key).is_some_and(|local| {
52                    // BOUNDS: aura_local_for_key resolves indices from this definition table.
53                    self.defs.auras[local.as_usize()].granted_aura_state == Some(required)
54                })
55        })
56    }
57}
58
59#[cfg(test)]
60mod tests;