1use crate::abilities::Abilities;
2use crate::choices::{Choice, Choices, MoveCategory, MOVES};
3use crate::define_enum_with_from_str;
4use crate::instruction::{
5 BoostInstruction, ChangeSideConditionInstruction, ChangeStatInstruction, ChangeType,
6 EnableMoveInstruction, Instruction, RemoveVolatileStatusInstruction, StateInstructions,
7};
8use crate::items::Items;
9use crate::pokemon::PokemonName;
10use core::panic;
11use std::collections::HashSet;
12use std::ops::{Index, IndexMut};
13use std::str::FromStr;
14
15fn common_pkmn_stat_calc(stat: u16, ev: u16, level: u16) -> u16 {
16 ((2 * stat + 31 + (ev / 4)) * level) / 100
18}
19
20fn multiply_boost(boost_num: i8, stat_value: i16) -> i16 {
21 match boost_num {
22 -6 => stat_value * 2 / 8,
23 -5 => stat_value * 2 / 7,
24 -4 => stat_value * 2 / 6,
25 -3 => stat_value * 2 / 5,
26 -2 => stat_value * 2 / 4,
27 -1 => stat_value * 2 / 3,
28 0 => stat_value,
29 1 => stat_value * 3 / 2,
30 2 => stat_value * 4 / 2,
31 3 => stat_value * 5 / 2,
32 4 => stat_value * 6 / 2,
33 5 => stat_value * 7 / 2,
34 6 => stat_value * 8 / 2,
35 _ => panic!("Invalid boost number"),
36 }
37}
38
39#[derive(Debug, Clone)]
40pub struct DamageDealt {
41 pub damage: i16,
42 pub move_category: MoveCategory,
43 pub hit_substitute: bool,
44}
45
46impl Default for DamageDealt {
47 fn default() -> DamageDealt {
48 DamageDealt {
49 damage: 0,
50 move_category: MoveCategory::Physical,
51 hit_substitute: false,
52 }
53 }
54}
55
56#[derive(Debug, PartialEq, Eq, Copy, Clone)]
57pub enum LastUsedMove {
58 Move(PokemonMoveIndex),
59 Switch(PokemonIndex),
60 None,
61}
62
63#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
64pub enum MoveChoice {
65 MoveTera(PokemonMoveIndex),
66 Move(PokemonMoveIndex),
67 Switch(PokemonIndex),
68 None,
69}
70
71impl MoveChoice {
72 pub fn to_string(&self, side: &Side) -> String {
73 match self {
74 MoveChoice::MoveTera(index) => {
75 format!("{}-tera", side.get_active_immutable().moves[index].id).to_lowercase()
76 }
77 MoveChoice::Move(index) => {
78 format!("{}", side.get_active_immutable().moves[index].id).to_lowercase()
79 }
80 MoveChoice::Switch(index) => {
81 format!("switch {}", side.pokemon[*index].id).to_lowercase()
82 }
83 MoveChoice::None => "No Move".to_string(),
84 }
85 }
86}
87
88define_enum_with_from_str! {
89 #[repr(u8)]
90 #[derive(Debug, PartialEq, Copy, Clone)]
91 SideMovesFirst {
92 SideOne,
93 SideTwo,
94 SpeedTie
95 }
96}
97
98define_enum_with_from_str! {
99 #[repr(u8)]
100 #[derive(Debug, PartialEq, Clone)]
101 PokemonNature {
102 HARDY,
103 LONELY,
104 ADAMANT,
105 NAUGHTY,
106 BRAVE,
107 BOLD,
108 DOCILE,
109 IMPISH,
110 LAX,
111 RELAXED,
112 MODEST,
113 MILD,
114 BASHFUL,
115 RASH,
116 QUIET,
117 CALM,
118 GENTLE,
119 CAREFUL,
120 QUIRKY,
121 SASSY,
122 TIMID,
123 HASTY,
124 JOLLY,
125 NAIVE,
126 SERIOUS
127 }
128}
129
130define_enum_with_from_str! {
131 #[repr(u8)]
132 #[derive(Debug, PartialEq, Copy, Clone)]
133 PokemonStatus {
134 NONE,
135 BURN,
136 SLEEP,
137 FREEZE,
138 PARALYZE,
139 POISON,
140 TOXIC,
141 }
142}
143
144define_enum_with_from_str! {
145 #[repr(u8)]
146 #[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
147 PokemonVolatileStatus {
148 NONE,
149 AQUARING,
150 ATTRACT,
151 AUTOTOMIZE,
152 BANEFULBUNKER,
153 BIDE,
154 BOUNCE,
155 BURNINGBULWARK,
156 CHARGE,
157 CONFUSION,
158 CURSE,
159 DEFENSECURL,
160 DESTINYBOND,
161 DIG,
162 DISABLE,
163 DIVE,
164 ELECTRIFY,
165 ELECTROSHOT,
166 EMBARGO,
167 ENCORE,
168 ENDURE,
169 FLASHFIRE,
170 FLINCH,
171 FLY,
172 FOCUSENERGY,
173 FOLLOWME,
174 FORESIGHT,
175 FREEZESHOCK,
176 GASTROACID,
177 GEOMANCY,
178 GLAIVERUSH,
179 GRUDGE,
180 HEALBLOCK,
181 HELPINGHAND,
182 ICEBURN,
183 IMPRISON,
184 INGRAIN,
185 KINGSSHIELD,
186 LASERFOCUS,
187 LEECHSEED,
188 LIGHTSCREEN,
189 LOCKEDMOVE,
190 MAGICCOAT,
191 MAGNETRISE,
192 MAXGUARD,
193 METEORBEAM,
194 MINIMIZE,
195 MIRACLEEYE,
196 MUSTRECHARGE,
197 NIGHTMARE,
198 NORETREAT,
199 OCTOLOCK,
200 PARTIALLYTRAPPED,
201 PERISH4,
202 PERISH3,
203 PERISH2,
204 PERISH1,
205 PHANTOMFORCE,
206 POWDER,
207 POWERSHIFT,
208 POWERTRICK,
209 PROTECT,
210 PROTOSYNTHESISATK,
211 PROTOSYNTHESISDEF,
212 PROTOSYNTHESISSPA,
213 PROTOSYNTHESISSPD,
214 PROTOSYNTHESISSPE,
215 QUARKDRIVEATK,
216 QUARKDRIVEDEF,
217 QUARKDRIVESPA,
218 QUARKDRIVESPD,
219 QUARKDRIVESPE,
220 RAGE,
221 RAGEPOWDER,
222 RAZORWIND,
223 REFLECT,
224 ROOST,
225 SALTCURE,
226 SHADOWFORCE,
227 SKULLBASH,
228 SKYATTACK,
229 SKYDROP,
230 SILKTRAP,
231 SLOWSTART,
232 SMACKDOWN,
233 SNATCH,
234 SOLARBEAM,
235 SOLARBLADE,
236 SPARKLINGARIA,
237 SPIKYSHIELD,
238 SPOTLIGHT,
239 STOCKPILE,
240 SUBSTITUTE,
241 SYRUPBOMB,
242 TARSHOT,
243 TAUNT,
244 TELEKINESIS,
245 THROATCHOP,
246 TRUANT,
247 TORMENT,
248 TYPECHANGE,
249 UNBURDEN,
250 UPROAR,
251 YAWN,
252 YAWNSLEEPTHISTURN,
253 },
254 default = NONE
255}
256
257#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]
258pub enum PokemonSideCondition {
259 AuroraVeil,
260 CraftyShield,
261 HealingWish,
262 LightScreen,
263 LuckyChant,
264 LunarDance,
265 MatBlock,
266 Mist,
267 Protect,
268 QuickGuard,
269 Reflect,
270 Safeguard,
271 Spikes,
272 Stealthrock,
273 StickyWeb,
274 Tailwind,
275 ToxicCount,
276 ToxicSpikes,
277 WideGuard,
278}
279
280#[derive(Debug, PartialEq, Copy, Clone)]
281pub enum SideReference {
282 SideOne,
283 SideTwo,
284}
285
286impl SideReference {
287 pub fn get_other_side(&self) -> SideReference {
288 match self {
289 SideReference::SideOne => SideReference::SideTwo,
290 SideReference::SideTwo => SideReference::SideOne,
291 }
292 }
293}
294
295define_enum_with_from_str! {
296 #[repr(u8)]
297 #[derive(Debug, PartialEq, Copy, Clone)]
298 Weather {
299 NONE,
300 SUN,
301 RAIN,
302 SAND,
303 HAIL,
304 SNOW,
305 HARSHSUN,
306 HEAVYRAIN,
307 }
308}
309
310#[derive(Debug, PartialEq, Clone)]
311pub struct StateWeather {
312 pub weather_type: Weather,
313 pub turns_remaining: i8,
314}
315
316define_enum_with_from_str! {
317 #[repr(u8)]
318 #[derive(Debug, PartialEq, Copy, Clone)]
319 Terrain {
320 NONE,
321 ELECTRICTERRAIN,
322 PSYCHICTERRAIN,
323 MISTYTERRAIN,
324 GRASSYTERRAIN,
325 }
326}
327
328#[derive(Debug, PartialEq, Clone)]
329pub struct StateTerrain {
330 pub terrain_type: Terrain,
331 pub turns_remaining: i8,
332}
333
334#[derive(Debug, PartialEq, Clone)]
335pub struct StateTrickRoom {
336 pub active: bool,
337 pub turns_remaining: i8,
338}
339
340define_enum_with_from_str! {
341 #[repr(u8)]
342 #[derive(Debug, Clone, Copy, PartialEq)]
343 PokemonType {
344 NORMAL,
345 FIRE,
346 WATER,
347 ELECTRIC,
348 GRASS,
349 ICE,
350 FIGHTING,
351 POISON,
352 GROUND,
353 FLYING,
354 PSYCHIC,
355 BUG,
356 ROCK,
357 GHOST,
358 DRAGON,
359 DARK,
360 STEEL,
361 FAIRY,
362 STELLAR,
363 TYPELESS,
364 },
365 default = TYPELESS
366}
367
368#[derive(Debug, PartialEq, Clone, Copy)]
369pub enum PokemonBoostableStat {
370 Attack,
371 Defense,
372 SpecialAttack,
373 SpecialDefense,
374 Speed,
375 Evasion,
376 Accuracy,
377}
378
379#[derive(Debug, PartialEq, Clone)]
380pub struct SideConditions {
381 pub aurora_veil: i8,
382 pub crafty_shield: i8,
383 pub healing_wish: i8,
384 pub light_screen: i8,
385 pub lucky_chant: i8,
386 pub lunar_dance: i8,
387 pub mat_block: i8,
388 pub mist: i8,
389 pub protect: i8,
390 pub quick_guard: i8,
391 pub reflect: i8,
392 pub safeguard: i8,
393 pub spikes: i8,
394 pub stealth_rock: i8,
395 pub sticky_web: i8,
396 pub tailwind: i8,
397 pub toxic_count: i8,
398 pub toxic_spikes: i8,
399 pub wide_guard: i8,
400}
401
402impl Default for SideConditions {
403 fn default() -> SideConditions {
404 SideConditions {
405 aurora_veil: 0,
406 crafty_shield: 0,
407 healing_wish: 0,
408 light_screen: 0,
409 lucky_chant: 0,
410 lunar_dance: 0,
411 mat_block: 0,
412 mist: 0,
413 protect: 0,
414 quick_guard: 0,
415 reflect: 0,
416 safeguard: 0,
417 spikes: 0,
418 stealth_rock: 0,
419 sticky_web: 0,
420 tailwind: 0,
421 toxic_count: 0,
422 toxic_spikes: 0,
423 wide_guard: 0,
424 }
425 }
426}
427
428#[derive(Debug, Copy, PartialEq, Clone, Eq, Hash)]
429pub enum PokemonMoveIndex {
430 M0,
431 M1,
432 M2,
433 M3,
434}
435
436#[derive(Debug, Clone)]
437pub struct PokemonMoves {
438 pub m0: Move,
439 pub m1: Move,
440 pub m2: Move,
441 pub m3: Move,
442}
443
444impl Index<&PokemonMoveIndex> for PokemonMoves {
445 type Output = Move;
446
447 fn index(&self, index: &PokemonMoveIndex) -> &Self::Output {
448 match index {
449 PokemonMoveIndex::M0 => &self.m0,
450 PokemonMoveIndex::M1 => &self.m1,
451 PokemonMoveIndex::M2 => &self.m2,
452 PokemonMoveIndex::M3 => &self.m3,
453 }
454 }
455}
456
457impl IndexMut<&PokemonMoveIndex> for PokemonMoves {
458 fn index_mut(&mut self, index: &PokemonMoveIndex) -> &mut Self::Output {
459 match index {
460 PokemonMoveIndex::M0 => &mut self.m0,
461 PokemonMoveIndex::M1 => &mut self.m1,
462 PokemonMoveIndex::M2 => &mut self.m2,
463 PokemonMoveIndex::M3 => &mut self.m3,
464 }
465 }
466}
467
468pub struct PokemonMoveIterator<'a> {
469 pub pokemon_move: &'a PokemonMoves,
470 pub pokemon_move_index: PokemonMoveIndex,
471 pub index: usize,
472}
473
474impl<'a> Iterator for PokemonMoveIterator<'a> {
475 type Item = &'a Move;
476
477 fn next(&mut self) -> Option<Self::Item> {
478 match self.index {
479 0 => {
480 self.index += 1;
481 self.pokemon_move_index = PokemonMoveIndex::M0;
482 Some(&self.pokemon_move.m0)
483 }
484 1 => {
485 self.index += 1;
486 self.pokemon_move_index = PokemonMoveIndex::M1;
487 Some(&self.pokemon_move.m1)
488 }
489 2 => {
490 self.index += 1;
491 self.pokemon_move_index = PokemonMoveIndex::M2;
492 Some(&self.pokemon_move.m2)
493 }
494 3 => {
495 self.index += 1;
496 self.pokemon_move_index = PokemonMoveIndex::M3;
497 Some(&self.pokemon_move.m3)
498 }
499 _ => None,
500 }
501 }
502}
503
504impl<'a> IntoIterator for &'a PokemonMoves {
505 type Item = &'a Move;
506 type IntoIter = PokemonMoveIterator<'a>;
507
508 fn into_iter(self) -> Self::IntoIter {
509 PokemonMoveIterator {
510 pokemon_move: &self,
511 pokemon_move_index: PokemonMoveIndex::M0,
512 index: 0,
513 }
514 }
515}
516
517#[derive(Debug, Clone)]
518pub struct Move {
519 pub id: Choices,
520 pub disabled: bool,
521 pub pp: i8,
522 pub choice: Choice,
523}
524
525impl Default for Move {
526 fn default() -> Move {
527 Move {
528 id: Choices::NONE,
529 disabled: false,
530 pp: 32,
531 choice: Choice::default(),
532 }
533 }
534}
535
536#[derive(Debug, Clone)]
537pub struct Pokemon {
538 pub id: PokemonName,
539 pub level: i8,
540 pub types: (PokemonType, PokemonType),
541 pub base_types: (PokemonType, PokemonType),
542 pub hp: i16,
543 pub maxhp: i16,
544 pub ability: Abilities,
545 pub base_ability: Abilities,
546 pub item: Items,
547 pub nature: PokemonNature,
548 pub evs: (u8, u8, u8, u8, u8, u8),
549 pub attack: i16,
550 pub defense: i16,
551 pub special_attack: i16,
552 pub special_defense: i16,
553 pub speed: i16,
554 pub status: PokemonStatus,
555 pub rest_turns: i8,
556 pub sleep_turns: i8,
557 pub weight_kg: f32,
558 pub terastallized: bool,
559 pub tera_type: PokemonType,
560 pub moves: PokemonMoves,
561}
562
563impl Pokemon {
564 pub fn recalculate_stats(
565 &mut self,
566 side_ref: &SideReference,
567 instructions: &mut StateInstructions,
568 ) {
569 let stats = self.calculate_stats_from_base_stats();
571 if stats.1 != self.attack {
572 let ins = Instruction::ChangeAttack(ChangeStatInstruction {
573 side_ref: *side_ref,
574 amount: stats.1 - self.attack,
575 });
576 self.attack = stats.1;
577 instructions.instruction_list.push(ins);
578 }
579 if stats.2 != self.defense {
580 let ins = Instruction::ChangeDefense(ChangeStatInstruction {
581 side_ref: *side_ref,
582 amount: stats.2 - self.defense,
583 });
584 self.defense = stats.2;
585 instructions.instruction_list.push(ins);
586 }
587 if stats.3 != self.special_attack {
588 let ins = Instruction::ChangeSpecialAttack(ChangeStatInstruction {
589 side_ref: *side_ref,
590 amount: stats.3 - self.special_attack,
591 });
592 self.special_attack = stats.3;
593 instructions.instruction_list.push(ins);
594 }
595 if stats.4 != self.special_defense {
596 let ins = Instruction::ChangeSpecialDefense(ChangeStatInstruction {
597 side_ref: *side_ref,
598 amount: stats.4 - self.special_defense,
599 });
600 self.special_defense = stats.4;
601 instructions.instruction_list.push(ins);
602 }
603 if stats.5 != self.speed {
604 let ins = Instruction::ChangeSpeed(ChangeStatInstruction {
605 side_ref: *side_ref,
606 amount: stats.5 - self.speed,
607 });
608 self.speed = stats.5;
609 instructions.instruction_list.push(ins);
610 }
611 }
612 pub fn calculate_stats_from_base_stats(&self) -> (i16, i16, i16, i16, i16, i16) {
613 let base_stats = self.id.base_stats();
614 (
615 (common_pkmn_stat_calc(base_stats.0 as u16, self.evs.0 as u16, self.level as u16)
616 + self.level as u16
617 + 10) as i16,
618 (common_pkmn_stat_calc(base_stats.1 as u16, self.evs.1 as u16, self.level as u16) + 5)
619 as i16,
620 (common_pkmn_stat_calc(base_stats.2 as u16, self.evs.2 as u16, self.level as u16) + 5)
621 as i16,
622 (common_pkmn_stat_calc(base_stats.3 as u16, self.evs.3 as u16, self.level as u16) + 5)
623 as i16,
624 (common_pkmn_stat_calc(base_stats.4 as u16, self.evs.4 as u16, self.level as u16) + 5)
625 as i16,
626 (common_pkmn_stat_calc(base_stats.5 as u16, self.evs.5 as u16, self.level as u16) + 5)
627 as i16,
628 )
629 }
630 pub fn get_stat_from_boostable_stat(&self, stat: PokemonBoostableStat) -> i16 {
631 match stat {
632 PokemonBoostableStat::Attack => self.attack,
633 PokemonBoostableStat::Defense => self.defense,
634 PokemonBoostableStat::SpecialAttack => self.special_attack,
635 PokemonBoostableStat::SpecialDefense => self.special_defense,
636 PokemonBoostableStat::Speed => self.speed,
637 _ => panic!("Not implemented"),
638 }
639 }
640 pub fn get_sleep_talk_choices(&self) -> Vec<Choice> {
641 let mut vec = Vec::with_capacity(4);
642 for p in self.moves.into_iter() {
643 if p.id != Choices::SLEEPTALK && p.id != Choices::NONE {
644 vec.push(p.choice.clone());
645 }
646 }
647 vec
648 }
649 pub fn replace_move(&mut self, move_index: PokemonMoveIndex, new_move_name: Choices) {
650 self.moves[&move_index].choice = MOVES.get(&new_move_name).unwrap().to_owned();
651 self.moves[&move_index].id = new_move_name;
652 }
653
654 pub fn add_available_moves(
655 &self,
656 vec: &mut Vec<MoveChoice>,
657 last_used_move: &LastUsedMove,
658 encored: bool,
659 can_tera: bool,
660 ) {
661 let mut iter = self.moves.into_iter();
662 while let Some(p) = iter.next() {
663 if !p.disabled && p.pp > 0 {
664 match last_used_move {
665 LastUsedMove::Move(last_used_move) => {
666 if encored && last_used_move != &iter.pokemon_move_index {
667 continue;
668 } else if (self.moves[last_used_move].id == Choices::BLOODMOON
669 || self.moves[last_used_move].id == Choices::GIGATONHAMMER)
670 && &iter.pokemon_move_index == last_used_move
671 {
672 continue;
673 }
674 }
675 _ => {
676 }
680 }
681 if self.item == Items::ASSAULTVEST
682 && self.moves[&iter.pokemon_move_index].choice.category == MoveCategory::Status
683 {
684 continue;
685 }
686 vec.push(MoveChoice::Move(iter.pokemon_move_index));
687 if can_tera {
688 vec.push(MoveChoice::MoveTera(iter.pokemon_move_index));
689 }
690 }
691 }
692 }
693
694 pub fn add_move_from_choice(&self, vec: &mut Vec<MoveChoice>, choice: Choices) {
695 let mut iter = self.moves.into_iter();
696 while let Some(p) = iter.next() {
697 if p.id == choice {
698 vec.push(MoveChoice::Move(iter.pokemon_move_index));
699 }
700 }
701 }
702
703 #[cfg(feature = "terastallization")]
704 pub fn has_type(&self, pkmn_type: &PokemonType) -> bool {
705 if self.terastallized {
706 pkmn_type == &self.tera_type
707 } else {
708 pkmn_type == &self.types.0 || pkmn_type == &self.types.1
709 }
710 }
711
712 #[cfg(not(feature = "terastallization"))]
713 pub fn has_type(&self, pkmn_type: &PokemonType) -> bool {
714 pkmn_type == &self.types.0 || pkmn_type == &self.types.1
715 }
716
717 pub fn item_is_permanent(&self) -> bool {
718 match self.item {
719 Items::SPLASHPLATE => self.id == PokemonName::ARCEUSWATER,
720 Items::TOXICPLATE => self.id == PokemonName::ARCEUSPOISON,
721 Items::EARTHPLATE => self.id == PokemonName::ARCEUSGROUND,
722 Items::STONEPLATE => self.id == PokemonName::ARCEUSROCK,
723 Items::INSECTPLATE => self.id == PokemonName::ARCEUSBUG,
724 Items::SPOOKYPLATE => self.id == PokemonName::ARCEUSGHOST,
725 Items::IRONPLATE => self.id == PokemonName::ARCEUSSTEEL,
726 Items::FLAMEPLATE => self.id == PokemonName::ARCEUSFIRE,
727 Items::MEADOWPLATE => self.id == PokemonName::ARCEUSGRASS,
728 Items::ZAPPLATE => self.id == PokemonName::ARCEUSELECTRIC,
729 Items::MINDPLATE => self.id == PokemonName::ARCEUSPSYCHIC,
730 Items::ICICLEPLATE => self.id == PokemonName::ARCEUSICE,
731 Items::DRACOPLATE => self.id == PokemonName::ARCEUSDRAGON,
732 Items::DREADPLATE => self.id == PokemonName::ARCEUSDARK,
733 Items::FISTPLATE => self.id == PokemonName::ARCEUSFIGHTING,
734 Items::BLANKPLATE => self.id == PokemonName::ARCEUS,
735 Items::SKYPLATE => self.id == PokemonName::ARCEUSFLYING,
736 Items::PIXIEPLATE => self.id == PokemonName::ARCEUSFAIRY,
737 Items::BUGMEMORY => self.id == PokemonName::SILVALLYBUG,
738 Items::FIGHTINGMEMORY => self.id == PokemonName::SILVALLYFIGHTING,
739 Items::GHOSTMEMORY => self.id == PokemonName::SILVALLYGHOST,
740 Items::PSYCHICMEMORY => self.id == PokemonName::SILVALLYPSYCHIC,
741 Items::FLYINGMEMORY => self.id == PokemonName::SILVALLYFLYING,
742 Items::STEELMEMORY => self.id == PokemonName::SILVALLYSTEEL,
743 Items::ICEMEMORY => self.id == PokemonName::SILVALLYICE,
744 Items::POISONMEMORY => self.id == PokemonName::SILVALLYPOISON,
745 Items::FIREMEMORY => self.id == PokemonName::SILVALLYFIRE,
746 Items::DRAGONMEMORY => self.id == PokemonName::SILVALLYDRAGON,
747 Items::GROUNDMEMORY => self.id == PokemonName::SILVALLYGROUND,
748 Items::WATERMEMORY => self.id == PokemonName::SILVALLYWATER,
749 Items::DARKMEMORY => self.id == PokemonName::SILVALLYDARK,
750 Items::ROCKMEMORY => self.id == PokemonName::SILVALLYROCK,
751 Items::GRASSMEMORY => self.id == PokemonName::SILVALLYGRASS,
752 Items::FAIRYMEMORY => self.id == PokemonName::SILVALLYFAIRY,
753 Items::ELECTRICMEMORY => self.id == PokemonName::SILVALLYELECTRIC,
754 Items::CORNERSTONEMASK => {
755 self.id == PokemonName::OGERPONCORNERSTONE
756 || self.id == PokemonName::OGERPONCORNERSTONETERA
757 }
758 Items::HEARTHFLAMEMASK => {
759 self.id == PokemonName::OGERPONHEARTHFLAME
760 || self.id == PokemonName::OGERPONHEARTHFLAMETERA
761 }
762 Items::WELLSPRINGMASK => {
763 self.id == PokemonName::OGERPONWELLSPRING
764 || self.id == PokemonName::OGERPONWELLSPRINGTERA
765 }
766 _ => false,
767 }
768 }
769
770 pub fn item_can_be_removed(&self) -> bool {
771 if self.ability == Abilities::STICKYHOLD {
772 return false;
773 }
774 !self.item_is_permanent()
775 }
776
777 pub fn is_grounded(&self) -> bool {
778 if self.item == Items::IRONBALL {
779 return true;
780 }
781 if self.has_type(&PokemonType::FLYING)
782 || self.ability == Abilities::LEVITATE
783 || self.item == Items::AIRBALLOON
784 {
785 return false;
786 }
787 true
788 }
789
790 pub fn volatile_status_can_be_applied(
791 &self,
792 volatile_status: &PokemonVolatileStatus,
793 active_volatiles: &HashSet<PokemonVolatileStatus>,
794 first_move: bool,
795 ) -> bool {
796 if active_volatiles.contains(volatile_status) || self.hp == 0 {
797 return false;
798 }
799 match volatile_status {
800 PokemonVolatileStatus::LEECHSEED => {
801 if self.has_type(&PokemonType::GRASS)
802 || active_volatiles.contains(&PokemonVolatileStatus::SUBSTITUTE)
803 {
804 return false;
805 }
806 true
807 }
808 PokemonVolatileStatus::CONFUSION => {
809 if active_volatiles.contains(&PokemonVolatileStatus::SUBSTITUTE) {
810 return false;
811 }
812 true
813 }
814 PokemonVolatileStatus::SUBSTITUTE => self.hp > self.maxhp / 4,
815 PokemonVolatileStatus::FLINCH => {
816 if !first_move || [Abilities::INNERFOCUS].contains(&self.ability) {
817 return false;
818 }
819 true
820 }
821 PokemonVolatileStatus::PROTECT => first_move,
822 PokemonVolatileStatus::TAUNT
823 | PokemonVolatileStatus::TORMENT
824 | PokemonVolatileStatus::ENCORE
825 | PokemonVolatileStatus::DISABLE
826 | PokemonVolatileStatus::HEALBLOCK
827 | PokemonVolatileStatus::ATTRACT => self.ability != Abilities::AROMAVEIL,
828 PokemonVolatileStatus::YAWN => {
829 !active_volatiles.contains(&PokemonVolatileStatus::YAWNSLEEPTHISTURN)
831 }
832 _ => true,
833 }
834 }
835
836 pub fn immune_to_stats_lowered_by_opponent(
837 &self,
838 stat: &PokemonBoostableStat,
839 volatiles: &HashSet<PokemonVolatileStatus>,
840 ) -> bool {
841 if [
842 Abilities::CLEARBODY,
843 Abilities::WHITESMOKE,
844 Abilities::FULLMETALBODY,
845 ]
846 .contains(&self.ability)
847 || ([Items::CLEARAMULET].contains(&self.item))
848 {
849 return true;
850 }
851
852 if volatiles.contains(&PokemonVolatileStatus::SUBSTITUTE) {
853 return true;
854 }
855
856 if stat == &PokemonBoostableStat::Attack && self.ability == Abilities::HYPERCUTTER {
857 return true;
858 } else if stat == &PokemonBoostableStat::Accuracy && self.ability == Abilities::KEENEYE {
859 return true;
860 }
861
862 false
863 }
864}
865
866impl Default for Pokemon {
867 fn default() -> Pokemon {
868 Pokemon {
869 id: PokemonName::NONE,
870 level: 100,
871 types: (PokemonType::NORMAL, PokemonType::TYPELESS),
872 base_types: (PokemonType::NORMAL, PokemonType::TYPELESS),
873 hp: 100,
874 maxhp: 100,
875 ability: Abilities::NONE,
876 base_ability: Abilities::NONE,
877 item: Items::NONE,
878 nature: PokemonNature::SERIOUS,
879 evs: (85, 85, 85, 85, 85, 85),
880 attack: 100,
881 defense: 100,
882 special_attack: 100,
883 special_defense: 100,
884 speed: 100,
885 status: PokemonStatus::NONE,
886 rest_turns: 0,
887 sleep_turns: 0,
888 weight_kg: 1.0,
889 terastallized: false,
890 tera_type: PokemonType::NORMAL,
891 moves: PokemonMoves {
892 m0: Default::default(),
893 m1: Default::default(),
894 m2: Default::default(),
895 m3: Default::default(),
896 },
897 }
898 }
899}
900
901#[derive(Debug, Copy, PartialEq, Clone, Eq, Hash)]
902pub enum PokemonIndex {
903 P0,
904 P1,
905 P2,
906 P3,
907 P4,
908 P5,
909}
910
911pub fn pokemon_index_iter() -> PokemonIndexIterator {
912 PokemonIndexIterator { index: 0 }
913}
914
915pub struct PokemonIndexIterator {
916 index: usize,
917}
918
919impl Iterator for PokemonIndexIterator {
920 type Item = PokemonIndex;
921
922 fn next(&mut self) -> Option<Self::Item> {
923 match self.index {
924 0 => {
925 self.index += 1;
926 Some(PokemonIndex::P0)
927 }
928 1 => {
929 self.index += 1;
930 Some(PokemonIndex::P1)
931 }
932 2 => {
933 self.index += 1;
934 Some(PokemonIndex::P2)
935 }
936 3 => {
937 self.index += 1;
938 Some(PokemonIndex::P3)
939 }
940 4 => {
941 self.index += 1;
942 Some(PokemonIndex::P4)
943 }
944 5 => {
945 self.index += 1;
946 Some(PokemonIndex::P5)
947 }
948 _ => None,
949 }
950 }
951}
952
953#[derive(Debug, Clone)]
954pub struct SidePokemon {
955 pub p0: Pokemon,
956 pub p1: Pokemon,
957 pub p2: Pokemon,
958 pub p3: Pokemon,
959 pub p4: Pokemon,
960 pub p5: Pokemon,
961}
962
963impl<'a> IntoIterator for &'a SidePokemon {
964 type Item = &'a Pokemon;
965 type IntoIter = SidePokemonIterator<'a>;
966
967 fn into_iter(self) -> Self::IntoIter {
968 SidePokemonIterator {
969 side_pokemon: &self,
970 pokemon_index: PokemonIndex::P0,
971 index: 0,
972 }
973 }
974}
975
976pub struct SidePokemonIterator<'a> {
977 pub side_pokemon: &'a SidePokemon,
978 pub pokemon_index: PokemonIndex,
979 pub index: usize,
980}
981
982impl<'a> Iterator for SidePokemonIterator<'a> {
983 type Item = &'a Pokemon;
984
985 fn next(&mut self) -> Option<Self::Item> {
986 match self.index {
987 0 => {
988 self.index += 1;
989 self.pokemon_index = PokemonIndex::P0;
990 Some(&self.side_pokemon.p0)
991 }
992 1 => {
993 self.index += 1;
994 self.pokemon_index = PokemonIndex::P1;
995 Some(&self.side_pokemon.p1)
996 }
997 2 => {
998 self.index += 1;
999 self.pokemon_index = PokemonIndex::P2;
1000 Some(&self.side_pokemon.p2)
1001 }
1002 3 => {
1003 self.index += 1;
1004 self.pokemon_index = PokemonIndex::P3;
1005 Some(&self.side_pokemon.p3)
1006 }
1007 4 => {
1008 self.index += 1;
1009 self.pokemon_index = PokemonIndex::P4;
1010 Some(&self.side_pokemon.p4)
1011 }
1012 5 => {
1013 self.index += 1;
1014 self.pokemon_index = PokemonIndex::P5;
1015 Some(&self.side_pokemon.p5)
1016 }
1017 _ => None,
1018 }
1019 }
1020}
1021
1022impl Index<PokemonIndex> for SidePokemon {
1023 type Output = Pokemon;
1024
1025 fn index(&self, index: PokemonIndex) -> &Self::Output {
1026 match index {
1027 PokemonIndex::P0 => &self.p0,
1028 PokemonIndex::P1 => &self.p1,
1029 PokemonIndex::P2 => &self.p2,
1030 PokemonIndex::P3 => &self.p3,
1031 PokemonIndex::P4 => &self.p4,
1032 PokemonIndex::P5 => &self.p5,
1033 }
1034 }
1035}
1036
1037impl Index<&PokemonIndex> for SidePokemon {
1038 type Output = Pokemon;
1039
1040 fn index(&self, index: &PokemonIndex) -> &Self::Output {
1041 match index {
1042 PokemonIndex::P0 => &self.p0,
1043 PokemonIndex::P1 => &self.p1,
1044 PokemonIndex::P2 => &self.p2,
1045 PokemonIndex::P3 => &self.p3,
1046 PokemonIndex::P4 => &self.p4,
1047 PokemonIndex::P5 => &self.p5,
1048 }
1049 }
1050}
1051
1052impl IndexMut<PokemonIndex> for SidePokemon {
1053 fn index_mut(&mut self, index: PokemonIndex) -> &mut Self::Output {
1054 match index {
1055 PokemonIndex::P0 => &mut self.p0,
1056 PokemonIndex::P1 => &mut self.p1,
1057 PokemonIndex::P2 => &mut self.p2,
1058 PokemonIndex::P3 => &mut self.p3,
1059 PokemonIndex::P4 => &mut self.p4,
1060 PokemonIndex::P5 => &mut self.p5,
1061 }
1062 }
1063}
1064
1065#[derive(Debug, Clone)]
1066pub struct Side {
1067 pub active_index: PokemonIndex,
1068 pub baton_passing: bool,
1069 pub pokemon: SidePokemon,
1070 pub side_conditions: SideConditions,
1071 pub wish: (i8, i16),
1072 pub future_sight: (i8, PokemonIndex),
1073 pub force_switch: bool,
1074 pub force_trapped: bool,
1075 pub slow_uturn_move: bool,
1076 pub volatile_statuses: HashSet<PokemonVolatileStatus>,
1077 pub substitute_health: i16,
1078 pub attack_boost: i8,
1079 pub defense_boost: i8,
1080 pub special_attack_boost: i8,
1081 pub special_defense_boost: i8,
1082 pub speed_boost: i8,
1083 pub accuracy_boost: i8,
1084 pub evasion_boost: i8,
1085 pub last_used_move: LastUsedMove,
1086 pub damage_dealt: DamageDealt,
1087 pub switch_out_move_second_saved_move: Choices,
1088}
1089
1090impl Side {
1091 pub fn calculate_highest_stat(&self) -> PokemonBoostableStat {
1092 let mut highest_stat = PokemonBoostableStat::Attack;
1093 let mut highest_stat_value = self.calculate_boosted_stat(PokemonBoostableStat::Attack);
1094 for stat in [
1095 PokemonBoostableStat::Defense,
1096 PokemonBoostableStat::SpecialAttack,
1097 PokemonBoostableStat::SpecialDefense,
1098 PokemonBoostableStat::Speed,
1099 ] {
1100 let stat_value = self.calculate_boosted_stat(stat);
1101 if stat_value > highest_stat_value {
1102 highest_stat = stat;
1103 highest_stat_value = stat_value;
1104 }
1105 }
1106 highest_stat
1107 }
1108 pub fn get_boost_from_boost_enum(&self, boost_enum: &PokemonBoostableStat) -> i8 {
1109 match boost_enum {
1110 PokemonBoostableStat::Attack => self.attack_boost,
1111 PokemonBoostableStat::Defense => self.defense_boost,
1112 PokemonBoostableStat::SpecialAttack => self.special_attack_boost,
1113 PokemonBoostableStat::SpecialDefense => self.special_defense_boost,
1114 PokemonBoostableStat::Speed => self.speed_boost,
1115 PokemonBoostableStat::Evasion => self.evasion_boost,
1116 PokemonBoostableStat::Accuracy => self.accuracy_boost,
1117 }
1118 }
1119
1120 pub fn calculate_boosted_stat(&self, stat: PokemonBoostableStat) -> i16 {
1121 let active = self.get_active_immutable();
1126 match stat {
1127 PokemonBoostableStat::Attack => {
1128 #[cfg(feature = "gen4")]
1129 let boost = if active.ability == Abilities::SIMPLE {
1130 (self.attack_boost * 2).min(6).max(-6)
1131 } else {
1132 self.attack_boost
1133 };
1134
1135 #[cfg(not(feature = "gen4"))]
1136 let boost = self.attack_boost;
1137
1138 multiply_boost(boost, active.attack)
1139 }
1140 PokemonBoostableStat::Defense => {
1141 #[cfg(feature = "gen4")]
1142 let boost = if active.ability == Abilities::SIMPLE {
1143 (self.defense_boost * 2).min(6).max(-6)
1144 } else {
1145 self.defense_boost
1146 };
1147 #[cfg(not(feature = "gen4"))]
1148 let boost = self.defense_boost;
1149
1150 multiply_boost(boost, active.defense)
1151 }
1152 PokemonBoostableStat::SpecialAttack => {
1153 #[cfg(feature = "gen4")]
1154 let boost = if active.ability == Abilities::SIMPLE {
1155 (self.special_attack_boost * 2).min(6).max(-6)
1156 } else {
1157 self.special_attack_boost
1158 };
1159 #[cfg(not(feature = "gen4"))]
1160 let boost = self.special_attack_boost;
1161
1162 multiply_boost(boost, active.special_attack)
1163 }
1164 PokemonBoostableStat::SpecialDefense => {
1165 #[cfg(feature = "gen4")]
1166 let boost = if active.ability == Abilities::SIMPLE {
1167 (self.special_defense_boost * 2).min(6).max(-6)
1168 } else {
1169 self.special_defense_boost
1170 };
1171 #[cfg(not(feature = "gen4"))]
1172 let boost = self.special_defense_boost;
1173
1174 multiply_boost(boost, active.special_defense)
1175 }
1176 PokemonBoostableStat::Speed => {
1177 #[cfg(feature = "gen4")]
1178 let boost = if active.ability == Abilities::SIMPLE {
1179 (self.speed_boost * 2).min(6).max(-6)
1180 } else {
1181 self.speed_boost
1182 };
1183 #[cfg(not(feature = "gen4"))]
1184 let boost = self.speed_boost;
1185
1186 multiply_boost(boost, active.speed)
1187 }
1188 _ => {
1189 panic!("Not implemented")
1190 }
1191 }
1192 }
1193
1194 pub fn has_alive_non_rested_sleeping_pkmn(&self) -> bool {
1195 for p in self.pokemon.into_iter() {
1196 if p.status == PokemonStatus::SLEEP && p.hp > 0 && p.rest_turns == 0 {
1197 return true;
1198 }
1199 }
1200 false
1201 }
1202
1203 #[cfg(not(feature = "terastallization"))]
1204 pub fn can_use_tera(&self) -> bool {
1205 false
1206 }
1207
1208 #[cfg(feature = "terastallization")]
1209 pub fn can_use_tera(&self) -> bool {
1210 for p in self.pokemon.into_iter() {
1211 if p.terastallized {
1212 return false;
1213 }
1214 }
1215 true
1216 }
1217
1218 fn toggle_force_switch(&mut self) {
1219 self.force_switch = !self.force_switch;
1220 }
1221
1222 pub fn add_switches(&self, vec: &mut Vec<MoveChoice>) {
1223 let mut iter = self.pokemon.into_iter();
1224 while let Some(p) = iter.next() {
1225 if p.hp > 0 && iter.pokemon_index != self.active_index {
1226 vec.push(MoveChoice::Switch(iter.pokemon_index));
1227 }
1228 }
1229 if vec.len() == 0 {
1230 vec.push(MoveChoice::None);
1231 }
1232 }
1233
1234 pub fn trapped(&self, opponent_active: &Pokemon) -> bool {
1235 let active_pkmn = self.get_active_immutable();
1236 if self
1237 .volatile_statuses
1238 .contains(&PokemonVolatileStatus::LOCKEDMOVE)
1239 {
1240 return true;
1241 }
1242 if active_pkmn.item == Items::SHEDSHELL || active_pkmn.has_type(&PokemonType::GHOST) {
1243 return false;
1244 } else if self
1245 .volatile_statuses
1246 .contains(&PokemonVolatileStatus::PARTIALLYTRAPPED)
1247 {
1248 return true;
1249 } else if opponent_active.ability == Abilities::SHADOWTAG {
1250 return true;
1251 } else if opponent_active.ability == Abilities::ARENATRAP && active_pkmn.is_grounded() {
1252 return true;
1253 } else if opponent_active.ability == Abilities::MAGNETPULL
1254 && active_pkmn.has_type(&PokemonType::STEEL)
1255 {
1256 return true;
1257 }
1258 false
1259 }
1260
1261 pub fn get_active(&mut self) -> &mut Pokemon {
1262 &mut self.pokemon[self.active_index]
1263 }
1264
1265 pub fn get_active_immutable(&self) -> &Pokemon {
1266 &self.pokemon[self.active_index]
1267 }
1268
1269 pub fn num_fainted_pkmn(&self) -> i8 {
1270 let mut count = 0;
1271 for p in self.pokemon.into_iter() {
1272 if p.hp == 0 && p.level != 1 {
1273 count += 1;
1274 }
1275 }
1276 count
1277 }
1278
1279 pub fn visible_alive_pkmn(&self) -> i8 {
1280 let mut count = 0;
1281 for p in self.pokemon.into_iter() {
1282 if p.hp > 0 {
1283 count += 1;
1284 }
1285 }
1286 count
1287 }
1288
1289 pub fn get_alive_pkmn_indices(&self) -> Vec<PokemonIndex> {
1290 let mut vec = Vec::with_capacity(6);
1291 let mut iter = self.pokemon.into_iter();
1292
1293 while let Some(p) = iter.next() {
1294 if p.hp > 0 && iter.pokemon_index != self.active_index {
1295 vec.push(iter.pokemon_index.clone());
1296 }
1297 }
1298
1299 vec
1300 }
1301
1302 pub fn get_side_condition(&self, side_condition: PokemonSideCondition) -> i8 {
1303 match side_condition {
1304 PokemonSideCondition::AuroraVeil => self.side_conditions.aurora_veil,
1305 PokemonSideCondition::CraftyShield => self.side_conditions.crafty_shield,
1306 PokemonSideCondition::HealingWish => self.side_conditions.healing_wish,
1307 PokemonSideCondition::LightScreen => self.side_conditions.light_screen,
1308 PokemonSideCondition::LuckyChant => self.side_conditions.lucky_chant,
1309 PokemonSideCondition::LunarDance => self.side_conditions.lunar_dance,
1310 PokemonSideCondition::MatBlock => self.side_conditions.mat_block,
1311 PokemonSideCondition::Mist => self.side_conditions.mist,
1312 PokemonSideCondition::Protect => self.side_conditions.protect,
1313 PokemonSideCondition::QuickGuard => self.side_conditions.quick_guard,
1314 PokemonSideCondition::Reflect => self.side_conditions.reflect,
1315 PokemonSideCondition::Safeguard => self.side_conditions.safeguard,
1316 PokemonSideCondition::Spikes => self.side_conditions.spikes,
1317 PokemonSideCondition::Stealthrock => self.side_conditions.stealth_rock,
1318 PokemonSideCondition::StickyWeb => self.side_conditions.sticky_web,
1319 PokemonSideCondition::Tailwind => self.side_conditions.tailwind,
1320 PokemonSideCondition::ToxicCount => self.side_conditions.toxic_count,
1321 PokemonSideCondition::ToxicSpikes => self.side_conditions.toxic_spikes,
1322 PokemonSideCondition::WideGuard => self.side_conditions.wide_guard,
1323 }
1324 }
1325
1326 pub fn update_side_condition(&mut self, side_condition: PokemonSideCondition, amount: i8) {
1327 match side_condition {
1328 PokemonSideCondition::AuroraVeil => self.side_conditions.aurora_veil += amount,
1329 PokemonSideCondition::CraftyShield => self.side_conditions.crafty_shield += amount,
1330 PokemonSideCondition::HealingWish => self.side_conditions.healing_wish += amount,
1331 PokemonSideCondition::LightScreen => self.side_conditions.light_screen += amount,
1332 PokemonSideCondition::LuckyChant => self.side_conditions.lucky_chant += amount,
1333 PokemonSideCondition::LunarDance => self.side_conditions.lunar_dance += amount,
1334 PokemonSideCondition::MatBlock => self.side_conditions.mat_block += amount,
1335 PokemonSideCondition::Mist => self.side_conditions.mist += amount,
1336 PokemonSideCondition::Protect => self.side_conditions.protect += amount,
1337 PokemonSideCondition::QuickGuard => self.side_conditions.quick_guard += amount,
1338 PokemonSideCondition::Reflect => self.side_conditions.reflect += amount,
1339 PokemonSideCondition::Safeguard => self.side_conditions.safeguard += amount,
1340 PokemonSideCondition::Spikes => self.side_conditions.spikes += amount,
1341 PokemonSideCondition::Stealthrock => self.side_conditions.stealth_rock += amount,
1342 PokemonSideCondition::StickyWeb => self.side_conditions.sticky_web += amount,
1343 PokemonSideCondition::Tailwind => self.side_conditions.tailwind += amount,
1344 PokemonSideCondition::ToxicCount => self.side_conditions.toxic_count += amount,
1345 PokemonSideCondition::ToxicSpikes => self.side_conditions.toxic_spikes += amount,
1346 PokemonSideCondition::WideGuard => self.side_conditions.wide_guard += amount,
1347 }
1348 }
1349}
1350
1351impl Default for Side {
1352 fn default() -> Side {
1353 Side {
1354 active_index: PokemonIndex::P0,
1355 baton_passing: false,
1356 pokemon: SidePokemon {
1357 p0: Pokemon {
1358 ..Pokemon::default()
1359 },
1360 p1: Pokemon {
1361 ..Pokemon::default()
1362 },
1363 p2: Pokemon {
1364 ..Pokemon::default()
1365 },
1366 p3: Pokemon {
1367 ..Pokemon::default()
1368 },
1369 p4: Pokemon {
1370 ..Pokemon::default()
1371 },
1372 p5: Pokemon {
1373 ..Pokemon::default()
1374 },
1375 },
1376 substitute_health: 0,
1377 attack_boost: 0,
1378 defense_boost: 0,
1379 special_attack_boost: 0,
1380 special_defense_boost: 0,
1381 speed_boost: 0,
1382 accuracy_boost: 0,
1383 side_conditions: SideConditions {
1384 ..Default::default()
1385 },
1386 volatile_statuses: HashSet::<PokemonVolatileStatus>::new(),
1387 wish: (0, 0),
1388 future_sight: (0, PokemonIndex::P0),
1389 force_switch: false,
1390 slow_uturn_move: false,
1391 force_trapped: false,
1392 last_used_move: LastUsedMove::None,
1393 damage_dealt: DamageDealt::default(),
1394 switch_out_move_second_saved_move: Choices::NONE,
1395 evasion_boost: 0,
1396 }
1397 }
1398}
1399
1400#[derive(Debug, Clone)]
1401pub struct State {
1402 pub side_one: Side,
1403 pub side_two: Side,
1404 pub weather: StateWeather,
1405 pub terrain: StateTerrain,
1406 pub trick_room: StateTrickRoom,
1407 pub team_preview: bool,
1408 pub use_last_used_move: bool,
1409 pub use_damage_dealt: bool,
1410}
1411
1412impl Default for State {
1413 fn default() -> State {
1414 let mut s = State {
1415 side_one: Side::default(),
1416 side_two: Side::default(),
1417 weather: StateWeather {
1418 weather_type: Weather::NONE,
1419 turns_remaining: -1,
1420 },
1421 terrain: StateTerrain {
1422 terrain_type: Terrain::NONE,
1423 turns_remaining: 0,
1424 },
1425 trick_room: StateTrickRoom {
1426 active: false,
1427 turns_remaining: 0,
1428 },
1429 team_preview: false,
1430 use_damage_dealt: false,
1431 use_last_used_move: false,
1432 };
1433
1434 s.side_two.get_active().speed += 1;
1436 s
1437 }
1438}
1439
1440impl State {
1441 pub fn battle_is_over(&self) -> f32 {
1442 if self.side_one.pokemon.into_iter().all(|p| p.hp <= 0) {
1446 return -1.0;
1447 }
1448 if self
1450 .side_two
1451 .pokemon
1452 .into_iter()
1453 .all(|p| p.hp <= 0 && p.level != 1)
1454 {
1455 return 1.0;
1456 }
1457 0.0
1458 }
1459
1460 pub fn get_all_options(&self) -> (Vec<MoveChoice>, Vec<MoveChoice>) {
1461 let mut side_one_options: Vec<MoveChoice> = Vec::with_capacity(9);
1462 let mut side_two_options: Vec<MoveChoice> = Vec::with_capacity(9);
1463
1464 let side_one_active = self.side_one.get_active_immutable();
1465 let side_two_active = self.side_two.get_active_immutable();
1466
1467 if self.side_one.force_switch {
1468 self.side_one.add_switches(&mut side_one_options);
1469 if self.side_two.switch_out_move_second_saved_move == Choices::NONE {
1470 side_two_options.push(MoveChoice::None);
1471 } else {
1472 self.side_two.get_active_immutable().add_move_from_choice(
1473 &mut side_two_options,
1474 self.side_two.switch_out_move_second_saved_move,
1475 );
1476 }
1477 return (side_one_options, side_two_options);
1478 }
1479
1480 if self.side_two.force_switch {
1481 self.side_two.add_switches(&mut side_two_options);
1482 if self.side_one.switch_out_move_second_saved_move == Choices::NONE {
1483 side_one_options.push(MoveChoice::None);
1484 } else {
1485 self.side_one.get_active_immutable().add_move_from_choice(
1486 &mut side_one_options,
1487 self.side_one.switch_out_move_second_saved_move,
1488 );
1489 }
1490 return (side_one_options, side_two_options);
1491 }
1492
1493 let side_one_force_switch = self.side_one.get_active_immutable().hp <= 0;
1494 let side_two_force_switch = self.side_two.get_active_immutable().hp <= 0;
1495
1496 if side_one_force_switch && side_two_force_switch {
1497 self.side_one.add_switches(&mut side_one_options);
1498 self.side_two.add_switches(&mut side_two_options);
1499 return (side_one_options, side_two_options);
1500 }
1501 if side_one_force_switch {
1502 self.side_one.add_switches(&mut side_one_options);
1503 side_two_options.push(MoveChoice::None);
1504 return (side_one_options, side_two_options);
1505 }
1506 if side_two_force_switch {
1507 side_one_options.push(MoveChoice::None);
1508 self.side_two.add_switches(&mut side_two_options);
1509 return (side_one_options, side_two_options);
1510 }
1511
1512 if self
1513 .side_one
1514 .volatile_statuses
1515 .contains(&PokemonVolatileStatus::MUSTRECHARGE)
1516 {
1517 side_one_options.push(MoveChoice::None);
1518 } else {
1519 let encored = self
1520 .side_one
1521 .volatile_statuses
1522 .contains(&PokemonVolatileStatus::ENCORE);
1523 self.side_one.get_active_immutable().add_available_moves(
1524 &mut side_one_options,
1525 &self.side_one.last_used_move,
1526 encored,
1527 self.side_one.can_use_tera(),
1528 );
1529 if !self.side_one.trapped(side_two_active) {
1530 self.side_one.add_switches(&mut side_one_options);
1531 }
1532 }
1533
1534 if self
1535 .side_two
1536 .volatile_statuses
1537 .contains(&PokemonVolatileStatus::MUSTRECHARGE)
1538 {
1539 side_two_options.push(MoveChoice::None);
1540 } else {
1541 let encored = self
1542 .side_two
1543 .volatile_statuses
1544 .contains(&PokemonVolatileStatus::ENCORE);
1545 self.side_two.get_active_immutable().add_available_moves(
1546 &mut side_two_options,
1547 &self.side_two.last_used_move,
1548 encored,
1549 self.side_two.can_use_tera(),
1550 );
1551 if !self.side_two.trapped(side_one_active) {
1552 self.side_two.add_switches(&mut side_two_options);
1553 }
1554 }
1555
1556 if side_one_options.len() == 0 {
1557 side_one_options.push(MoveChoice::None);
1558 }
1559 if side_two_options.len() == 0 {
1560 side_two_options.push(MoveChoice::None);
1561 }
1562
1563 (side_one_options, side_two_options)
1564 }
1565
1566 pub fn get_side(&mut self, side_ref: &SideReference) -> &mut Side {
1567 match side_ref {
1568 SideReference::SideOne => &mut self.side_one,
1569 SideReference::SideTwo => &mut self.side_two,
1570 }
1571 }
1572
1573 pub fn get_side_immutable(&self, side_ref: &SideReference) -> &Side {
1574 match side_ref {
1575 SideReference::SideOne => &self.side_one,
1576 SideReference::SideTwo => &self.side_two,
1577 }
1578 }
1579
1580 pub fn get_both_sides(&mut self, side_ref: &SideReference) -> (&mut Side, &mut Side) {
1581 match side_ref {
1582 SideReference::SideOne => (&mut self.side_one, &mut self.side_two),
1583 SideReference::SideTwo => (&mut self.side_two, &mut self.side_one),
1584 }
1585 }
1586
1587 pub fn get_both_sides_immutable(&self, side_ref: &SideReference) -> (&Side, &Side) {
1588 match side_ref {
1589 SideReference::SideOne => (&self.side_one, &self.side_two),
1590 SideReference::SideTwo => (&self.side_two, &self.side_one),
1591 }
1592 }
1593
1594 pub fn re_enable_disabled_moves(
1595 &mut self,
1596 side_ref: &SideReference,
1597 vec_to_add_to: &mut Vec<Instruction>,
1598 ) {
1599 let active = self.get_side(side_ref).get_active();
1600 if active.moves.m0.disabled {
1601 active.moves.m0.disabled = false;
1602 vec_to_add_to.push(Instruction::EnableMove(EnableMoveInstruction {
1603 side_ref: *side_ref,
1604 move_index: PokemonMoveIndex::M0,
1605 }));
1606 }
1607 if active.moves.m1.disabled {
1608 active.moves.m1.disabled = false;
1609 vec_to_add_to.push(Instruction::EnableMove(EnableMoveInstruction {
1610 side_ref: *side_ref,
1611 move_index: PokemonMoveIndex::M1,
1612 }));
1613 }
1614 if active.moves.m2.disabled {
1615 active.moves.m2.disabled = false;
1616 vec_to_add_to.push(Instruction::EnableMove(EnableMoveInstruction {
1617 side_ref: *side_ref,
1618 move_index: PokemonMoveIndex::M2,
1619 }));
1620 }
1621 if active.moves.m3.disabled {
1622 active.moves.m3.disabled = false;
1623 vec_to_add_to.push(Instruction::EnableMove(EnableMoveInstruction {
1624 side_ref: *side_ref,
1625 move_index: PokemonMoveIndex::M3,
1626 }));
1627 }
1628 }
1629
1630 pub fn reset_toxic_count(
1631 &mut self,
1632 side_ref: &SideReference,
1633 vec_to_add_to: &mut Vec<Instruction>,
1634 ) {
1635 let side = self.get_side(side_ref);
1636 if side.side_conditions.toxic_count > 0 {
1637 vec_to_add_to.push(Instruction::ChangeSideCondition(
1638 ChangeSideConditionInstruction {
1639 side_ref: *side_ref,
1640 side_condition: PokemonSideCondition::ToxicCount,
1641 amount: -1 * side.side_conditions.toxic_count,
1642 },
1643 ));
1644 side.side_conditions.toxic_count = 0;
1645 }
1646 }
1647
1648 pub fn remove_volatile_statuses_on_switch(
1649 &mut self,
1650 side_ref: &SideReference,
1651 vec_to_add_to: &mut Vec<Instruction>,
1652 baton_passing: bool,
1653 ) {
1654 let side = self.get_side(side_ref);
1655
1656 let mut volatile_statuses = std::mem::take(&mut side.volatile_statuses);
1659
1660 let active = side.get_active();
1661 volatile_statuses.retain(|pkmn_volatile_status| {
1662 let should_retain = match pkmn_volatile_status {
1663 PokemonVolatileStatus::SUBSTITUTE => baton_passing,
1664 PokemonVolatileStatus::LEECHSEED => baton_passing,
1665 PokemonVolatileStatus::TYPECHANGE => {
1666 if active.base_types != active.types {
1667 vec_to_add_to.push(Instruction::ChangeType(ChangeType {
1668 side_ref: *side_ref,
1669 new_types: active.base_types,
1670 old_types: active.types,
1671 }));
1672 active.types = active.base_types;
1673 }
1674 false
1675 }
1676 _ => false,
1677 };
1678
1679 if !should_retain {
1680 vec_to_add_to.push(Instruction::RemoveVolatileStatus(
1681 RemoveVolatileStatusInstruction {
1682 side_ref: *side_ref,
1683 volatile_status: *pkmn_volatile_status,
1684 },
1685 ));
1686 }
1687 should_retain
1688 });
1689
1690 side.volatile_statuses = volatile_statuses;
1692 }
1693
1694 pub fn reset_boosts(&mut self, side_ref: &SideReference, vec_to_add_to: &mut Vec<Instruction>) {
1695 let side = self.get_side(side_ref);
1696
1697 if side.attack_boost != 0 {
1698 vec_to_add_to.push(Instruction::Boost(BoostInstruction {
1699 side_ref: *side_ref,
1700 stat: PokemonBoostableStat::Attack,
1701 amount: -1 * side.attack_boost,
1702 }));
1703 side.attack_boost = 0;
1704 }
1705
1706 if side.defense_boost != 0 {
1707 vec_to_add_to.push(Instruction::Boost(BoostInstruction {
1708 side_ref: *side_ref,
1709 stat: PokemonBoostableStat::Defense,
1710 amount: -1 * side.defense_boost,
1711 }));
1712 side.defense_boost = 0;
1713 }
1714
1715 if side.special_attack_boost != 0 {
1716 vec_to_add_to.push(Instruction::Boost(BoostInstruction {
1717 side_ref: *side_ref,
1718 stat: PokemonBoostableStat::SpecialAttack,
1719 amount: -1 * side.special_attack_boost,
1720 }));
1721 side.special_attack_boost = 0;
1722 }
1723
1724 if side.special_defense_boost != 0 {
1725 vec_to_add_to.push(Instruction::Boost(BoostInstruction {
1726 side_ref: *side_ref,
1727 stat: PokemonBoostableStat::SpecialDefense,
1728 amount: -1 * side.special_defense_boost,
1729 }));
1730 side.special_defense_boost = 0;
1731 }
1732
1733 if side.speed_boost != 0 {
1734 vec_to_add_to.push(Instruction::Boost(BoostInstruction {
1735 side_ref: *side_ref,
1736 stat: PokemonBoostableStat::Speed,
1737 amount: -1 * side.speed_boost,
1738 }));
1739 side.speed_boost = 0;
1740 }
1741
1742 if side.evasion_boost != 0 {
1743 vec_to_add_to.push(Instruction::Boost(BoostInstruction {
1744 side_ref: *side_ref,
1745 stat: PokemonBoostableStat::Evasion,
1746 amount: -1 * side.evasion_boost,
1747 }));
1748 side.evasion_boost = 0;
1749 }
1750
1751 if side.accuracy_boost != 0 {
1752 vec_to_add_to.push(Instruction::Boost(BoostInstruction {
1753 side_ref: *side_ref,
1754 stat: PokemonBoostableStat::Accuracy,
1755 amount: -1 * side.accuracy_boost,
1756 }));
1757 side.accuracy_boost = 0;
1758 }
1759 }
1760
1761 pub fn terrain_is_active(&self, terrain: &Terrain) -> bool {
1762 &self.terrain.terrain_type == terrain && self.terrain.turns_remaining > 0
1763 }
1764
1765 pub fn weather_is_active(&self, weather: &Weather) -> bool {
1766 let s1_active = self.side_one.get_active_immutable();
1767 let s2_active = self.side_two.get_active_immutable();
1768 &self.weather.weather_type == weather
1769 && s1_active.ability != Abilities::AIRLOCK
1770 && s1_active.ability != Abilities::CLOUDNINE
1771 && s2_active.ability != Abilities::AIRLOCK
1772 && s2_active.ability != Abilities::CLOUDNINE
1773 }
1774
1775 fn _state_contains_any_move(&self, moves: &[Choices]) -> bool {
1776 for s in [&self.side_one, &self.side_two] {
1777 for pkmn in s.pokemon.into_iter() {
1778 for mv in pkmn.moves.into_iter() {
1779 if moves.contains(&mv.id) {
1780 return true;
1781 }
1782 }
1783 }
1784 }
1785
1786 false
1787 }
1788
1789 pub fn set_damage_dealt_flag(&mut self) {
1790 if self._state_contains_any_move(&[
1791 Choices::COUNTER,
1792 Choices::MIRRORCOAT,
1793 Choices::METALBURST,
1794 Choices::COMEUPPANCE,
1795 Choices::FOCUSPUNCH,
1796 ]) {
1797 self.use_damage_dealt = true
1798 }
1799 }
1800
1801 pub fn set_last_used_move_flag(&mut self) {
1802 if self._state_contains_any_move(&[
1803 Choices::ENCORE,
1804 Choices::FAKEOUT,
1805 Choices::FIRSTIMPRESSION,
1806 Choices::BLOODMOON,
1807 Choices::GIGATONHAMMER,
1808 ]) {
1809 self.use_last_used_move = true
1810 }
1811 }
1812
1813 pub fn set_conditional_mechanics(&mut self) {
1814 self.set_damage_dealt_flag();
1820 self.set_last_used_move_flag();
1821 }
1822
1823 fn damage(&mut self, side_ref: &SideReference, amount: i16) {
1824 let active = self.get_side(&side_ref).get_active();
1825
1826 active.hp -= amount;
1827 }
1828
1829 fn heal(&mut self, side_ref: &SideReference, amount: i16) {
1830 let active = self.get_side(&side_ref).get_active();
1831
1832 active.hp += amount;
1833 }
1834
1835 fn switch(
1836 &mut self,
1837 side_ref: &SideReference,
1838 next_active_index: PokemonIndex,
1839 _: PokemonIndex,
1840 ) {
1841 let side = self.get_side(&side_ref);
1842 side.active_index = next_active_index;
1843 }
1844
1845 fn reverse_switch(
1846 &mut self,
1847 side_ref: &SideReference,
1848 _: PokemonIndex,
1849 previous_active_index: PokemonIndex,
1850 ) {
1851 let side = self.get_side(&side_ref);
1852 side.active_index = previous_active_index;
1853 }
1854
1855 fn apply_volatile_status(
1856 &mut self,
1857 side_ref: &SideReference,
1858 volatile_status: PokemonVolatileStatus,
1859 ) {
1860 self.get_side(&side_ref)
1861 .volatile_statuses
1862 .insert(volatile_status);
1863 }
1864
1865 fn remove_volatile_status(
1866 &mut self,
1867 side_ref: &SideReference,
1868 volatile_status: PokemonVolatileStatus,
1869 ) {
1870 self.get_side(&side_ref)
1871 .volatile_statuses
1872 .remove(&volatile_status);
1873 }
1874
1875 fn change_status(
1876 &mut self,
1877 side_ref: &SideReference,
1878 pokemon_index: PokemonIndex,
1879 new_status: PokemonStatus,
1880 ) {
1881 let pkmn = &mut self.get_side(&side_ref).pokemon[pokemon_index];
1882 pkmn.status = new_status;
1883 }
1884
1885 fn apply_boost(&mut self, side_ref: &SideReference, stat: &PokemonBoostableStat, amount: i8) {
1886 let side = self.get_side(&side_ref);
1887 match stat {
1888 PokemonBoostableStat::Attack => side.attack_boost += amount,
1889 PokemonBoostableStat::Defense => side.defense_boost += amount,
1890 PokemonBoostableStat::SpecialAttack => side.special_attack_boost += amount,
1891 PokemonBoostableStat::SpecialDefense => side.special_defense_boost += amount,
1892 PokemonBoostableStat::Speed => side.speed_boost += amount,
1893 PokemonBoostableStat::Evasion => side.evasion_boost += amount,
1894 PokemonBoostableStat::Accuracy => side.accuracy_boost += amount,
1895 }
1896 }
1897
1898 fn increment_side_condition(
1899 &mut self,
1900 side_ref: &SideReference,
1901 side_condition: &PokemonSideCondition,
1902 amount: i8,
1903 ) {
1904 let side = self.get_side(&side_ref);
1905
1906 match side_condition {
1907 PokemonSideCondition::AuroraVeil => side.side_conditions.aurora_veil += amount,
1908 PokemonSideCondition::CraftyShield => side.side_conditions.crafty_shield += amount,
1909 PokemonSideCondition::HealingWish => side.side_conditions.healing_wish += amount,
1910 PokemonSideCondition::LightScreen => side.side_conditions.light_screen += amount,
1911 PokemonSideCondition::LuckyChant => side.side_conditions.lucky_chant += amount,
1912 PokemonSideCondition::LunarDance => side.side_conditions.lunar_dance += amount,
1913 PokemonSideCondition::MatBlock => side.side_conditions.mat_block += amount,
1914 PokemonSideCondition::Mist => side.side_conditions.mist += amount,
1915 PokemonSideCondition::Protect => side.side_conditions.protect += amount,
1916 PokemonSideCondition::QuickGuard => side.side_conditions.quick_guard += amount,
1917 PokemonSideCondition::Reflect => side.side_conditions.reflect += amount,
1918 PokemonSideCondition::Safeguard => side.side_conditions.safeguard += amount,
1919 PokemonSideCondition::Spikes => side.side_conditions.spikes += amount,
1920 PokemonSideCondition::Stealthrock => side.side_conditions.stealth_rock += amount,
1921 PokemonSideCondition::StickyWeb => side.side_conditions.sticky_web += amount,
1922 PokemonSideCondition::Tailwind => side.side_conditions.tailwind += amount,
1923 PokemonSideCondition::ToxicCount => side.side_conditions.toxic_count += amount,
1924 PokemonSideCondition::ToxicSpikes => side.side_conditions.toxic_spikes += amount,
1925 PokemonSideCondition::WideGuard => side.side_conditions.wide_guard += amount,
1926 }
1927 }
1928
1929 fn change_types(
1930 &mut self,
1931 side_reference: &SideReference,
1932 new_types: (PokemonType, PokemonType),
1933 ) {
1934 self.get_side(side_reference).get_active().types = new_types;
1935 }
1936
1937 fn change_item(&mut self, side_reference: &SideReference, new_item: Items) {
1938 self.get_side(side_reference).get_active().item = new_item;
1939 }
1940
1941 fn change_weather(&mut self, weather_type: Weather, turns_remaining: i8) {
1942 self.weather.weather_type = weather_type;
1943 self.weather.turns_remaining = turns_remaining;
1944 }
1945
1946 fn change_terrain(&mut self, terrain_type: Terrain, turns_remaining: i8) {
1947 self.terrain.terrain_type = terrain_type;
1948 self.terrain.turns_remaining = turns_remaining;
1949 }
1950
1951 fn enable_move(&mut self, side_reference: &SideReference, move_index: &PokemonMoveIndex) {
1952 self.get_side(side_reference).get_active().moves[move_index].disabled = false;
1953 }
1954
1955 fn disable_move(&mut self, side_reference: &SideReference, move_index: &PokemonMoveIndex) {
1956 self.get_side(side_reference).get_active().moves[move_index].disabled = true;
1957 }
1958
1959 fn set_wish(&mut self, side_reference: &SideReference, wish_amount_change: i16) {
1960 self.get_side(side_reference).wish.0 = 2;
1961 self.get_side(side_reference).wish.1 += wish_amount_change;
1962 }
1963
1964 fn unset_wish(&mut self, side_reference: &SideReference, wish_amount_change: i16) {
1965 self.get_side(side_reference).wish.0 = 0;
1966 self.get_side(side_reference).wish.1 -= wish_amount_change;
1967 }
1968
1969 fn increment_wish(&mut self, side_reference: &SideReference) {
1970 self.get_side(side_reference).wish.0 += 1;
1971 }
1972
1973 fn decrement_wish(&mut self, side_reference: &SideReference) {
1974 self.get_side(side_reference).wish.0 -= 1;
1975 }
1976
1977 fn set_future_sight(&mut self, side_reference: &SideReference, pokemon_index: PokemonIndex) {
1978 let side = self.get_side(side_reference);
1979 side.future_sight.0 = 3;
1980 side.future_sight.1 = pokemon_index;
1981 }
1982
1983 fn unset_future_sight(
1984 &mut self,
1985 side_reference: &SideReference,
1986 previous_pokemon_index: PokemonIndex,
1987 ) {
1988 let side = self.get_side(side_reference);
1989 side.future_sight.0 = 0;
1990 side.future_sight.1 = previous_pokemon_index;
1991 }
1992
1993 fn increment_future_sight(&mut self, side_reference: &SideReference) {
1994 self.get_side(side_reference).future_sight.0 += 1;
1995 }
1996
1997 fn decrement_future_sight(&mut self, side_reference: &SideReference) {
1998 self.get_side(side_reference).future_sight.0 -= 1;
1999 }
2000
2001 fn damage_substitute(&mut self, side_reference: &SideReference, amount: i16) {
2002 self.get_side(side_reference).substitute_health -= amount;
2003 }
2004
2005 fn heal_substitute(&mut self, side_reference: &SideReference, amount: i16) {
2006 self.get_side(side_reference).substitute_health += amount;
2007 }
2008
2009 fn set_substitute_health(&mut self, side_reference: &SideReference, amount: i16) {
2010 self.get_side(side_reference).substitute_health += amount;
2011 }
2012
2013 fn decrement_rest_turn(&mut self, side_reference: &SideReference) {
2014 self.get_side(side_reference).get_active().rest_turns -= 1;
2015 }
2016
2017 fn increment_rest_turn(&mut self, side_reference: &SideReference) {
2018 self.get_side(side_reference).get_active().rest_turns += 1;
2019 }
2020
2021 fn set_rest_turn(
2022 &mut self,
2023 side_reference: &SideReference,
2024 pokemon_index: PokemonIndex,
2025 amount: i8,
2026 ) {
2027 self.get_side(side_reference).pokemon[pokemon_index].rest_turns = amount;
2028 }
2029
2030 fn set_sleep_turn(
2031 &mut self,
2032 side_reference: &SideReference,
2033 pokemon_index: PokemonIndex,
2034 amount: i8,
2035 ) {
2036 self.get_side(side_reference).pokemon[pokemon_index].sleep_turns = amount;
2037 }
2038
2039 fn toggle_trickroom(&mut self, new_turns_remaining: i8) {
2040 self.trick_room.active = !self.trick_room.active;
2041 self.trick_room.turns_remaining = new_turns_remaining;
2042 }
2043
2044 fn set_last_used_move(&mut self, side_reference: &SideReference, last_used_move: LastUsedMove) {
2045 match side_reference {
2046 SideReference::SideOne => self.side_one.last_used_move = last_used_move,
2047 SideReference::SideTwo => self.side_two.last_used_move = last_used_move,
2048 }
2049 }
2050
2051 fn decrement_pp(
2052 &mut self,
2053 side_reference: &SideReference,
2054 move_index: &PokemonMoveIndex,
2055 amount: &i8,
2056 ) {
2057 match side_reference {
2058 SideReference::SideOne => self.side_one.get_active().moves[move_index].pp -= amount,
2059 SideReference::SideTwo => self.side_two.get_active().moves[move_index].pp -= amount,
2060 }
2061 }
2062
2063 fn increment_pp(
2064 &mut self,
2065 side_reference: &SideReference,
2066 move_index: &PokemonMoveIndex,
2067 amount: &i8,
2068 ) {
2069 match side_reference {
2070 SideReference::SideOne => self.side_one.get_active().moves[move_index].pp += amount,
2071 SideReference::SideTwo => self.side_two.get_active().moves[move_index].pp += amount,
2072 }
2073 }
2074
2075 fn set_damage_dealt(
2076 side: &mut Side,
2077 damage_change: i16,
2078 move_category: MoveCategory,
2079 toggle_hit_substitute: bool,
2080 ) {
2081 side.damage_dealt.damage += damage_change;
2082 side.damage_dealt.move_category = move_category;
2083 if toggle_hit_substitute {
2084 side.damage_dealt.hit_substitute = !side.damage_dealt.hit_substitute;
2085 }
2086 }
2087
2088 pub fn apply_instructions(&mut self, instructions: &Vec<Instruction>) {
2089 for i in instructions {
2090 self.apply_one_instruction(i)
2091 }
2092 }
2093
2094 pub fn apply_one_instruction(&mut self, instruction: &Instruction) {
2095 match instruction {
2096 Instruction::Damage(instruction) => {
2097 self.damage(&instruction.side_ref, instruction.damage_amount)
2098 }
2099 Instruction::Switch(instruction) => self.switch(
2100 &instruction.side_ref,
2101 instruction.next_index,
2102 instruction.previous_index,
2103 ),
2104 Instruction::ApplyVolatileStatus(instruction) => {
2105 self.apply_volatile_status(&instruction.side_ref, instruction.volatile_status)
2106 }
2107 Instruction::RemoveVolatileStatus(instruction) => {
2108 self.remove_volatile_status(&instruction.side_ref, instruction.volatile_status)
2109 }
2110 Instruction::ChangeStatus(instruction) => self.change_status(
2111 &instruction.side_ref,
2112 instruction.pokemon_index,
2113 instruction.new_status,
2114 ),
2115 Instruction::Boost(instruction) => {
2116 self.apply_boost(&instruction.side_ref, &instruction.stat, instruction.amount)
2117 }
2118 Instruction::ChangeSideCondition(instruction) => self.increment_side_condition(
2119 &instruction.side_ref,
2120 &instruction.side_condition,
2121 instruction.amount,
2122 ),
2123 Instruction::ChangeWeather(instruction) => self.change_weather(
2124 instruction.new_weather,
2125 instruction.new_weather_turns_remaining,
2126 ),
2127 Instruction::DecrementWeatherTurnsRemaining => {
2128 self.weather.turns_remaining -= 1;
2129 }
2130 Instruction::ChangeTerrain(instruction) => self.change_terrain(
2131 instruction.new_terrain,
2132 instruction.new_terrain_turns_remaining,
2133 ),
2134 Instruction::DecrementTerrainTurnsRemaining => {
2135 self.terrain.turns_remaining -= 1;
2136 }
2137 Instruction::ChangeType(instruction) => {
2138 self.change_types(&instruction.side_ref, instruction.new_types)
2139 }
2140 Instruction::ChangeAbility(instruction) => {
2141 self.get_side(&instruction.side_ref).get_active().ability =
2142 instruction.new_ability.clone();
2143 }
2144 Instruction::Heal(instruction) => {
2145 self.heal(&instruction.side_ref, instruction.heal_amount)
2146 }
2147 Instruction::ChangeItem(instruction) => {
2148 self.change_item(&instruction.side_ref, instruction.new_item)
2149 }
2150 Instruction::ChangeAttack(instruction) => {
2151 self.get_side(&instruction.side_ref).get_active().attack += instruction.amount;
2152 }
2153 Instruction::ChangeDefense(instruction) => {
2154 self.get_side(&instruction.side_ref).get_active().defense += instruction.amount;
2155 }
2156 Instruction::ChangeSpecialAttack(instruction) => {
2157 self.get_side(&instruction.side_ref)
2158 .get_active()
2159 .special_attack += instruction.amount;
2160 }
2161 Instruction::ChangeSpecialDefense(instruction) => {
2162 self.get_side(&instruction.side_ref)
2163 .get_active()
2164 .special_defense += instruction.amount;
2165 }
2166 Instruction::ChangeSpeed(instruction) => {
2167 self.get_side(&instruction.side_ref).get_active().speed += instruction.amount;
2168 }
2169 Instruction::EnableMove(instruction) => {
2170 self.enable_move(&instruction.side_ref, &instruction.move_index)
2171 }
2172 Instruction::DisableMove(instruction) => {
2173 self.disable_move(&instruction.side_ref, &instruction.move_index)
2174 }
2175 Instruction::ChangeWish(instruction) => {
2176 self.set_wish(&instruction.side_ref, instruction.wish_amount_change);
2177 }
2178 Instruction::DecrementWish(instruction) => {
2179 self.decrement_wish(&instruction.side_ref);
2180 }
2181 Instruction::SetFutureSight(instruction) => {
2182 self.set_future_sight(&instruction.side_ref, instruction.pokemon_index);
2183 }
2184 Instruction::DecrementFutureSight(instruction) => {
2185 self.decrement_future_sight(&instruction.side_ref);
2186 }
2187 Instruction::DamageSubstitute(instruction) => {
2188 self.damage_substitute(&instruction.side_ref, instruction.damage_amount);
2189 }
2190 Instruction::ChangeSubstituteHealth(instruction) => {
2191 self.set_substitute_health(&instruction.side_ref, instruction.health_change);
2192 }
2193 Instruction::SetRestTurns(instruction) => {
2194 self.set_rest_turn(
2195 &instruction.side_ref,
2196 instruction.pokemon_index,
2197 instruction.new_turns,
2198 );
2199 }
2200 Instruction::SetSleepTurns(instruction) => {
2201 self.set_sleep_turn(
2202 &instruction.side_ref,
2203 instruction.pokemon_index,
2204 instruction.new_turns,
2205 );
2206 }
2207 Instruction::DecrementRestTurns(instruction) => {
2208 self.decrement_rest_turn(&instruction.side_ref);
2209 }
2210 Instruction::ToggleTrickRoom(instruction) => {
2211 self.toggle_trickroom(instruction.new_trickroom_turns_remaining)
2212 }
2213 Instruction::DecrementTrickRoomTurnsRemaining => {
2214 self.trick_room.turns_remaining -= 1;
2215 }
2216 Instruction::ToggleSideOneForceSwitch => self.side_one.toggle_force_switch(),
2217 Instruction::ToggleSideTwoForceSwitch => self.side_two.toggle_force_switch(),
2218 Instruction::SetSideOneMoveSecondSwitchOutMove(instruction) => {
2219 self.side_one.switch_out_move_second_saved_move = instruction.new_choice;
2220 }
2221 Instruction::SetSideTwoMoveSecondSwitchOutMove(instruction) => {
2222 self.side_two.switch_out_move_second_saved_move = instruction.new_choice;
2223 }
2224 Instruction::ToggleBatonPassing(instruction) => match instruction.side_ref {
2225 SideReference::SideOne => {
2226 self.side_one.baton_passing = !self.side_one.baton_passing
2227 }
2228 SideReference::SideTwo => {
2229 self.side_two.baton_passing = !self.side_two.baton_passing
2230 }
2231 },
2232 Instruction::ToggleTerastallized(instruction) => match instruction.side_ref {
2233 SideReference::SideOne => self.side_one.get_active().terastallized ^= true,
2234 SideReference::SideTwo => self.side_two.get_active().terastallized ^= true,
2235 },
2236 Instruction::SetLastUsedMove(instruction) => {
2237 self.set_last_used_move(&instruction.side_ref, instruction.last_used_move)
2238 }
2239 Instruction::SetDamageDealtSideOne(instruction) => State::set_damage_dealt(
2240 &mut self.side_one,
2241 instruction.damage_change,
2242 instruction.move_category,
2243 instruction.toggle_hit_substitute,
2244 ),
2245 Instruction::SetDamageDealtSideTwo(instruction) => State::set_damage_dealt(
2246 &mut self.side_two,
2247 instruction.damage_change,
2248 instruction.move_category,
2249 instruction.toggle_hit_substitute,
2250 ),
2251 Instruction::DecrementPP(instruction) => self.decrement_pp(
2252 &instruction.side_ref,
2253 &instruction.move_index,
2254 &instruction.amount,
2255 ),
2256 Instruction::FormeChange(instruction) => {
2257 self.get_side(&instruction.side_ref).get_active().id = instruction.new_forme;
2258 }
2259 }
2260 }
2261
2262 pub fn reverse_instructions(&mut self, instructions: &Vec<Instruction>) {
2263 for i in instructions.iter().rev() {
2264 self.reverse_one_instruction(i);
2265 }
2266 }
2267
2268 pub fn reverse_one_instruction(&mut self, instruction: &Instruction) {
2269 match instruction {
2270 Instruction::Damage(instruction) => {
2271 self.heal(&instruction.side_ref, instruction.damage_amount)
2272 }
2273 Instruction::Switch(instruction) => self.reverse_switch(
2274 &instruction.side_ref,
2275 instruction.next_index,
2276 instruction.previous_index,
2277 ),
2278 Instruction::ApplyVolatileStatus(instruction) => {
2279 self.remove_volatile_status(&instruction.side_ref, instruction.volatile_status)
2280 }
2281 Instruction::RemoveVolatileStatus(instruction) => {
2282 self.apply_volatile_status(&instruction.side_ref, instruction.volatile_status)
2283 }
2284 Instruction::ChangeStatus(instruction) => self.change_status(
2285 &instruction.side_ref,
2286 instruction.pokemon_index,
2287 instruction.old_status,
2288 ),
2289 Instruction::Boost(instruction) => self.apply_boost(
2290 &instruction.side_ref,
2291 &instruction.stat,
2292 -1 * instruction.amount,
2293 ),
2294 Instruction::ChangeSideCondition(instruction) => self.increment_side_condition(
2295 &instruction.side_ref,
2296 &instruction.side_condition,
2297 -1 * instruction.amount,
2298 ),
2299 Instruction::ChangeWeather(instruction) => self.change_weather(
2300 instruction.previous_weather,
2301 instruction.previous_weather_turns_remaining,
2302 ),
2303 Instruction::DecrementWeatherTurnsRemaining => {
2304 self.weather.turns_remaining += 1;
2305 }
2306 Instruction::ChangeTerrain(instruction) => self.change_terrain(
2307 instruction.previous_terrain,
2308 instruction.previous_terrain_turns_remaining,
2309 ),
2310 Instruction::DecrementTerrainTurnsRemaining => {
2311 self.terrain.turns_remaining += 1;
2312 }
2313 Instruction::ChangeType(instruction) => {
2314 self.change_types(&instruction.side_ref, instruction.old_types)
2315 }
2316 Instruction::ChangeAbility(instruction) => {
2317 self.get_side(&instruction.side_ref).get_active().ability =
2318 instruction.old_ability.clone();
2319 }
2320 Instruction::EnableMove(instruction) => {
2321 self.disable_move(&instruction.side_ref, &instruction.move_index)
2322 }
2323 Instruction::DisableMove(instruction) => {
2324 self.enable_move(&instruction.side_ref, &instruction.move_index)
2325 }
2326 Instruction::Heal(instruction) => {
2327 self.damage(&instruction.side_ref, instruction.heal_amount)
2328 }
2329 Instruction::ChangeItem(instruction) => {
2330 self.change_item(&instruction.side_ref, instruction.current_item)
2331 }
2332 Instruction::ChangeAttack(instruction) => {
2333 self.get_side(&instruction.side_ref).get_active().attack -= instruction.amount;
2334 }
2335 Instruction::ChangeDefense(instruction) => {
2336 self.get_side(&instruction.side_ref).get_active().defense -= instruction.amount;
2337 }
2338 Instruction::ChangeSpecialAttack(instruction) => {
2339 self.get_side(&instruction.side_ref)
2340 .get_active()
2341 .special_attack -= instruction.amount;
2342 }
2343 Instruction::ChangeSpecialDefense(instruction) => {
2344 self.get_side(&instruction.side_ref)
2345 .get_active()
2346 .special_defense -= instruction.amount;
2347 }
2348 Instruction::ChangeSpeed(instruction) => {
2349 self.get_side(&instruction.side_ref).get_active().speed -= instruction.amount;
2350 }
2351 Instruction::ChangeWish(instruction) => {
2352 self.unset_wish(&instruction.side_ref, instruction.wish_amount_change)
2353 }
2354 Instruction::DecrementWish(instruction) => self.increment_wish(&instruction.side_ref),
2355 Instruction::SetFutureSight(instruction) => {
2356 self.unset_future_sight(&instruction.side_ref, instruction.previous_pokemon_index)
2357 }
2358 Instruction::DecrementFutureSight(instruction) => {
2359 self.increment_future_sight(&instruction.side_ref)
2360 }
2361 Instruction::DamageSubstitute(instruction) => {
2362 self.heal_substitute(&instruction.side_ref, instruction.damage_amount);
2363 }
2364 Instruction::ChangeSubstituteHealth(instruction) => {
2365 self.set_substitute_health(&instruction.side_ref, -1 * instruction.health_change);
2366 }
2367 Instruction::SetRestTurns(instruction) => {
2368 self.set_rest_turn(
2369 &instruction.side_ref,
2370 instruction.pokemon_index,
2371 instruction.previous_turns,
2372 );
2373 }
2374 Instruction::SetSleepTurns(instruction) => {
2375 self.set_sleep_turn(
2376 &instruction.side_ref,
2377 instruction.pokemon_index,
2378 instruction.previous_turns,
2379 );
2380 }
2381 Instruction::DecrementRestTurns(instruction) => {
2382 self.increment_rest_turn(&instruction.side_ref);
2383 }
2384 Instruction::ToggleTrickRoom(instruction) => {
2385 self.toggle_trickroom(instruction.previous_trickroom_turns_remaining)
2386 }
2387 Instruction::DecrementTrickRoomTurnsRemaining => {
2388 self.trick_room.turns_remaining += 1;
2389 }
2390 Instruction::ToggleSideOneForceSwitch => self.side_one.toggle_force_switch(),
2391 Instruction::ToggleSideTwoForceSwitch => self.side_two.toggle_force_switch(),
2392 Instruction::SetSideOneMoveSecondSwitchOutMove(instruction) => {
2393 self.side_one.switch_out_move_second_saved_move = instruction.previous_choice;
2394 }
2395 Instruction::SetSideTwoMoveSecondSwitchOutMove(instruction) => {
2396 self.side_two.switch_out_move_second_saved_move = instruction.previous_choice;
2397 }
2398 Instruction::ToggleBatonPassing(instruction) => match instruction.side_ref {
2399 SideReference::SideOne => {
2400 self.side_one.baton_passing = !self.side_one.baton_passing
2401 }
2402 SideReference::SideTwo => {
2403 self.side_two.baton_passing = !self.side_two.baton_passing
2404 }
2405 },
2406 Instruction::ToggleTerastallized(instruction) => match instruction.side_ref {
2407 SideReference::SideOne => self.side_one.get_active().terastallized ^= true,
2408 SideReference::SideTwo => self.side_two.get_active().terastallized ^= true,
2409 },
2410 Instruction::SetLastUsedMove(instruction) => {
2411 self.set_last_used_move(&instruction.side_ref, instruction.previous_last_used_move)
2412 }
2413 Instruction::SetDamageDealtSideOne(instruction) => State::set_damage_dealt(
2414 &mut self.side_one,
2415 -1 * instruction.damage_change,
2416 instruction.previous_move_category,
2417 instruction.toggle_hit_substitute,
2418 ),
2419 Instruction::SetDamageDealtSideTwo(instruction) => State::set_damage_dealt(
2420 &mut self.side_two,
2421 -1 * instruction.damage_change,
2422 instruction.previous_move_category,
2423 instruction.toggle_hit_substitute,
2424 ),
2425 Instruction::DecrementPP(instruction) => self.increment_pp(
2426 &instruction.side_ref,
2427 &instruction.move_index,
2428 &instruction.amount,
2429 ),
2430 Instruction::FormeChange(instruction) => {
2431 self.get_side(&instruction.side_ref).get_active().id = instruction.previous_forme;
2432 }
2433 }
2434 }
2435}
2436
2437impl Move {
2438 pub fn serialize(&self) -> String {
2439 format!("{:?};{};{}", self.id, self.disabled, self.pp)
2440 }
2441 pub fn deserialize(serialized: &str) -> Move {
2442 let split: Vec<&str> = serialized.split(";").collect();
2443 Move {
2444 id: Choices::from_str(split[0]).unwrap(),
2445 disabled: split[1].parse::<bool>().unwrap(),
2446 pp: split[2].parse::<i8>().unwrap(),
2447 choice: MOVES
2448 .get(&Choices::from_str(split[0]).unwrap())
2449 .unwrap()
2450 .to_owned(),
2451 }
2452 }
2453}
2454
2455impl Pokemon {
2456 pub fn serialize(&self) -> String {
2457 let evs_str = format!(
2458 "{};{};{};{};{};{}",
2459 self.evs.0, self.evs.1, self.evs.2, self.evs.3, self.evs.4, self.evs.5
2460 );
2461 format!(
2462 "{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}",
2463 self.id,
2464 self.level,
2465 self.types.0.to_string(),
2466 self.types.1.to_string(),
2467 self.base_types.0.to_string(),
2468 self.base_types.1.to_string(),
2469 self.hp,
2470 self.maxhp,
2471 self.ability.to_string(),
2472 self.base_ability.to_string(),
2473 self.item.to_string(),
2474 self.nature.to_string(),
2475 evs_str,
2476 self.attack,
2477 self.defense,
2478 self.special_attack,
2479 self.special_defense,
2480 self.speed,
2481 self.status.to_string(),
2482 self.rest_turns,
2483 self.sleep_turns,
2484 self.weight_kg,
2485 self.moves.m0.serialize(),
2486 self.moves.m1.serialize(),
2487 self.moves.m2.serialize(),
2488 self.moves.m3.serialize(),
2489 self.terastallized,
2490 self.tera_type.to_string(),
2491 )
2492 }
2493
2494 pub fn deserialize(serialized: &str) -> Pokemon {
2495 let split: Vec<&str> = serialized.split(",").collect();
2496 let evs = if split[12] != "" {
2497 let mut ev_iter = split[12].split(";");
2498 (
2499 ev_iter.next().unwrap().parse::<u8>().unwrap(),
2500 ev_iter.next().unwrap().parse::<u8>().unwrap(),
2501 ev_iter.next().unwrap().parse::<u8>().unwrap(),
2502 ev_iter.next().unwrap().parse::<u8>().unwrap(),
2503 ev_iter.next().unwrap().parse::<u8>().unwrap(),
2504 ev_iter.next().unwrap().parse::<u8>().unwrap(),
2505 )
2506 } else {
2507 (85, 85, 85, 85, 85, 85)
2508 };
2509 Pokemon {
2510 id: PokemonName::from_str(split[0]).unwrap(),
2511 level: split[1].parse::<i8>().unwrap(),
2512 types: (
2513 PokemonType::from_str(split[2]).unwrap(),
2514 PokemonType::from_str(split[3]).unwrap(),
2515 ),
2516 base_types: (
2517 PokemonType::from_str(split[4]).unwrap(),
2518 PokemonType::from_str(split[5]).unwrap(),
2519 ),
2520 hp: split[6].parse::<i16>().unwrap(),
2521 maxhp: split[7].parse::<i16>().unwrap(),
2522 ability: Abilities::from_str(split[8]).unwrap(),
2523 base_ability: Abilities::from_str(split[9]).unwrap(),
2524 item: Items::from_str(split[10]).unwrap(),
2525 nature: PokemonNature::from_str(split[11]).unwrap(),
2526 evs,
2527 attack: split[13].parse::<i16>().unwrap(),
2528 defense: split[14].parse::<i16>().unwrap(),
2529 special_attack: split[15].parse::<i16>().unwrap(),
2530 special_defense: split[16].parse::<i16>().unwrap(),
2531 speed: split[17].parse::<i16>().unwrap(),
2532 status: PokemonStatus::from_str(split[18]).unwrap(),
2533 rest_turns: split[19].parse::<i8>().unwrap(),
2534 sleep_turns: split[20].parse::<i8>().unwrap(),
2535 weight_kg: split[21].parse::<f32>().unwrap(),
2536 moves: PokemonMoves {
2537 m0: Move::deserialize(split[22]),
2538 m1: Move::deserialize(split[23]),
2539 m2: Move::deserialize(split[24]),
2540 m3: Move::deserialize(split[25]),
2541 },
2542 terastallized: split[26].parse::<bool>().unwrap(),
2543 tera_type: PokemonType::from_str(split[27]).unwrap(),
2544 }
2545 }
2546}
2547
2548impl LastUsedMove {
2549 pub fn serialize(&self) -> String {
2550 match self {
2551 LastUsedMove::Move(move_index) => format!("move:{}", move_index.serialize()),
2552 LastUsedMove::Switch(pkmn_index) => format!("switch:{}", pkmn_index.serialize()),
2553 LastUsedMove::None => "move:none".to_string(),
2554 }
2555 }
2556 pub fn deserialize(serialized: &str) -> LastUsedMove {
2557 let split: Vec<&str> = serialized.split(":").collect();
2558 match split[0] {
2559 "move" => {
2560 if split[1] == "none" {
2561 LastUsedMove::None
2562 } else {
2563 LastUsedMove::Move(PokemonMoveIndex::deserialize(split[1]))
2564 }
2565 }
2566 "switch" => LastUsedMove::Switch(PokemonIndex::deserialize(split[1])),
2567 _ => panic!("Invalid LastUsedMove: {}", serialized),
2568 }
2569 }
2570}
2571
2572impl PokemonIndex {
2573 pub fn serialize(&self) -> String {
2574 match self {
2575 PokemonIndex::P0 => "0".to_string(),
2576 PokemonIndex::P1 => "1".to_string(),
2577 PokemonIndex::P2 => "2".to_string(),
2578 PokemonIndex::P3 => "3".to_string(),
2579 PokemonIndex::P4 => "4".to_string(),
2580 PokemonIndex::P5 => "5".to_string(),
2581 }
2582 }
2583
2584 pub fn deserialize(serialized: &str) -> PokemonIndex {
2585 match serialized {
2586 "0" => PokemonIndex::P0,
2587 "1" => PokemonIndex::P1,
2588 "2" => PokemonIndex::P2,
2589 "3" => PokemonIndex::P3,
2590 "4" => PokemonIndex::P4,
2591 "5" => PokemonIndex::P5,
2592 _ => panic!("Invalid PokemonIndex: {}", serialized),
2593 }
2594 }
2595}
2596
2597impl PokemonMoveIndex {
2598 pub fn serialize(&self) -> String {
2599 match self {
2600 PokemonMoveIndex::M0 => "0".to_string(),
2601 PokemonMoveIndex::M1 => "1".to_string(),
2602 PokemonMoveIndex::M2 => "2".to_string(),
2603 PokemonMoveIndex::M3 => "3".to_string(),
2604 }
2605 }
2606
2607 pub fn deserialize(serialized: &str) -> PokemonMoveIndex {
2608 match serialized {
2609 "0" => PokemonMoveIndex::M0,
2610 "1" => PokemonMoveIndex::M1,
2611 "2" => PokemonMoveIndex::M2,
2612 "3" => PokemonMoveIndex::M3,
2613 _ => panic!("Invalid PokemonMoveIndex: {}", serialized),
2614 }
2615 }
2616}
2617
2618impl SideConditions {
2619 pub fn serialize(&self) -> String {
2620 format!(
2621 "{};{};{};{};{};{};{};{};{};{};{};{};{};{};{};{};{};{};{}",
2622 self.aurora_veil,
2623 self.crafty_shield,
2624 self.healing_wish,
2625 self.light_screen,
2626 self.lucky_chant,
2627 self.lunar_dance,
2628 self.mat_block,
2629 self.mist,
2630 self.protect,
2631 self.quick_guard,
2632 self.reflect,
2633 self.safeguard,
2634 self.spikes,
2635 self.stealth_rock,
2636 self.sticky_web,
2637 self.tailwind,
2638 self.toxic_count,
2639 self.toxic_spikes,
2640 self.wide_guard,
2641 )
2642 }
2643 pub fn deserialize(serialized: &str) -> SideConditions {
2644 let split: Vec<&str> = serialized.split(";").collect();
2645 SideConditions {
2646 aurora_veil: split[0].parse::<i8>().unwrap(),
2647 crafty_shield: split[1].parse::<i8>().unwrap(),
2648 healing_wish: split[2].parse::<i8>().unwrap(),
2649 light_screen: split[3].parse::<i8>().unwrap(),
2650 lucky_chant: split[4].parse::<i8>().unwrap(),
2651 lunar_dance: split[5].parse::<i8>().unwrap(),
2652 mat_block: split[6].parse::<i8>().unwrap(),
2653 mist: split[7].parse::<i8>().unwrap(),
2654 protect: split[8].parse::<i8>().unwrap(),
2655 quick_guard: split[9].parse::<i8>().unwrap(),
2656 reflect: split[10].parse::<i8>().unwrap(),
2657 safeguard: split[11].parse::<i8>().unwrap(),
2658 spikes: split[12].parse::<i8>().unwrap(),
2659 stealth_rock: split[13].parse::<i8>().unwrap(),
2660 sticky_web: split[14].parse::<i8>().unwrap(),
2661 tailwind: split[15].parse::<i8>().unwrap(),
2662 toxic_count: split[16].parse::<i8>().unwrap(),
2663 toxic_spikes: split[17].parse::<i8>().unwrap(),
2664 wide_guard: split[18].parse::<i8>().unwrap(),
2665 }
2666 }
2667}
2668
2669impl Side {
2670 pub fn serialize(&self) -> String {
2671 let mut vs_string = String::new();
2672 for vs in &self.volatile_statuses {
2673 vs_string.push_str(&vs.to_string());
2674 vs_string.push_str(":");
2675 }
2676 format!(
2677 "{}={}={}={}={}={}={}={}={}={}={}={}={}={}={}={}={}={}={}={}={}={}={}={}={}={}={}",
2678 self.pokemon.p0.serialize(),
2679 self.pokemon.p1.serialize(),
2680 self.pokemon.p2.serialize(),
2681 self.pokemon.p3.serialize(),
2682 self.pokemon.p4.serialize(),
2683 self.pokemon.p5.serialize(),
2684 self.active_index.serialize(),
2685 self.side_conditions.serialize(),
2686 vs_string,
2687 self.substitute_health,
2688 self.attack_boost,
2689 self.defense_boost,
2690 self.special_attack_boost,
2691 self.special_defense_boost,
2692 self.speed_boost,
2693 self.accuracy_boost,
2694 self.evasion_boost,
2695 self.wish.0,
2696 self.wish.1,
2697 self.future_sight.0,
2698 self.future_sight.1.serialize(),
2699 self.force_switch,
2700 self.switch_out_move_second_saved_move.to_string(),
2701 self.baton_passing,
2702 self.force_trapped,
2703 self.last_used_move.serialize(),
2704 self.slow_uturn_move,
2705 )
2706 }
2707 pub fn deserialize(serialized: &str) -> Side {
2708 let split: Vec<&str> = serialized.split("=").collect();
2709
2710 let mut vs_hashset = HashSet::new();
2711 if split[8] != "" {
2712 for item in split[8].split(":") {
2713 vs_hashset.insert(PokemonVolatileStatus::from_str(item).unwrap());
2714 }
2715 }
2716 Side {
2717 pokemon: SidePokemon {
2718 p0: Pokemon::deserialize(split[0]),
2719 p1: Pokemon::deserialize(split[1]),
2720 p2: Pokemon::deserialize(split[2]),
2721 p3: Pokemon::deserialize(split[3]),
2722 p4: Pokemon::deserialize(split[4]),
2723 p5: Pokemon::deserialize(split[5]),
2724 },
2725 active_index: PokemonIndex::deserialize(split[6]),
2726 side_conditions: SideConditions::deserialize(split[7]),
2727 volatile_statuses: vs_hashset,
2728 substitute_health: split[9].parse::<i16>().unwrap(),
2729 attack_boost: split[10].parse::<i8>().unwrap(),
2730 defense_boost: split[11].parse::<i8>().unwrap(),
2731 special_attack_boost: split[12].parse::<i8>().unwrap(),
2732 special_defense_boost: split[13].parse::<i8>().unwrap(),
2733 speed_boost: split[14].parse::<i8>().unwrap(),
2734 accuracy_boost: split[15].parse::<i8>().unwrap(),
2735 evasion_boost: split[16].parse::<i8>().unwrap(),
2736 wish: (
2737 split[17].parse::<i8>().unwrap(),
2738 split[18].parse::<i16>().unwrap(),
2739 ),
2740 future_sight: (
2741 split[19].parse::<i8>().unwrap(),
2742 PokemonIndex::deserialize(split[20]),
2743 ),
2744 force_switch: split[21].parse::<bool>().unwrap(),
2745 switch_out_move_second_saved_move: Choices::from_str(split[22]).unwrap(),
2746 baton_passing: split[23].parse::<bool>().unwrap(),
2747 force_trapped: split[24].parse::<bool>().unwrap(),
2748 last_used_move: LastUsedMove::deserialize(split[25]),
2749 damage_dealt: DamageDealt::default(),
2750 slow_uturn_move: split[26].parse::<bool>().unwrap(),
2751 }
2752 }
2753}
2754
2755impl StateWeather {
2756 pub fn serialize(&self) -> String {
2757 format!("{:?};{}", self.weather_type, self.turns_remaining)
2758 }
2759 pub fn deserialize(serialized: &str) -> StateWeather {
2760 let split: Vec<&str> = serialized.split(";").collect();
2761 StateWeather {
2762 weather_type: Weather::from_str(split[0]).unwrap(),
2763 turns_remaining: split[1].parse::<i8>().unwrap(),
2764 }
2765 }
2766}
2767
2768impl StateTerrain {
2769 pub fn serialize(&self) -> String {
2770 format!("{:?};{}", self.terrain_type, self.turns_remaining)
2771 }
2772 pub fn deserialize(serialized: &str) -> StateTerrain {
2773 let split: Vec<&str> = serialized.split(";").collect();
2774 StateTerrain {
2775 terrain_type: Terrain::from_str(split[0]).unwrap(),
2776 turns_remaining: split[1].parse::<i8>().unwrap(),
2777 }
2778 }
2779}
2780
2781impl StateTrickRoom {
2782 pub fn serialize(&self) -> String {
2783 format!("{};{}", self.active, self.turns_remaining)
2784 }
2785 pub fn deserialize(serialized: &str) -> StateTrickRoom {
2786 let split: Vec<&str> = serialized.split(";").collect();
2787 StateTrickRoom {
2788 active: split[0].parse::<bool>().unwrap(),
2789 turns_remaining: split[1].parse::<i8>().unwrap(),
2790 }
2791 }
2792}
2793
2794impl State {
2795 pub fn serialize(&self) -> String {
2796 format!(
2797 "{}/{}/{}/{}/{}/{}",
2798 self.side_one.serialize(),
2799 self.side_two.serialize(),
2800 self.weather.serialize(),
2801 self.terrain.serialize(),
2802 self.trick_room.serialize(),
2803 self.team_preview
2804 )
2805 }
2806
2807 pub fn deserialize(serialized: &str) -> State {
2993 let split: Vec<&str> = serialized.split("/").collect();
2994 let mut state = State {
2995 side_one: Side::deserialize(split[0]),
2996 side_two: Side::deserialize(split[1]),
2997 weather: StateWeather::deserialize(split[2]),
2998 terrain: StateTerrain::deserialize(split[3]),
2999 trick_room: StateTrickRoom::deserialize(split[4]),
3000 team_preview: split[5].parse::<bool>().unwrap(),
3001 use_damage_dealt: false,
3002 use_last_used_move: false,
3003 };
3004 state.set_conditional_mechanics();
3005 state
3006 }
3007}