1use std::fmt::Debug;
2
3use ahash::HashMap;
4use serde::{
5 Deserialize,
6 Serialize,
7};
8use serde_string_enum::{
9 DeserializeLabeledStringEnum,
10 SerializeLabeledStringEnum,
11};
12
13#[derive(
15 Debug,
16 Clone,
17 Copy,
18 PartialEq,
19 Eq,
20 Hash,
21 SerializeLabeledStringEnum,
22 DeserializeLabeledStringEnum,
23)]
24pub enum Stat {
25 #[string = "hp"]
26 HP,
27 #[string = "atk"]
28 #[alias = "Attack"]
29 Atk,
30 #[string = "def"]
31 #[alias = "Defense"]
32 Def,
33 #[string = "spa"]
34 #[alias = "spatk"]
35 #[alias = "Sp.Atk"]
36 #[alias = "Special Attack"]
37 SpAtk,
38 #[string = "spd"]
39 #[alias = "spdef"]
40 #[alias = "Sp.Def"]
41 #[alias = "Special Defense"]
42 SpDef,
43 #[string = "spe"]
44 #[alias = "Speed"]
45 Spe,
46}
47
48pub type StatMap<T> = HashMap<Stat, T>;
50
51pub type PartialStatTable = StatMap<u16>;
53
54fn next_stat_for_iterator(stat: Stat) -> Option<Stat> {
55 match stat {
56 Stat::HP => Some(Stat::Atk),
57 Stat::Atk => Some(Stat::Def),
58 Stat::Def => Some(Stat::SpAtk),
59 Stat::SpAtk => Some(Stat::SpDef),
60 Stat::SpDef => Some(Stat::Spe),
61 Stat::Spe => None,
62 }
63}
64
65pub struct StatTableEntries<'s> {
67 table: &'s StatTable,
68 next_stat: Option<Stat>,
69}
70
71impl<'s> StatTableEntries<'s> {
72 fn new(table: &'s StatTable) -> Self {
74 Self {
75 table,
76 next_stat: Some(Stat::HP),
77 }
78 }
79}
80
81impl<'s> Iterator for StatTableEntries<'s> {
82 type Item = (Stat, u16);
83
84 fn next(&mut self) -> Option<Self::Item> {
85 let stat = self.next_stat?;
86 let value = self.table.get(stat);
87 self.next_stat = next_stat_for_iterator(stat);
88 Some((stat, value))
89 }
90}
91
92#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
96pub struct StatTable {
97 #[serde(default)]
98 pub hp: u16,
99 #[serde(default)]
100 pub atk: u16,
101 #[serde(default)]
102 pub def: u16,
103 #[serde(default)]
104 pub spa: u16,
105 #[serde(default)]
106 pub spd: u16,
107 #[serde(default)]
108 pub spe: u16,
109}
110
111impl StatTable {
112 pub fn get(&self, stat: Stat) -> u16 {
114 match stat {
115 Stat::HP => self.hp,
116 Stat::Atk => self.atk,
117 Stat::Def => self.def,
118 Stat::SpAtk => self.spa,
119 Stat::SpDef => self.spd,
120 Stat::Spe => self.spe,
121 }
122 }
123
124 pub fn set(&mut self, stat: Stat, value: u16) {
126 let stat = match stat {
127 Stat::HP => &mut self.hp,
128 Stat::Atk => &mut self.atk,
129 Stat::Def => &mut self.def,
130 Stat::SpAtk => &mut self.spa,
131 Stat::SpDef => &mut self.spd,
132 Stat::Spe => &mut self.spe,
133 };
134 *stat = value;
135 }
136
137 pub fn entries<'s>(&'s self) -> StatTableEntries<'s> {
139 StatTableEntries::new(self)
140 }
141
142 pub fn values<'s>(&'s self) -> impl Iterator<Item = u16> + 's {
144 self.entries().map(|(_, value)| value)
145 }
146
147 pub fn sum(&self) -> u32 {
149 self.hp as u32
150 + self.atk as u32
151 + self.def as u32
152 + self.spa as u32
153 + self.spd as u32
154 + self.spe as u32
155 }
156
157 pub fn without_hp(&self) -> PartialStatTable {
159 PartialStatTable::from_iter([
160 (Stat::Atk, self.atk),
161 (Stat::Def, self.def),
162 (Stat::SpAtk, self.spa),
163 (Stat::SpDef, self.spd),
164 (Stat::Spe, self.spe),
165 ])
166 }
167}
168
169impl From<&PartialStatTable> for StatTable {
170 fn from(value: &PartialStatTable) -> Self {
171 Self {
172 hp: *value.get(&Stat::HP).unwrap_or(&0) as u16,
173 atk: *value.get(&Stat::Atk).unwrap_or(&0) as u16,
174 def: *value.get(&Stat::Def).unwrap_or(&0) as u16,
175 spa: *value.get(&Stat::SpAtk).unwrap_or(&0) as u16,
176 spd: *value.get(&Stat::SpDef).unwrap_or(&0) as u16,
177 spe: *value.get(&Stat::Spe).unwrap_or(&0) as u16,
178 }
179 }
180}
181
182impl FromIterator<(Stat, u16)> for StatTable {
183 fn from_iter<T: IntoIterator<Item = (Stat, u16)>>(iter: T) -> Self {
184 let mut out = StatTable::default();
185 for (stat, value) in iter {
186 out.set(stat, value);
187 }
188 out
189 }
190}
191
192impl<'s> IntoIterator for &'s StatTable {
193 type IntoIter = StatTableEntries<'s>;
194 type Item = (Stat, u16);
195 fn into_iter(self) -> Self::IntoIter {
196 self.entries()
197 }
198}
199
200#[cfg(test)]
201mod stat_test {
202 use crate::{
203 mons::Stat,
204 test_util::{
205 test_string_deserialization,
206 test_string_serialization,
207 },
208 };
209
210 #[test]
211 fn serializes_to_string() {
212 test_string_serialization(Stat::HP, "hp");
213 test_string_serialization(Stat::Atk, "atk");
214 test_string_serialization(Stat::Def, "def");
215 test_string_serialization(Stat::SpAtk, "spa");
216 test_string_serialization(Stat::SpDef, "spd");
217 test_string_serialization(Stat::Spe, "spe");
218 }
219
220 #[test]
221 fn deserializes_capitalized() {
222 test_string_deserialization("HP", Stat::HP);
223 test_string_deserialization("Atk", Stat::Atk);
224 test_string_deserialization("Def", Stat::Def);
225 test_string_deserialization("SpAtk", Stat::SpAtk);
226 test_string_deserialization("SpDef", Stat::SpDef);
227 test_string_deserialization("Spe", Stat::Spe);
228 }
229
230 #[test]
231 fn deserializes_full_names() {
232 test_string_deserialization("Attack", Stat::Atk);
233 test_string_deserialization("Defense", Stat::Def);
234 test_string_deserialization("Special Attack", Stat::SpAtk);
235 test_string_deserialization("Sp.Atk", Stat::SpAtk);
236 test_string_deserialization("Special Defense", Stat::SpDef);
237 test_string_deserialization("Sp.Def", Stat::SpDef);
238 test_string_deserialization("Speed", Stat::Spe);
239 }
240}
241
242#[cfg(test)]
243mod stat_table_test {
244 use crate::{
245 PartialStatTable,
246 Stat,
247 StatTable,
248 };
249
250 #[test]
251 fn converts_from_partial_stat_table() {
252 let mut table = PartialStatTable::default();
253 table.insert(Stat::HP, 2);
254 table.insert(Stat::SpDef, 255);
255 let table = StatTable::from(&table);
256 assert_eq!(
257 table,
258 StatTable {
259 hp: 2,
260 atk: 0,
261 def: 0,
262 spa: 0,
263 spd: 255,
264 spe: 0,
265 }
266 )
267 }
268
269 #[test]
270 fn gets_associated_value() {
271 let st = StatTable {
272 hp: 1,
273 atk: 2,
274 def: 3,
275 spa: 4,
276 spd: 5,
277 spe: 6,
278 };
279 assert_eq!(st.get(Stat::HP), 1);
280 assert_eq!(st.get(Stat::Atk), 2);
281 assert_eq!(st.get(Stat::Def), 3);
282 assert_eq!(st.get(Stat::SpAtk), 4);
283 assert_eq!(st.get(Stat::SpDef), 5);
284 assert_eq!(st.get(Stat::Spe), 6);
285 }
286
287 #[test]
288 fn sets_associated_value() {
289 let mut st = StatTable {
290 hp: 1,
291 atk: 2,
292 def: 3,
293 spa: 4,
294 spd: 5,
295 spe: 6,
296 };
297 st.set(Stat::HP, 2);
298 st.set(Stat::Atk, 4);
299 st.set(Stat::Def, 6);
300 st.set(Stat::SpAtk, 8);
301 st.set(Stat::SpDef, 10);
302 st.set(Stat::Spe, 12);
303 assert_eq!(st.get(Stat::HP), 2);
304 assert_eq!(st.get(Stat::Atk), 4);
305 assert_eq!(st.get(Stat::Def), 6);
306 assert_eq!(st.get(Stat::SpAtk), 8);
307 assert_eq!(st.get(Stat::SpDef), 10);
308 assert_eq!(st.get(Stat::Spe), 12);
309 }
310
311 #[test]
312 fn sums() {
313 let st = StatTable {
314 hp: 100,
315 atk: 120,
316 def: 120,
317 spa: 150,
318 spd: 100,
319 spe: 90,
320 };
321 assert_eq!(st.sum(), 680);
322 }
323
324 #[test]
325 fn values_iterates_over_all_values() {
326 let st = StatTable {
327 hp: 100,
328 atk: 120,
329 def: 120,
330 spa: 150,
331 spd: 100,
332 spe: 90,
333 };
334 assert!(st.values().all(|val| val < 255));
335 assert_eq!(st.values().sum::<u16>(), 680);
336 }
337
338 #[test]
339 fn from_iter_constructs_table() {
340 let st = StatTable::from_iter([
341 (Stat::HP, 108),
342 (Stat::Atk, 130),
343 (Stat::Def, 95),
344 (Stat::SpAtk, 80),
345 (Stat::SpDef, 85),
346 (Stat::Spe, 102),
347 ]);
348 assert_eq!(
349 st,
350 StatTable {
351 hp: 108,
352 atk: 130,
353 def: 95,
354 spa: 80,
355 spd: 85,
356 spe: 102,
357 }
358 )
359 }
360}