1use serde_string_enum::{
2 DeserializeLabeledStringEnum,
3 SerializeLabeledStringEnum,
4};
5
6#[derive(
16 Debug,
17 Default,
18 Clone,
19 Copy,
20 PartialEq,
21 Eq,
22 Hash,
23 SerializeLabeledStringEnum,
24 DeserializeLabeledStringEnum,
25)]
26pub enum MoveTarget {
27 #[string = "AdjacentAlly"]
29 AdjacentAlly,
30 #[string = "AdjacentAllyOrUser"]
32 AdjacentAllyOrUser,
33 #[string = "AdjacentFoe"]
35 AdjacentFoe,
36 #[string = "All"]
38 All,
39 #[string = "AllAdjacent"]
41 AllAdjacent,
42 #[string = "AllAdjacentFoes"]
46 AllAdjacentFoes,
47 #[string = "Allies"]
49 Allies,
50 #[string = "AllySide"]
52 AllySide,
53 #[string = "AllyTeam"]
55 AllyTeam,
56 #[string = "Any"]
58 Any,
59 #[string = "Field"]
61 Field,
62 #[string = "FoeSide"]
64 FoeSide,
65 #[string = "Normal"]
69 #[default]
70 Normal,
71 #[string = "RandomNormal"]
73 RandomNormal,
74 #[string = "Scripted"]
76 Scripted,
77 #[string = "User"]
79 User,
80}
81
82impl MoveTarget {
83 pub fn choosable(&self) -> bool {
85 match self {
86 Self::Normal
87 | Self::Any
88 | Self::AdjacentAlly
89 | Self::AdjacentAllyOrUser
90 | Self::AdjacentFoe => true,
91 _ => false,
92 }
93 }
94
95 pub fn requires_target(&self) -> bool {
97 match self {
98 Self::All
99 | Self::AllAdjacent
100 | Self::AllAdjacentFoes
101 | Self::Allies
102 | Self::AllySide
103 | Self::AllyTeam
104 | Self::Field
105 | Self::FoeSide
106 | Self::Scripted => false,
107 _ => true,
108 }
109 }
110
111 pub fn has_single_target(&self) -> bool {
113 match self {
114 Self::All
115 | Self::AllAdjacent
116 | Self::AllAdjacentFoes
117 | Self::Allies
118 | Self::AllySide
119 | Self::AllyTeam
120 | Self::Field
121 | Self::FoeSide => false,
122 _ => true,
123 }
124 }
125
126 pub fn affects_mons_directly(&self) -> bool {
128 match self {
129 Self::AllySide | Self::AllyTeam | Self::Field | Self::FoeSide => false,
130 _ => true,
131 }
132 }
133
134 pub fn can_target_user(&self) -> bool {
136 match self {
137 Self::User
138 | Self::All
139 | Self::Allies
140 | Self::AllySide
141 | Self::AllyTeam
142 | Self::AdjacentAllyOrUser => true,
143 _ => false,
144 }
145 }
146
147 pub fn can_target_foes(&self) -> bool {
149 match self {
150 Self::AdjacentAlly
151 | Self::AdjacentAllyOrUser
152 | Self::Allies
153 | Self::AllySide
154 | Self::AllyTeam => false,
155 _ => true,
156 }
157 }
158
159 pub fn is_adjacent_only(&self) -> bool {
161 match self {
162 Self::AdjacentAlly
163 | Self::AdjacentAllyOrUser
164 | Self::AdjacentFoe
165 | Self::AllAdjacent
166 | Self::AllAdjacentFoes
167 | Self::Normal
168 | Self::RandomNormal => true,
169 _ => false,
170 }
171 }
172
173 pub fn is_random(&self) -> bool {
175 match self {
176 Self::RandomNormal => true,
177 _ => false,
178 }
179 }
180
181 pub fn valid_target(&self, relative_target: isize, adjacency_reach: u8) -> bool {
183 match self {
184 Self::AdjacentAlly
185 | Self::AdjacentAllyOrUser
186 | Self::AdjacentFoe
187 | Self::Any
188 | Self::Normal
189 | Self::RandomNormal
190 | Self::Scripted
191 | Self::User => self.is_affected(relative_target, adjacency_reach),
192 _ => false,
193 }
194 }
195
196 pub fn is_affected(&self, relative_target: isize, adjacency_reach: u8) -> bool {
198 let is_self = relative_target == 0;
199 let is_foe = relative_target > 0;
200 let is_adjacent = if relative_target > 0 {
201 relative_target <= adjacency_reach as isize
203 } else {
204 relative_target == -(adjacency_reach as isize) + 1
206 };
207
208 match self {
209 Self::AdjacentAlly => is_adjacent && !is_foe,
210 Self::AdjacentAllyOrUser => (is_adjacent && !is_foe) || is_self,
211 Self::AdjacentFoe | Self::AllAdjacentFoes => is_adjacent && is_foe,
212 Self::All => true,
213 Self::AllAdjacent => is_adjacent,
214 Self::Allies => !is_foe && !is_self,
215 Self::AllySide | Self::AllyTeam => !is_foe,
216 Self::Any => !is_self,
217 Self::Field => true,
218 Self::FoeSide => is_foe,
219 Self::Normal | Self::RandomNormal | Self::Scripted => is_adjacent,
220 Self::User => is_self,
221 }
222 }
223}
224
225#[cfg(test)]
226mod move_target_test {
227 use crate::{
228 moves::MoveTarget,
229 test_util::{
230 test_string_deserialization,
231 test_string_serialization,
232 },
233 };
234
235 #[test]
236 fn serializes_to_string() {
237 test_string_serialization(MoveTarget::AdjacentAlly, "AdjacentAlly");
238 test_string_serialization(MoveTarget::AllAdjacentFoes, "AllAdjacentFoes");
239 test_string_serialization(MoveTarget::RandomNormal, "RandomNormal");
240 }
241
242 #[test]
243 fn deserializes_lowercase() {
244 test_string_deserialization("normal", MoveTarget::Normal);
245 test_string_deserialization("allyTeam", MoveTarget::AllyTeam);
246 test_string_deserialization("foeside", MoveTarget::FoeSide);
247 }
248
249 #[test]
250 fn choosable() {
251 assert!(MoveTarget::Normal.choosable());
252 assert!(MoveTarget::Any.choosable());
253 assert!(MoveTarget::AdjacentAlly.choosable());
254 assert!(MoveTarget::AdjacentAllyOrUser.choosable());
255 assert!(MoveTarget::AdjacentFoe.choosable());
256 assert!(!MoveTarget::RandomNormal.choosable());
257 assert!(!MoveTarget::All.choosable());
258 assert!(!MoveTarget::AllAdjacentFoes.choosable());
259 }
260
261 #[test]
262 fn valid_target_any_adjacent() {
263 assert!(MoveTarget::RandomNormal.valid_target(1, 2));
264 assert!(MoveTarget::Scripted.valid_target(1, 2));
265 assert!(MoveTarget::Normal.valid_target(1, 2));
266 assert!(MoveTarget::RandomNormal.valid_target(2, 2));
267 assert!(MoveTarget::Scripted.valid_target(2, 2));
268 assert!(MoveTarget::Normal.valid_target(2, 2));
269 assert!(MoveTarget::RandomNormal.valid_target(-1, 2));
270 assert!(MoveTarget::Scripted.valid_target(-1, 2));
271 assert!(MoveTarget::Normal.valid_target(-1, 2));
272
273 assert!(!MoveTarget::Normal.valid_target(0, 2));
274 assert!(!MoveTarget::Normal.valid_target(3, 2));
275 assert!(!MoveTarget::Normal.valid_target(-2, 2));
276
277 assert!(MoveTarget::Normal.valid_target(3, 3));
278 assert!(MoveTarget::Normal.valid_target(-2, 3));
279 assert!(!MoveTarget::Normal.valid_target(4, 3));
280 assert!(!MoveTarget::Normal.valid_target(-3, 3));
281 }
282
283 #[test]
284 fn valid_target_adjacent_ally() {
285 assert!(MoveTarget::AdjacentAlly.valid_target(-1, 2));
286
287 assert!(!MoveTarget::AdjacentAlly.valid_target(0, 2));
288 assert!(!MoveTarget::AdjacentAlly.valid_target(1, 2));
289 assert!(!MoveTarget::AdjacentAlly.valid_target(2, 2));
290 assert!(!MoveTarget::AdjacentAlly.valid_target(3, 2));
291 assert!(!MoveTarget::AdjacentAlly.valid_target(-2, 2));
292
293 assert!(MoveTarget::AdjacentAlly.valid_target(-2, 3));
294 assert!(!MoveTarget::AdjacentAlly.valid_target(-3, 3));
295 }
296
297 #[test]
298 fn valid_target_adjacent_ally_or_user() {
299 assert!(MoveTarget::AdjacentAllyOrUser.valid_target(-1, 2));
300 assert!(MoveTarget::AdjacentAllyOrUser.valid_target(0, 2));
301
302 assert!(!MoveTarget::AdjacentAllyOrUser.valid_target(1, 2));
303 assert!(!MoveTarget::AdjacentAllyOrUser.valid_target(2, 2));
304 assert!(!MoveTarget::AdjacentAllyOrUser.valid_target(3, 2));
305 assert!(!MoveTarget::AdjacentAllyOrUser.valid_target(-2, 2));
306
307 assert!(MoveTarget::AdjacentAllyOrUser.valid_target(-2, 3));
308 assert!(!MoveTarget::AdjacentAllyOrUser.valid_target(-3, 3));
309 }
310
311 #[test]
312 fn valid_target_adjacent_foe() {
313 assert!(MoveTarget::AdjacentFoe.valid_target(1, 2));
314 assert!(MoveTarget::AdjacentFoe.valid_target(2, 2));
315
316 assert!(!MoveTarget::AdjacentFoe.valid_target(0, 2));
317 assert!(!MoveTarget::AdjacentFoe.valid_target(3, 2));
318 assert!(!MoveTarget::AdjacentFoe.valid_target(-1, 2));
319 assert!(!MoveTarget::AdjacentFoe.valid_target(-2, 2));
320
321 assert!(MoveTarget::AdjacentFoe.valid_target(3, 3));
322 assert!(!MoveTarget::AdjacentFoe.valid_target(-2, 3));
323 assert!(!MoveTarget::AdjacentFoe.valid_target(4, 3));
324 assert!(!MoveTarget::AdjacentFoe.valid_target(-3, 3));
325 }
326
327 #[test]
328 fn valid_target_any_but_user() {
329 assert!(MoveTarget::Any.valid_target(1, 2));
330 assert!(MoveTarget::Any.valid_target(2, 2));
331 assert!(MoveTarget::Any.valid_target(3, 2));
332 assert!(MoveTarget::Any.valid_target(-1, 2));
333 assert!(MoveTarget::Any.valid_target(-2, 2));
334
335 assert!(!MoveTarget::Any.valid_target(0, 2));
336 }
337
338 #[test]
339 fn valid_target_user() {
340 assert!(MoveTarget::User.valid_target(0, 2));
341
342 assert!(!MoveTarget::User.valid_target(1, 2));
343 assert!(!MoveTarget::User.valid_target(2, 2));
344 assert!(!MoveTarget::User.valid_target(3, 2));
345 assert!(!MoveTarget::User.valid_target(-1, 2));
346 assert!(!MoveTarget::User.valid_target(-2, 2));
347 }
348}