Skip to main content

deepwoken_reqparse/model/
stat.rs

1use std::fmt;
2use std::str::FromStr;
3
4use serde::{Deserialize, Deserializer, Serialize, de};
5
6#[repr(u32)]
7#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
8pub enum Stat {
9    Strength = 0,
10    Fortitude = 1,
11    Agility = 2,
12    Intelligence = 3,
13    Willpower = 4,
14    Charisma = 5,
15    HeavyWeapon = 6,
16    MediumWeapon = 7,
17    LightWeapon = 8,
18    Frostdraw = 9,
19    Flamecharm = 10,
20    Thundercall = 11,
21    Galebreathe = 12,
22    Shadowcast = 13,
23    Ironsing = 14,
24    Bloodrend = 15,
25    /// A stat representing the total cost of all stats, aka the 'Cost'
26    /// Can and should be used to model power levels
27    Total = 16
28}
29
30impl Stat {
31    pub fn from_u32_unchecked(value: u32) -> Self {
32        // LOL
33        unsafe { std::mem::transmute(value) }
34    }
35    
36    pub fn short_name(&self) -> &'static str {
37        match self {
38            Stat::Strength => "STR",
39            Stat::Fortitude => "FTD",
40            Stat::Agility => "AGL",
41            Stat::Intelligence => "INT",
42            Stat::Willpower => "WLL",
43            Stat::Charisma => "CHA",
44            Stat::HeavyWeapon => "HVY",
45            Stat::MediumWeapon => "MED",
46            Stat::LightWeapon => "LHT",
47            Stat::Frostdraw => "ICE",
48            Stat::Flamecharm => "FLM",
49            Stat::Thundercall => "LTN",
50            Stat::Galebreathe => "WND",
51            Stat::Shadowcast => "SDW",
52            Stat::Ironsing => "MTL",
53            Stat::Bloodrend => "BLD",
54            Stat::Total => "TTL",
55        }
56    }
57
58    pub fn name(&self) -> &'static str {
59        match self {
60            Stat::Strength => "Strength",
61            Stat::Fortitude => "Fortitude",
62            Stat::Agility => "Agility",
63            Stat::Intelligence => "Intelligence",
64            Stat::Willpower => "Willpower",
65            Stat::Charisma => "Charisma",
66            Stat::HeavyWeapon => "Heavy Wep.",
67            Stat::MediumWeapon => "Medium Wep.",
68            Stat::LightWeapon => "Light Wep.",
69            Stat::Frostdraw => "Frostdraw",
70            Stat::Flamecharm => "Flamecharm",
71            Stat::Thundercall => "Thundercall",
72            Stat::Galebreathe => "Galebreathe",
73            Stat::Shadowcast => "Shadowcast",
74            Stat::Ironsing => "Ironsing",
75            Stat::Bloodrend => "Bloodrend",
76            Stat::Total => "Total",
77        }
78    }
79
80    pub fn from_name(name: &str) -> Option<Self> {
81        let name = name.to_uppercase();
82        match name.as_str() {
83            "STRENGTH" => Some(Stat::Strength),
84            "FORTITUDE" => Some(Stat::Fortitude),
85            "AGILITY" => Some(Stat::Agility),
86            "INTELLIGENCE" => Some(Stat::Intelligence),
87            "WILLPOWER" => Some(Stat::Willpower),
88            "CHARISMA" => Some(Stat::Charisma),
89            "HEAVY WEP." | "HEAVY" => Some(Stat::HeavyWeapon),
90            "MEDIUM WEP." | "MEDIUM" => Some(Stat::MediumWeapon),
91            "LIGHT WEP." | "LIGHT" => Some(Stat::LightWeapon),
92            "FROSTDRAW" => Some(Stat::Frostdraw),
93            "FLAMECHARM" => Some(Stat::Flamecharm),
94            "THUNDERCALL" => Some(Stat::Thundercall),
95            "GALEBREATHE" => Some(Stat::Galebreathe),
96            "SHADOWCAST" => Some(Stat::Shadowcast),
97            "IRONSING" => Some(Stat::Ironsing),
98            "BLOODREND" => Some(Stat::Bloodrend),
99            "TOTAL" => Some(Stat::Total),
100            _ => None,
101        }
102    }
103
104    pub fn from_short_name(short: &str) -> Option<Self> {
105        let short = short.to_uppercase();
106
107        match short.as_str() {
108            "STR" => Some(Stat::Strength),
109            "FTD" => Some(Stat::Fortitude),
110            "AGL" | "AGI" => Some(Stat::Agility),
111            "INT" => Some(Stat::Intelligence),
112            // bruh
113            "WLL" | "WIL" => Some(Stat::Willpower),
114            "CHA" => Some(Stat::Charisma),
115            "HVY" => Some(Stat::HeavyWeapon),
116            "MED" => Some(Stat::MediumWeapon),
117            "LHT" => Some(Stat::LightWeapon),
118            "ICE" => Some(Stat::Frostdraw),
119            "FLM" | "FIR" => Some(Stat::Flamecharm),
120            "LTN" => Some(Stat::Thundercall),
121            "WND" => Some(Stat::Galebreathe),
122            "SDW" => Some(Stat::Shadowcast),
123            "MTL" => Some(Stat::Ironsing),
124            "BLD" => Some(Stat::Bloodrend),
125            "TTL" | "TOT" => Some(Stat::Total),
126            _ => None,
127        }
128    }
129
130    pub const fn is_attunement(&self) -> bool {
131        match self {
132            Stat::Frostdraw | Stat::Flamecharm | Stat::Thundercall
133            | Stat::Galebreathe | Stat::Shadowcast | Stat::Ironsing
134            | Stat::Bloodrend => true,
135            _ => false,
136        }
137    }
138
139    pub const fn as_u32(self) -> u32 {
140        self as u32
141    }
142
143    pub const fn as_i64(self) -> i64 {
144        self as i64
145    }
146}
147
148impl From<Stat> for u32 {
149    fn from(stat: Stat) -> u32 {
150        stat as u32
151    }
152}
153
154impl From<Stat> for i64 {
155    fn from(stat: Stat) -> i64 {
156        stat as i64
157    }
158}
159
160impl TryFrom<u32> for Stat {
161    type Error = &'static str;
162
163    fn try_from(value: u32) -> Result<Self, Self::Error> {
164        match value {
165            0 => Ok(Stat::Strength),
166            1 => Ok(Stat::Fortitude),
167            2 => Ok(Stat::Agility),
168            3 => Ok(Stat::Intelligence),
169            4 => Ok(Stat::Willpower),
170            5 => Ok(Stat::Charisma),
171            6 => Ok(Stat::HeavyWeapon),
172            7 => Ok(Stat::MediumWeapon),
173            8 => Ok(Stat::LightWeapon),
174            9 => Ok(Stat::Frostdraw),
175            10 => Ok(Stat::Flamecharm),
176            11 => Ok(Stat::Thundercall),
177            12 => Ok(Stat::Galebreathe),
178            13 => Ok(Stat::Shadowcast),
179            14 => Ok(Stat::Ironsing),
180            15 => Ok(Stat::Bloodrend),
181            _ => Err("Invalid stat id"),
182        }
183    }
184}
185
186impl TryFrom<i64> for Stat {
187    type Error = &'static str;
188
189    fn try_from(value: i64) -> Result<Self, Self::Error> {
190        if value < 0 {
191            return Err("Stat id cannot be negative");
192        }
193        (value as u32).try_into()
194    }
195}
196
197impl FromStr for Stat {
198    type Err = &'static str;
199
200    fn from_str(s: &str) -> Result<Self, Self::Err> {
201        Self::from_short_name(s)
202            .or_else(|| Self::from_name(s))
203            .ok_or("Invalid stat name or abbreviation")
204    }
205}
206
207impl fmt::Debug for Stat {
208    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
209        write!(f, "{}", self.name())
210    }
211}
212
213impl fmt::Display for Stat {
214    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215        write!(f, "{}", self.name())
216    }
217}
218
219impl From<Stat> for String {
220    fn from(stat: Stat) -> String {
221        stat.name().to_string()
222    }
223}
224
225impl<'de> Deserialize<'de> for Stat {
226    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
227    where
228        D: Deserializer<'de>,
229    {
230        let s = String::deserialize(deserializer)?;
231        // we can do this since it implements from_str
232        s.parse().map_err(de::Error::custom)
233    }
234}
235
236impl Serialize for Stat {
237    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
238    where
239        S: serde::Serializer,
240    {
241        serializer.serialize_str(self.name())
242    }
243}