Skip to main content

wowlab_engine_domain/pool/
spell_gate.rs

1use std::hash::Hash;
2
3use wowlab_types::sim::FastSet;
4
5use super::base::Pool;
6
7/// Independent reasons that can temporarily block an otherwise available spell.
8#[derive(Clone, Copy, Debug, Eq, PartialEq)]
9#[non_exhaustive]
10pub enum SpellGate {
11    ReactionDelay,
12    TargetHealth,
13    /// One or more active aura-263 effects disable the affected spell.
14    ActiveAuraRestriction,
15}
16
17impl SpellGate {
18    const fn blocker(self) -> SpellGateBlockers {
19        match self {
20            Self::ReactionDelay => SpellGateBlockers::REACTION_DELAY,
21            Self::TargetHealth => SpellGateBlockers::TARGET_HEALTH,
22            Self::ActiveAuraRestriction => SpellGateBlockers::ACTIVE_AURA_RESTRICTION,
23        }
24    }
25}
26
27bitflags::bitflags! {
28    #[derive(Clone, Copy, Debug, Default)]
29    struct SpellGateBlockers: u8 {
30        const REACTION_DELAY = 1 << 0;
31        const TARGET_HEALTH = 1 << 1;
32        const ACTIVE_AURA_RESTRICTION = 1 << 2;
33    }
34}
35
36#[derive(Clone, Copy, Debug)]
37struct SpellGateState {
38    base_enabled: bool,
39    blockers: SpellGateBlockers,
40}
41
42#[derive(Clone, Copy, Debug)]
43pub struct SpellGateUpdate {
44    pub base_enabled: bool,
45    pub available: bool,
46}
47
48#[derive(Clone, Debug)]
49pub struct SpellGatePool<K> {
50    state: Pool<K, SpellGateState>,
51    aura_state_exceptions: FastSet<K>,
52}
53
54impl<K> SpellGatePool<K>
55where
56    K: Copy + Eq + Hash,
57{
58    #[must_use]
59    pub fn new() -> Self {
60        Self::default()
61    }
62
63    pub fn set(&mut self, key: K, gate: SpellGate, update: SpellGateUpdate) -> bool {
64        let state = self.state.get_or_insert_with(key, || SpellGateState {
65            base_enabled: update.base_enabled,
66            blockers: SpellGateBlockers::empty(),
67        });
68
69        if update.available {
70            state.blockers.remove(gate.blocker());
71        } else {
72            state.blockers.insert(gate.blocker());
73        }
74
75        state.base_enabled && state.blockers.is_empty()
76    }
77
78    pub fn set_aura_state_exception(&mut self, key: K, active: bool) {
79        if active {
80            self.aura_state_exceptions.insert(key);
81        } else {
82            self.aura_state_exceptions.remove(&key);
83        }
84    }
85
86    pub fn ignores_aura_state(&self, key: &K) -> bool {
87        self.aura_state_exceptions.contains(key)
88    }
89
90    pub fn reset(&mut self) -> Vec<(K, bool)> {
91        self.aura_state_exceptions.clear();
92
93        self.state
94            .drain()
95            .map(|(key, state)| (key, state.base_enabled))
96            .collect()
97    }
98}
99
100impl<K> Default for SpellGatePool<K> {
101    fn default() -> Self {
102        Self {
103            state: Pool::default(),
104            aura_state_exceptions: FastSet::default(),
105        }
106    }
107}
108
109#[cfg(test)]
110#[path = "spell_gate/tests.rs"]
111mod tests;