1use crate::unit::UnitType;
2use serde::de::Error;
3use serde::{Deserialize, Serialize};
4use std::collections::BTreeMap;
5
6#[derive(Debug, PartialEq)]
8pub struct UnitConfig {
9 pub level1: Vec<UnitDef>,
10 pub level2: Vec<UnitDef>,
11 pub level3: Vec<UnitDef>,
12}
13
14impl UnitConfig {
15 pub fn get_unit<'a>(&'a self, unit_type: UnitType) -> Option<&'a UnitDef> {
16 let find = move |vec: &'a [UnitDef]| -> Option<&'a UnitDef> {
17 vec.iter().find(|unit| unit.unit_type == unit_type)
18 };
19 find(&self.level1).or_else(|| find(&self.level2).or_else(|| find(&self.level3)))
20 }
21}
22impl UnitDef {
23 pub fn special(&self, count: u64) -> Option<&Special> {
28 self.specials
29 .range(0..=count)
30 .rev()
31 .next()
32 .map(|(_, special)| special)
33 }
34}
35
36#[derive(Debug, Clone, PartialEq)]
38pub struct UnitDef {
39 pub unit_type: UnitType,
40 pub health: u64,
41 pub attack: u64,
42 pub armor: u64,
43 pub attack_range: u64,
44
45 pub specials: BTreeMap<u64, Special>,
49
50 pub multiple_moves_threshold: u64,
53
54 pub infinite_range_threshold: u64,
57
58 pub unobstructed_sight_threshold: u64,
61}
62
63fn verify_units(units: &[json::UnitJson]) -> Vec<UnitDef> {
64 units
65 .iter()
66 .map(|unit| UnitDef {
67 unit_type: unit.unit_type,
68 health: unit.stats.health,
69 attack: unit.stats.attack,
70 armor: unit.stats.armor,
71 attack_range: unit.stats.attackRange,
72 specials: unit
73 .special
74 .iter()
75 .map(|(n, special)| {
76 let special = Special {
77 health: special.health,
78 attack: special.attack,
79 armor: special.armor,
80 attack_range: special.attackRange,
81 targets: special.targets,
82 bonus_damage: special.bonusDamage,
83 healing: special.healing,
84 };
85 (*n, special)
86 })
87 .collect(),
88 multiple_moves_threshold: unit.multipleMovesThreshold.unwrap_or(0),
89 infinite_range_threshold: unit.infiniteRangeThreshold.unwrap_or(0),
90 unobstructed_sight_threshold: unit.unobstructedSightThreshold.unwrap_or(0),
91 })
92 .collect()
93}
94
95impl<'de> Deserialize<'de> for UnitConfig {
96 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
97 where
98 D: serde::Deserializer<'de>,
99 {
100 let json = json::UnitConfigJson::deserialize(deserializer)?;
101
102 let conf = UnitConfig {
103 level1: verify_units(&json.level1),
104 level2: verify_units(&json.level2),
105 level3: verify_units(&json.level3),
106 };
107 if conf.level1.is_empty() {
108 return Err(Error::custom("no level1 units"));
109 }
110 if conf.level2.is_empty() {
111 return Err(Error::custom("no level2 units"));
112 }
113 if conf.level3.is_empty() {
114 return Err(Error::custom("no level3 units"));
115 }
116 Ok(conf)
117 }
118}
119
120fn units_to_json(units: &[UnitDef], level3: bool) -> Vec<json::UnitJson> {
121 units
122 .iter()
123 .map(|unit| json::UnitJson {
124 unit_type: unit.unit_type,
125 stats: json::StatsJson {
126 health: unit.health,
127 attack: unit.attack,
128 armor: unit.armor,
129 attackRange: unit.attack_range,
130 },
131 special: unit
132 .specials
133 .iter()
134 .map(|(n, special)| {
135 (
136 *n,
137 json::SpecialJson {
138 health: special.health,
139 attack: special.attack,
140 armor: special.armor,
141 attackRange: special.attack_range,
142 targets: special.targets,
143 bonusDamage: special.bonus_damage,
144 healing: special.healing,
145 },
146 )
147 })
148 .collect(),
149 multipleMovesThreshold: if level3 {
150 Some(unit.multiple_moves_threshold)
151 } else {
152 None
153 },
154 infiniteRangeThreshold: if level3 {
155 Some(unit.infinite_range_threshold)
156 } else {
157 None
158 },
159 unobstructedSightThreshold: if level3 {
160 Some(unit.unobstructed_sight_threshold)
161 } else {
162 None
163 },
164 })
165 .collect()
166}
167
168impl Serialize for UnitConfig {
169 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
170 where
171 S: serde::Serializer,
172 {
173 json::UnitConfigJson {
174 level1: units_to_json(&self.level1, false),
175 level2: units_to_json(&self.level2, false),
176 level3: units_to_json(&self.level3, true),
177 }
178 .serialize(serializer)
179 }
180}
181
182#[derive(Debug, Clone, PartialEq)]
184pub struct Special {
185 pub health: u64,
186 pub attack: u64,
187 pub armor: u64,
188 pub attack_range: u64,
189 pub targets: u64,
190 pub bonus_damage: u64,
191 pub healing: u64,
192}
193
194#[allow(non_snake_case)]
195mod json {
196 use super::*;
197
198 #[derive(Serialize, Deserialize)]
199 pub struct UnitConfigJson {
200 pub level1: Vec<UnitJson>,
201 pub level2: Vec<UnitJson>,
202 pub level3: Vec<UnitJson>,
203 }
204
205 #[derive(Serialize, Deserialize)]
206 pub struct UnitJson {
207 #[serde(rename = "type")]
208 pub unit_type: UnitType,
209 pub stats: StatsJson,
210 pub special: BTreeMap<u64, SpecialJson>,
211 pub multipleMovesThreshold: Option<u64>,
212 pub infiniteRangeThreshold: Option<u64>,
213 pub unobstructedSightThreshold: Option<u64>,
214 }
215
216 #[derive(Serialize, Deserialize)]
217 pub struct StatsJson {
218 pub health: u64,
219 pub attack: u64,
220 pub armor: u64,
221 pub attackRange: u64,
222 }
223
224 #[derive(Serialize, Deserialize)]
225 pub struct SpecialJson {
226 pub health: u64,
227 pub attack: u64,
228 pub armor: u64,
229 pub attackRange: u64,
230 pub targets: u64,
231 pub bonusDamage: u64,
232 pub healing: u64,
233 }
234}