wowlab_engine_domain/dbc/semantics/
schools.rs1use wowlab_types::combat::DamageSchool;
2
3bitflags::bitflags! {
4 #[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq)]
6 pub struct SpellSchoolMask: u8 {
7 const PHYSICAL = 1 << 0;
8 const HOLY = 1 << 1;
9 const FIRE = 1 << 2;
10 const NATURE = 1 << 3;
11 const FROST = 1 << 4;
12 const SHADOW = 1 << 5;
13 const ARCANE = 1 << 6;
14 const CHAOS = Self::PHYSICAL.bits()
15 | Self::HOLY.bits()
16 | Self::FIRE.bits()
17 | Self::NATURE.bits()
18 | Self::FROST.bits()
19 | Self::SHADOW.bits()
20 | Self::ARCANE.bits();
21 }
22}
23
24impl SpellSchoolMask {
25 #[must_use]
26 pub const fn from_dbc(raw: i32) -> Self {
27 Self::from_bits_truncate(raw.to_le_bytes()[0])
28 }
29
30 #[must_use]
32 pub fn primary_school(self) -> DamageSchool {
33 if self.bits() == Self::CHAOS.bits() {
34 return DamageSchool::Chaos;
35 }
36
37 [
38 (Self::PHYSICAL, DamageSchool::Physical),
39 (Self::HOLY, DamageSchool::Holy),
40 (Self::FIRE, DamageSchool::Fire),
41 (Self::NATURE, DamageSchool::Nature),
42 (Self::FROST, DamageSchool::Frost),
43 (Self::SHADOW, DamageSchool::Shadow),
44 (Self::ARCANE, DamageSchool::Arcane),
45 ]
46 .into_iter()
47 .find_map(|(mask, school)| self.contains(mask).then_some(school))
48 .unwrap_or(DamageSchool::Physical)
49 }
50}
51
52impl From<DamageSchool> for SpellSchoolMask {
53 fn from(school: DamageSchool) -> Self {
54 match school {
55 DamageSchool::Physical => Self::PHYSICAL,
56 DamageSchool::Holy => Self::HOLY,
57 DamageSchool::Fire => Self::FIRE,
58 DamageSchool::Nature => Self::NATURE,
59 DamageSchool::Frost => Self::FROST,
60 DamageSchool::Shadow => Self::SHADOW,
61 DamageSchool::Arcane => Self::ARCANE,
62 DamageSchool::Chaos => Self::CHAOS,
63 }
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use googletest::prelude::*;
70
71 use super::*;
72
73 #[gtest]
74 fn chaos_is_the_complete_school_mask_but_not_physical_resolution() {
75 let chaos = SpellSchoolMask::CHAOS;
76
77 expect_that!(chaos.contains(SpellSchoolMask::PHYSICAL), is_true());
78 expect_that!(chaos.primary_school(), eq(DamageSchool::Chaos));
79 }
80
81 #[gtest]
82 fn multi_school_resolution_uses_lowest_set_school() {
83 let mask = SpellSchoolMask::FIRE | SpellSchoolMask::SHADOW;
84
85 expect_that!(mask.primary_school(), eq(DamageSchool::Fire));
86 }
87}