Skip to main content

wowlab_engine_domain/dbc/semantics/
stances.rs

1//! Shapeshift-stance semantics; a spell's stance mask sets bit `N - 1` for each castable form `N`.
2
3bitflags::bitflags! {
4    /// Behavior flags from `SpellShapeshiftForm.Flags`.
5    #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
6    pub struct SpellShapeshiftFormFlags: i32 {
7        const STANCE = 1 << 0;
8        const NOT_TOGGLEABLE = 1 << 1;
9        const PERSIST_ON_DEATH = 1 << 2;
10        const CAN_INTERACT_NPCS = 1 << 3;
11        const DONT_USE_WEAPON = 1 << 4;
12        const AGILITY_ATTACK_BONUS = 1 << 5;
13        const CAN_USE_EQUIPPED_ITEMS = 1 << 6;
14        const CAN_USE_ITEMS = 1 << 7;
15        const DONT_AUTO_UNSHIFT = 1 << 8;
16        const CONSIDERED_DEAD = 1 << 9;
17        const CAN_ONLY_CAST_SHAPESHIFT_SPELLS = 1 << 10;
18        const STANCE_CANCELS_AT_FLIGHTMASTER = 1 << 11;
19        const NO_EMOTE_SOUNDS = 1 << 12;
20        const NO_TRIGGER_TELEPORT = 1 << 13;
21        const CANNOT_CHANGE_EQUIPPED_ITEMS = 1 << 14;
22        const RESUMMON_PETS_ON_UNSHIFT = 1 << 15;
23        const CANNOT_USE_GAME_OBJECTS = 1 << 16;
24    }
25}
26
27impl SpellShapeshiftFormFlags {
28    /// Trinity treats stances as caster form rather than as a shifted body form.
29    #[must_use]
30    pub const fn acts_as_shifted(self) -> bool {
31        !self.contains(Self::STANCE)
32    }
33}
34
35/// A `WoW` shapeshift form needing a bespoke cast-gating bridge (only Stealth; ordinary forms resolve via `MOD_SHAPESHIFT`).
36#[derive(Clone, Copy, Debug, Eq, PartialEq)]
37#[repr(u32)]
38#[non_exhaustive]
39pub enum ShapeshiftForm {
40    /// Rogue stealth stance (`FORM_STEALTH` = 30), tracked separately since it grants no `MOD_SHAPESHIFT` aura.
41    Stealth = 30,
42}
43
44impl ShapeshiftForm {
45    /// The stance-mask bit for this form.
46    #[must_use]
47    pub const fn stance_mask(self) -> u64 {
48        1 << (self as u32 - 1)
49    }
50}
51
52/// Whether a spell's stance mask permits the given shapeshift form.
53#[must_use]
54pub const fn stance_mask_allows(mask: u64, form: ShapeshiftForm) -> bool {
55    mask & form.stance_mask() != 0
56}
57
58#[cfg(test)]
59mod tests {
60    use googletest::prelude::*;
61
62    use super::*;
63
64    #[gtest]
65    fn stealth_stance_bit_is_form_30() {
66        expect_that!(ShapeshiftForm::Stealth.stance_mask(), eq(1 << 29));
67        expect_that!(
68            stance_mask_allows(0x2000_0000, ShapeshiftForm::Stealth),
69            is_true()
70        );
71        expect_that!(!stance_mask_allows(0, ShapeshiftForm::Stealth), is_true());
72    }
73
74    #[gtest]
75    fn stance_forms_do_not_act_as_shifted() {
76        let shadowform = SpellShapeshiftFormFlags::from_bits_retain(9);
77
78        expect_that!(!shadowform.acts_as_shifted(), is_true());
79
80        let cat_form = SpellShapeshiftFormFlags::from_bits_retain(248);
81
82        expect_that!(cat_form.acts_as_shifted(), is_true());
83    }
84}