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::LUSTROUSGLOBE => self.id == PokemonName::PALKIAORIGIN,
720 Items::GRISEOUSCORE => self.id == PokemonName::GIRATINAORIGIN,
721 Items::ADAMANTCRYSTAL => self.id == PokemonName::DIALGAORIGIN,
722 Items::RUSTEDSWORD => {
723 self.id == PokemonName::ZACIANCROWNED || self.id == PokemonName::ZACIAN
724 }
725 Items::RUSTEDSHIELD => {
726 self.id == PokemonName::ZAMAZENTACROWNED || self.id == PokemonName::ZAMAZENTA
727 }
728 Items::SPLASHPLATE => self.id == PokemonName::ARCEUSWATER,
729 Items::TOXICPLATE => self.id == PokemonName::ARCEUSPOISON,
730 Items::EARTHPLATE => self.id == PokemonName::ARCEUSGROUND,
731 Items::STONEPLATE => self.id == PokemonName::ARCEUSROCK,
732 Items::INSECTPLATE => self.id == PokemonName::ARCEUSBUG,
733 Items::SPOOKYPLATE => self.id == PokemonName::ARCEUSGHOST,
734 Items::IRONPLATE => self.id == PokemonName::ARCEUSSTEEL,
735 Items::FLAMEPLATE => self.id == PokemonName::ARCEUSFIRE,
736 Items::MEADOWPLATE => self.id == PokemonName::ARCEUSGRASS,
737 Items::ZAPPLATE => self.id == PokemonName::ARCEUSELECTRIC,
738 Items::MINDPLATE => self.id == PokemonName::ARCEUSPSYCHIC,
739 Items::ICICLEPLATE => self.id == PokemonName::ARCEUSICE,
740 Items::DRACOPLATE => self.id == PokemonName::ARCEUSDRAGON,
741 Items::DREADPLATE => self.id == PokemonName::ARCEUSDARK,
742 Items::FISTPLATE => self.id == PokemonName::ARCEUSFIGHTING,
743 Items::BLANKPLATE => self.id == PokemonName::ARCEUS,
744 Items::SKYPLATE => self.id == PokemonName::ARCEUSFLYING,
745 Items::PIXIEPLATE => self.id == PokemonName::ARCEUSFAIRY,
746 Items::BUGMEMORY => self.id == PokemonName::SILVALLYBUG,
747 Items::FIGHTINGMEMORY => self.id == PokemonName::SILVALLYFIGHTING,
748 Items::GHOSTMEMORY => self.id == PokemonName::SILVALLYGHOST,
749 Items::PSYCHICMEMORY => self.id == PokemonName::SILVALLYPSYCHIC,
750 Items::FLYINGMEMORY => self.id == PokemonName::SILVALLYFLYING,
751 Items::STEELMEMORY => self.id == PokemonName::SILVALLYSTEEL,
752 Items::ICEMEMORY => self.id == PokemonName::SILVALLYICE,
753 Items::POISONMEMORY => self.id == PokemonName::SILVALLYPOISON,
754 Items::FIREMEMORY => self.id == PokemonName::SILVALLYFIRE,
755 Items::DRAGONMEMORY => self.id == PokemonName::SILVALLYDRAGON,
756 Items::GROUNDMEMORY => self.id == PokemonName::SILVALLYGROUND,
757 Items::WATERMEMORY => self.id == PokemonName::SILVALLYWATER,
758 Items::DARKMEMORY => self.id == PokemonName::SILVALLYDARK,
759 Items::ROCKMEMORY => self.id == PokemonName::SILVALLYROCK,
760 Items::GRASSMEMORY => self.id == PokemonName::SILVALLYGRASS,
761 Items::FAIRYMEMORY => self.id == PokemonName::SILVALLYFAIRY,
762 Items::ELECTRICMEMORY => self.id == PokemonName::SILVALLYELECTRIC,
763 Items::CORNERSTONEMASK => {
764 self.id == PokemonName::OGERPONCORNERSTONE
765 || self.id == PokemonName::OGERPONCORNERSTONETERA
766 }
767 Items::HEARTHFLAMEMASK => {
768 self.id == PokemonName::OGERPONHEARTHFLAME
769 || self.id == PokemonName::OGERPONHEARTHFLAMETERA
770 }
771 Items::WELLSPRINGMASK => {
772 self.id == PokemonName::OGERPONWELLSPRING
773 || self.id == PokemonName::OGERPONWELLSPRINGTERA
774 }
775 _ => false,
776 }
777 }
778
779 pub fn item_can_be_removed(&self) -> bool {
780 if self.ability == Abilities::STICKYHOLD {
781 return false;
782 }
783 !self.item_is_permanent()
784 }
785
786 pub fn is_grounded(&self) -> bool {
787 if self.item == Items::IRONBALL {
788 return true;
789 }
790 if self.has_type(&PokemonType::FLYING)
791 || self.ability == Abilities::LEVITATE
792 || self.item == Items::AIRBALLOON
793 {
794 return false;
795 }
796 true
797 }
798
799 pub fn volatile_status_can_be_applied(
800 &self,
801 volatile_status: &PokemonVolatileStatus,
802 active_volatiles: &HashSet<PokemonVolatileStatus>,
803 first_move: bool,
804 ) -> bool {
805 if active_volatiles.contains(volatile_status) || self.hp == 0 {
806 return false;
807 }
808 match volatile_status {
809 PokemonVolatileStatus::LEECHSEED => {
810 if self.has_type(&PokemonType::GRASS)
811 || active_volatiles.contains(&PokemonVolatileStatus::SUBSTITUTE)
812 {
813 return false;
814 }
815 true
816 }
817 PokemonVolatileStatus::CONFUSION => {
818 if active_volatiles.contains(&PokemonVolatileStatus::SUBSTITUTE) {
819 return false;
820 }
821 true
822 }
823 PokemonVolatileStatus::SUBSTITUTE => self.hp > self.maxhp / 4,
824 PokemonVolatileStatus::FLINCH => {
825 if !first_move || [Abilities::INNERFOCUS].contains(&self.ability) {
826 return false;
827 }
828 true
829 }
830 PokemonVolatileStatus::PROTECT => first_move,
831 PokemonVolatileStatus::TAUNT
832 | PokemonVolatileStatus::TORMENT
833 | PokemonVolatileStatus::ENCORE
834 | PokemonVolatileStatus::DISABLE
835 | PokemonVolatileStatus::HEALBLOCK
836 | PokemonVolatileStatus::ATTRACT => self.ability != Abilities::AROMAVEIL,
837 PokemonVolatileStatus::YAWN => {
838 !active_volatiles.contains(&PokemonVolatileStatus::YAWNSLEEPTHISTURN)
840 }
841 _ => true,
842 }
843 }
844
845 pub fn immune_to_stats_lowered_by_opponent(
846 &self,
847 stat: &PokemonBoostableStat,
848 volatiles: &HashSet<PokemonVolatileStatus>,
849 ) -> bool {
850 if [
851 Abilities::CLEARBODY,
852 Abilities::WHITESMOKE,
853 Abilities::FULLMETALBODY,
854 ]
855 .contains(&self.ability)
856 || ([Items::CLEARAMULET].contains(&self.item))
857 {
858 return true;
859 }
860
861 if volatiles.contains(&PokemonVolatileStatus::SUBSTITUTE) {
862 return true;
863 }
864
865 if stat == &PokemonBoostableStat::Attack && self.ability == Abilities::HYPERCUTTER {
866 return true;
867 } else if stat == &PokemonBoostableStat::Accuracy && self.ability == Abilities::KEENEYE {
868 return true;
869 }
870
871 false
872 }
873}
874
875impl Default for Pokemon {
876 fn default() -> Pokemon {
877 Pokemon {
878 id: PokemonName::NONE,
879 level: 100,
880 types: (PokemonType::NORMAL, PokemonType::TYPELESS),
881 base_types: (PokemonType::NORMAL, PokemonType::TYPELESS),
882 hp: 100,
883 maxhp: 100,
884 ability: Abilities::NONE,
885 base_ability: Abilities::NONE,
886 item: Items::NONE,
887 nature: PokemonNature::SERIOUS,
888 evs: (85, 85, 85, 85, 85, 85),
889 attack: 100,
890 defense: 100,
891 special_attack: 100,
892 special_defense: 100,
893 speed: 100,
894 status: PokemonStatus::NONE,
895 rest_turns: 0,
896 sleep_turns: 0,
897 weight_kg: 1.0,
898 terastallized: false,
899 tera_type: PokemonType::NORMAL,
900 moves: PokemonMoves {
901 m0: Default::default(),
902 m1: Default::default(),
903 m2: Default::default(),
904 m3: Default::default(),
905 },
906 }
907 }
908}
909
910#[derive(Debug, Copy, PartialEq, Clone, Eq, Hash)]
911pub enum PokemonIndex {
912 P0,
913 P1,
914 P2,
915 P3,
916 P4,
917 P5,
918}
919
920pub fn pokemon_index_iter() -> PokemonIndexIterator {
921 PokemonIndexIterator { index: 0 }
922}
923
924pub struct PokemonIndexIterator {
925 index: usize,
926}
927
928impl Iterator for PokemonIndexIterator {
929 type Item = PokemonIndex;
930
931 fn next(&mut self) -> Option<Self::Item> {
932 match self.index {
933 0 => {
934 self.index += 1;
935 Some(PokemonIndex::P0)
936 }
937 1 => {
938 self.index += 1;
939 Some(PokemonIndex::P1)
940 }
941 2 => {
942 self.index += 1;
943 Some(PokemonIndex::P2)
944 }
945 3 => {
946 self.index += 1;
947 Some(PokemonIndex::P3)
948 }
949 4 => {
950 self.index += 1;
951 Some(PokemonIndex::P4)
952 }
953 5 => {
954 self.index += 1;
955 Some(PokemonIndex::P5)
956 }
957 _ => None,
958 }
959 }
960}
961
962#[derive(Debug, Clone)]
963pub struct SidePokemon {
964 pub p0: Pokemon,
965 pub p1: Pokemon,
966 pub p2: Pokemon,
967 pub p3: Pokemon,
968 pub p4: Pokemon,
969 pub p5: Pokemon,
970}
971
972impl<'a> IntoIterator for &'a SidePokemon {
973 type Item = &'a Pokemon;
974 type IntoIter = SidePokemonIterator<'a>;
975
976 fn into_iter(self) -> Self::IntoIter {
977 SidePokemonIterator {
978 side_pokemon: &self,
979 pokemon_index: PokemonIndex::P0,
980 index: 0,
981 }
982 }
983}
984
985pub struct SidePokemonIterator<'a> {
986 pub side_pokemon: &'a SidePokemon,
987 pub pokemon_index: PokemonIndex,
988 pub index: usize,
989}
990
991impl<'a> Iterator for SidePokemonIterator<'a> {
992 type Item = &'a Pokemon;
993
994 fn next(&mut self) -> Option<Self::Item> {
995 match self.index {
996 0 => {
997 self.index += 1;
998 self.pokemon_index = PokemonIndex::P0;
999 Some(&self.side_pokemon.p0)
1000 }
1001 1 => {
1002 self.index += 1;
1003 self.pokemon_index = PokemonIndex::P1;
1004 Some(&self.side_pokemon.p1)
1005 }
1006 2 => {
1007 self.index += 1;
1008 self.pokemon_index = PokemonIndex::P2;
1009 Some(&self.side_pokemon.p2)
1010 }
1011 3 => {
1012 self.index += 1;
1013 self.pokemon_index = PokemonIndex::P3;
1014 Some(&self.side_pokemon.p3)
1015 }
1016 4 => {
1017 self.index += 1;
1018 self.pokemon_index = PokemonIndex::P4;
1019 Some(&self.side_pokemon.p4)
1020 }
1021 5 => {
1022 self.index += 1;
1023 self.pokemon_index = PokemonIndex::P5;
1024 Some(&self.side_pokemon.p5)
1025 }
1026 _ => None,
1027 }
1028 }
1029}
1030
1031impl Index<PokemonIndex> for SidePokemon {
1032 type Output = Pokemon;
1033
1034 fn index(&self, index: PokemonIndex) -> &Self::Output {
1035 match index {
1036 PokemonIndex::P0 => &self.p0,
1037 PokemonIndex::P1 => &self.p1,
1038 PokemonIndex::P2 => &self.p2,
1039 PokemonIndex::P3 => &self.p3,
1040 PokemonIndex::P4 => &self.p4,
1041 PokemonIndex::P5 => &self.p5,
1042 }
1043 }
1044}
1045
1046impl Index<&PokemonIndex> for SidePokemon {
1047 type Output = Pokemon;
1048
1049 fn index(&self, index: &PokemonIndex) -> &Self::Output {
1050 match index {
1051 PokemonIndex::P0 => &self.p0,
1052 PokemonIndex::P1 => &self.p1,
1053 PokemonIndex::P2 => &self.p2,
1054 PokemonIndex::P3 => &self.p3,
1055 PokemonIndex::P4 => &self.p4,
1056 PokemonIndex::P5 => &self.p5,
1057 }
1058 }
1059}
1060
1061impl IndexMut<PokemonIndex> for SidePokemon {
1062 fn index_mut(&mut self, index: PokemonIndex) -> &mut Self::Output {
1063 match index {
1064 PokemonIndex::P0 => &mut self.p0,
1065 PokemonIndex::P1 => &mut self.p1,
1066 PokemonIndex::P2 => &mut self.p2,
1067 PokemonIndex::P3 => &mut self.p3,
1068 PokemonIndex::P4 => &mut self.p4,
1069 PokemonIndex::P5 => &mut self.p5,
1070 }
1071 }
1072}
1073
1074#[derive(Debug, Clone)]
1075pub struct Side {
1076 pub active_index: PokemonIndex,
1077 pub baton_passing: bool,
1078 pub pokemon: SidePokemon,
1079 pub side_conditions: SideConditions,
1080 pub wish: (i8, i16),
1081 pub future_sight: (i8, PokemonIndex),
1082 pub force_switch: bool,
1083 pub force_trapped: bool,
1084 pub slow_uturn_move: bool,
1085 pub volatile_statuses: HashSet<PokemonVolatileStatus>,
1086 pub substitute_health: i16,
1087 pub attack_boost: i8,
1088 pub defense_boost: i8,
1089 pub special_attack_boost: i8,
1090 pub special_defense_boost: i8,
1091 pub speed_boost: i8,
1092 pub accuracy_boost: i8,
1093 pub evasion_boost: i8,
1094 pub last_used_move: LastUsedMove,
1095 pub damage_dealt: DamageDealt,
1096 pub switch_out_move_second_saved_move: Choices,
1097}
1098
1099impl Side {
1100 pub fn calculate_highest_stat(&self) -> PokemonBoostableStat {
1101 let mut highest_stat = PokemonBoostableStat::Attack;
1102 let mut highest_stat_value = self.calculate_boosted_stat(PokemonBoostableStat::Attack);
1103 for stat in [
1104 PokemonBoostableStat::Defense,
1105 PokemonBoostableStat::SpecialAttack,
1106 PokemonBoostableStat::SpecialDefense,
1107 PokemonBoostableStat::Speed,
1108 ] {
1109 let stat_value = self.calculate_boosted_stat(stat);
1110 if stat_value > highest_stat_value {
1111 highest_stat = stat;
1112 highest_stat_value = stat_value;
1113 }
1114 }
1115 highest_stat
1116 }
1117 pub fn get_boost_from_boost_enum(&self, boost_enum: &PokemonBoostableStat) -> i8 {
1118 match boost_enum {
1119 PokemonBoostableStat::Attack => self.attack_boost,
1120 PokemonBoostableStat::Defense => self.defense_boost,
1121 PokemonBoostableStat::SpecialAttack => self.special_attack_boost,
1122 PokemonBoostableStat::SpecialDefense => self.special_defense_boost,
1123 PokemonBoostableStat::Speed => self.speed_boost,
1124 PokemonBoostableStat::Evasion => self.evasion_boost,
1125 PokemonBoostableStat::Accuracy => self.accuracy_boost,
1126 }
1127 }
1128
1129 pub fn calculate_boosted_stat(&self, stat: PokemonBoostableStat) -> i16 {
1130 let active = self.get_active_immutable();
1135 match stat {
1136 PokemonBoostableStat::Attack => {
1137 #[cfg(feature = "gen4")]
1138 let boost = if active.ability == Abilities::SIMPLE {
1139 (self.attack_boost * 2).min(6).max(-6)
1140 } else {
1141 self.attack_boost
1142 };
1143
1144 #[cfg(not(feature = "gen4"))]
1145 let boost = self.attack_boost;
1146
1147 multiply_boost(boost, active.attack)
1148 }
1149 PokemonBoostableStat::Defense => {
1150 #[cfg(feature = "gen4")]
1151 let boost = if active.ability == Abilities::SIMPLE {
1152 (self.defense_boost * 2).min(6).max(-6)
1153 } else {
1154 self.defense_boost
1155 };
1156 #[cfg(not(feature = "gen4"))]
1157 let boost = self.defense_boost;
1158
1159 multiply_boost(boost, active.defense)
1160 }
1161 PokemonBoostableStat::SpecialAttack => {
1162 #[cfg(feature = "gen4")]
1163 let boost = if active.ability == Abilities::SIMPLE {
1164 (self.special_attack_boost * 2).min(6).max(-6)
1165 } else {
1166 self.special_attack_boost
1167 };
1168 #[cfg(not(feature = "gen4"))]
1169 let boost = self.special_attack_boost;
1170
1171 multiply_boost(boost, active.special_attack)
1172 }
1173 PokemonBoostableStat::SpecialDefense => {
1174 #[cfg(feature = "gen4")]
1175 let boost = if active.ability == Abilities::SIMPLE {
1176 (self.special_defense_boost * 2).min(6).max(-6)
1177 } else {
1178 self.special_defense_boost
1179 };
1180 #[cfg(not(feature = "gen4"))]
1181 let boost = self.special_defense_boost;
1182
1183 multiply_boost(boost, active.special_defense)
1184 }
1185 PokemonBoostableStat::Speed => {
1186 #[cfg(feature = "gen4")]
1187 let boost = if active.ability == Abilities::SIMPLE {
1188 (self.speed_boost * 2).min(6).max(-6)
1189 } else {
1190 self.speed_boost
1191 };
1192 #[cfg(not(feature = "gen4"))]
1193 let boost = self.speed_boost;
1194
1195 multiply_boost(boost, active.speed)
1196 }
1197 _ => {
1198 panic!("Not implemented")
1199 }
1200 }
1201 }
1202
1203 pub fn has_alive_non_rested_sleeping_pkmn(&self) -> bool {
1204 for p in self.pokemon.into_iter() {
1205 if p.status == PokemonStatus::SLEEP && p.hp > 0 && p.rest_turns == 0 {
1206 return true;
1207 }
1208 }
1209 false
1210 }
1211
1212 #[cfg(not(feature = "terastallization"))]
1213 pub fn can_use_tera(&self) -> bool {
1214 false
1215 }
1216
1217 #[cfg(feature = "terastallization")]
1218 pub fn can_use_tera(&self) -> bool {
1219 for p in self.pokemon.into_iter() {
1220 if p.terastallized {
1221 return false;
1222 }
1223 }
1224 true
1225 }
1226
1227 fn toggle_force_switch(&mut self) {
1228 self.force_switch = !self.force_switch;
1229 }
1230
1231 pub fn add_switches(&self, vec: &mut Vec<MoveChoice>) {
1232 let mut iter = self.pokemon.into_iter();
1233 while let Some(p) = iter.next() {
1234 if p.hp > 0 && iter.pokemon_index != self.active_index {
1235 vec.push(MoveChoice::Switch(iter.pokemon_index));
1236 }
1237 }
1238 if vec.len() == 0 {
1239 vec.push(MoveChoice::None);
1240 }
1241 }
1242
1243 pub fn trapped(&self, opponent_active: &Pokemon) -> bool {
1244 let active_pkmn = self.get_active_immutable();
1245 if self
1246 .volatile_statuses
1247 .contains(&PokemonVolatileStatus::LOCKEDMOVE)
1248 {
1249 return true;
1250 }
1251 if active_pkmn.item == Items::SHEDSHELL || active_pkmn.has_type(&PokemonType::GHOST) {
1252 return false;
1253 } else if self
1254 .volatile_statuses
1255 .contains(&PokemonVolatileStatus::PARTIALLYTRAPPED)
1256 {
1257 return true;
1258 } else if opponent_active.ability == Abilities::SHADOWTAG {
1259 return true;
1260 } else if opponent_active.ability == Abilities::ARENATRAP && active_pkmn.is_grounded() {
1261 return true;
1262 } else if opponent_active.ability == Abilities::MAGNETPULL
1263 && active_pkmn.has_type(&PokemonType::STEEL)
1264 {
1265 return true;
1266 }
1267 false
1268 }
1269
1270 pub fn get_active(&mut self) -> &mut Pokemon {
1271 &mut self.pokemon[self.active_index]
1272 }
1273
1274 pub fn get_active_immutable(&self) -> &Pokemon {
1275 &self.pokemon[self.active_index]
1276 }
1277
1278 pub fn num_fainted_pkmn(&self) -> i8 {
1279 let mut count = 0;
1280 for p in self.pokemon.into_iter() {
1281 if p.hp == 0 && p.level != 1 {
1282 count += 1;
1283 }
1284 }
1285 count
1286 }
1287
1288 pub fn visible_alive_pkmn(&self) -> i8 {
1289 let mut count = 0;
1290 for p in self.pokemon.into_iter() {
1291 if p.hp > 0 {
1292 count += 1;
1293 }
1294 }
1295 count
1296 }
1297
1298 pub fn get_alive_pkmn_indices(&self) -> Vec<PokemonIndex> {
1299 let mut vec = Vec::with_capacity(6);
1300 let mut iter = self.pokemon.into_iter();
1301
1302 while let Some(p) = iter.next() {
1303 if p.hp > 0 && iter.pokemon_index != self.active_index {
1304 vec.push(iter.pokemon_index.clone());
1305 }
1306 }
1307
1308 vec
1309 }
1310
1311 pub fn get_side_condition(&self, side_condition: PokemonSideCondition) -> i8 {
1312 match side_condition {
1313 PokemonSideCondition::AuroraVeil => self.side_conditions.aurora_veil,
1314 PokemonSideCondition::CraftyShield => self.side_conditions.crafty_shield,
1315 PokemonSideCondition::HealingWish => self.side_conditions.healing_wish,
1316 PokemonSideCondition::LightScreen => self.side_conditions.light_screen,
1317 PokemonSideCondition::LuckyChant => self.side_conditions.lucky_chant,
1318 PokemonSideCondition::LunarDance => self.side_conditions.lunar_dance,
1319 PokemonSideCondition::MatBlock => self.side_conditions.mat_block,
1320 PokemonSideCondition::Mist => self.side_conditions.mist,
1321 PokemonSideCondition::Protect => self.side_conditions.protect,
1322 PokemonSideCondition::QuickGuard => self.side_conditions.quick_guard,
1323 PokemonSideCondition::Reflect => self.side_conditions.reflect,
1324 PokemonSideCondition::Safeguard => self.side_conditions.safeguard,
1325 PokemonSideCondition::Spikes => self.side_conditions.spikes,
1326 PokemonSideCondition::Stealthrock => self.side_conditions.stealth_rock,
1327 PokemonSideCondition::StickyWeb => self.side_conditions.sticky_web,
1328 PokemonSideCondition::Tailwind => self.side_conditions.tailwind,
1329 PokemonSideCondition::ToxicCount => self.side_conditions.toxic_count,
1330 PokemonSideCondition::ToxicSpikes => self.side_conditions.toxic_spikes,
1331 PokemonSideCondition::WideGuard => self.side_conditions.wide_guard,
1332 }
1333 }
1334
1335 pub fn update_side_condition(&mut self, side_condition: PokemonSideCondition, amount: i8) {
1336 match side_condition {
1337 PokemonSideCondition::AuroraVeil => self.side_conditions.aurora_veil += amount,
1338 PokemonSideCondition::CraftyShield => self.side_conditions.crafty_shield += amount,
1339 PokemonSideCondition::HealingWish => self.side_conditions.healing_wish += amount,
1340 PokemonSideCondition::LightScreen => self.side_conditions.light_screen += amount,
1341 PokemonSideCondition::LuckyChant => self.side_conditions.lucky_chant += amount,
1342 PokemonSideCondition::LunarDance => self.side_conditions.lunar_dance += amount,
1343 PokemonSideCondition::MatBlock => self.side_conditions.mat_block += amount,
1344 PokemonSideCondition::Mist => self.side_conditions.mist += amount,
1345 PokemonSideCondition::Protect => self.side_conditions.protect += amount,
1346 PokemonSideCondition::QuickGuard => self.side_conditions.quick_guard += amount,
1347 PokemonSideCondition::Reflect => self.side_conditions.reflect += amount,
1348 PokemonSideCondition::Safeguard => self.side_conditions.safeguard += amount,
1349 PokemonSideCondition::Spikes => self.side_conditions.spikes += amount,
1350 PokemonSideCondition::Stealthrock => self.side_conditions.stealth_rock += amount,
1351 PokemonSideCondition::StickyWeb => self.side_conditions.sticky_web += amount,
1352 PokemonSideCondition::Tailwind => self.side_conditions.tailwind += amount,
1353 PokemonSideCondition::ToxicCount => self.side_conditions.toxic_count += amount,
1354 PokemonSideCondition::ToxicSpikes => self.side_conditions.toxic_spikes += amount,
1355 PokemonSideCondition::WideGuard => self.side_conditions.wide_guard += amount,
1356 }
1357 }
1358}
1359
1360impl Default for Side {
1361 fn default() -> Side {
1362 Side {
1363 active_index: PokemonIndex::P0,
1364 baton_passing: false,
1365 pokemon: SidePokemon {
1366 p0: Pokemon {
1367 ..Pokemon::default()
1368 },
1369 p1: Pokemon {
1370 ..Pokemon::default()
1371 },
1372 p2: Pokemon {
1373 ..Pokemon::default()
1374 },
1375 p3: Pokemon {
1376 ..Pokemon::default()
1377 },
1378 p4: Pokemon {
1379 ..Pokemon::default()
1380 },
1381 p5: Pokemon {
1382 ..Pokemon::default()
1383 },
1384 },
1385 substitute_health: 0,
1386 attack_boost: 0,
1387 defense_boost: 0,
1388 special_attack_boost: 0,
1389 special_defense_boost: 0,
1390 speed_boost: 0,
1391 accuracy_boost: 0,
1392 side_conditions: SideConditions {
1393 ..Default::default()
1394 },
1395 volatile_statuses: HashSet::<PokemonVolatileStatus>::new(),
1396 wish: (0, 0),
1397 future_sight: (0, PokemonIndex::P0),
1398 force_switch: false,
1399 slow_uturn_move: false,
1400 force_trapped: false,
1401 last_used_move: LastUsedMove::None,
1402 damage_dealt: DamageDealt::default(),
1403 switch_out_move_second_saved_move: Choices::NONE,
1404 evasion_boost: 0,
1405 }
1406 }
1407}
1408
1409#[derive(Debug, Clone)]
1410pub struct State {
1411 pub side_one: Side,
1412 pub side_two: Side,
1413 pub weather: StateWeather,
1414 pub terrain: StateTerrain,
1415 pub trick_room: StateTrickRoom,
1416 pub team_preview: bool,
1417 pub use_last_used_move: bool,
1418 pub use_damage_dealt: bool,
1419}
1420
1421impl Default for State {
1422 fn default() -> State {
1423 let mut s = State {
1424 side_one: Side::default(),
1425 side_two: Side::default(),
1426 weather: StateWeather {
1427 weather_type: Weather::NONE,
1428 turns_remaining: -1,
1429 },
1430 terrain: StateTerrain {
1431 terrain_type: Terrain::NONE,
1432 turns_remaining: 0,
1433 },
1434 trick_room: StateTrickRoom {
1435 active: false,
1436 turns_remaining: 0,
1437 },
1438 team_preview: false,
1439 use_damage_dealt: false,
1440 use_last_used_move: false,
1441 };
1442
1443 s.side_two.get_active().speed += 1;
1445 s
1446 }
1447}
1448
1449impl State {
1450 pub fn battle_is_over(&self) -> f32 {
1451 if self.side_one.pokemon.into_iter().all(|p| p.hp <= 0) {
1455 return -1.0;
1456 }
1457 if self
1459 .side_two
1460 .pokemon
1461 .into_iter()
1462 .all(|p| p.hp <= 0 && p.level != 1)
1463 {
1464 return 1.0;
1465 }
1466 0.0
1467 }
1468
1469 pub fn get_all_options(&self) -> (Vec<MoveChoice>, Vec<MoveChoice>) {
1470 let mut side_one_options: Vec<MoveChoice> = Vec::with_capacity(9);
1471 let mut side_two_options: Vec<MoveChoice> = Vec::with_capacity(9);
1472
1473 let side_one_active = self.side_one.get_active_immutable();
1474 let side_two_active = self.side_two.get_active_immutable();
1475
1476 if self.side_one.force_switch {
1477 self.side_one.add_switches(&mut side_one_options);
1478 if self.side_two.switch_out_move_second_saved_move == Choices::NONE {
1479 side_two_options.push(MoveChoice::None);
1480 } else {
1481 self.side_two.get_active_immutable().add_move_from_choice(
1482 &mut side_two_options,
1483 self.side_two.switch_out_move_second_saved_move,
1484 );
1485 }
1486 return (side_one_options, side_two_options);
1487 }
1488
1489 if self.side_two.force_switch {
1490 self.side_two.add_switches(&mut side_two_options);
1491 if self.side_one.switch_out_move_second_saved_move == Choices::NONE {
1492 side_one_options.push(MoveChoice::None);
1493 } else {
1494 self.side_one.get_active_immutable().add_move_from_choice(
1495 &mut side_one_options,
1496 self.side_one.switch_out_move_second_saved_move,
1497 );
1498 }
1499 return (side_one_options, side_two_options);
1500 }
1501
1502 let side_one_force_switch = self.side_one.get_active_immutable().hp <= 0;
1503 let side_two_force_switch = self.side_two.get_active_immutable().hp <= 0;
1504
1505 if side_one_force_switch && side_two_force_switch {
1506 self.side_one.add_switches(&mut side_one_options);
1507 self.side_two.add_switches(&mut side_two_options);
1508 return (side_one_options, side_two_options);
1509 }
1510 if side_one_force_switch {
1511 self.side_one.add_switches(&mut side_one_options);
1512 side_two_options.push(MoveChoice::None);
1513 return (side_one_options, side_two_options);
1514 }
1515 if side_two_force_switch {
1516 side_one_options.push(MoveChoice::None);
1517 self.side_two.add_switches(&mut side_two_options);
1518 return (side_one_options, side_two_options);
1519 }
1520
1521 if self
1522 .side_one
1523 .volatile_statuses
1524 .contains(&PokemonVolatileStatus::MUSTRECHARGE)
1525 {
1526 side_one_options.push(MoveChoice::None);
1527 } else {
1528 let encored = self
1529 .side_one
1530 .volatile_statuses
1531 .contains(&PokemonVolatileStatus::ENCORE);
1532 self.side_one.get_active_immutable().add_available_moves(
1533 &mut side_one_options,
1534 &self.side_one.last_used_move,
1535 encored,
1536 self.side_one.can_use_tera(),
1537 );
1538 if !self.side_one.trapped(side_two_active) {
1539 self.side_one.add_switches(&mut side_one_options);
1540 }
1541 }
1542
1543 if self
1544 .side_two
1545 .volatile_statuses
1546 .contains(&PokemonVolatileStatus::MUSTRECHARGE)
1547 {
1548 side_two_options.push(MoveChoice::None);
1549 } else {
1550 let encored = self
1551 .side_two
1552 .volatile_statuses
1553 .contains(&PokemonVolatileStatus::ENCORE);
1554 self.side_two.get_active_immutable().add_available_moves(
1555 &mut side_two_options,
1556 &self.side_two.last_used_move,
1557 encored,
1558 self.side_two.can_use_tera(),
1559 );
1560 if !self.side_two.trapped(side_one_active) {
1561 self.side_two.add_switches(&mut side_two_options);
1562 }
1563 }
1564
1565 if side_one_options.len() == 0 {
1566 side_one_options.push(MoveChoice::None);
1567 }
1568 if side_two_options.len() == 0 {
1569 side_two_options.push(MoveChoice::None);
1570 }
1571
1572 (side_one_options, side_two_options)
1573 }
1574
1575 pub fn get_side(&mut self, side_ref: &SideReference) -> &mut Side {
1576 match side_ref {
1577 SideReference::SideOne => &mut self.side_one,
1578 SideReference::SideTwo => &mut self.side_two,
1579 }
1580 }
1581
1582 pub fn get_side_immutable(&self, side_ref: &SideReference) -> &Side {
1583 match side_ref {
1584 SideReference::SideOne => &self.side_one,
1585 SideReference::SideTwo => &self.side_two,
1586 }
1587 }
1588
1589 pub fn get_both_sides(&mut self, side_ref: &SideReference) -> (&mut Side, &mut Side) {
1590 match side_ref {
1591 SideReference::SideOne => (&mut self.side_one, &mut self.side_two),
1592 SideReference::SideTwo => (&mut self.side_two, &mut self.side_one),
1593 }
1594 }
1595
1596 pub fn get_both_sides_immutable(&self, side_ref: &SideReference) -> (&Side, &Side) {
1597 match side_ref {
1598 SideReference::SideOne => (&self.side_one, &self.side_two),
1599 SideReference::SideTwo => (&self.side_two, &self.side_one),
1600 }
1601 }
1602
1603 pub fn re_enable_disabled_moves(
1604 &mut self,
1605 side_ref: &SideReference,
1606 vec_to_add_to: &mut Vec<Instruction>,
1607 ) {
1608 let active = self.get_side(side_ref).get_active();
1609 if active.moves.m0.disabled {
1610 active.moves.m0.disabled = false;
1611 vec_to_add_to.push(Instruction::EnableMove(EnableMoveInstruction {
1612 side_ref: *side_ref,
1613 move_index: PokemonMoveIndex::M0,
1614 }));
1615 }
1616 if active.moves.m1.disabled {
1617 active.moves.m1.disabled = false;
1618 vec_to_add_to.push(Instruction::EnableMove(EnableMoveInstruction {
1619 side_ref: *side_ref,
1620 move_index: PokemonMoveIndex::M1,
1621 }));
1622 }
1623 if active.moves.m2.disabled {
1624 active.moves.m2.disabled = false;
1625 vec_to_add_to.push(Instruction::EnableMove(EnableMoveInstruction {
1626 side_ref: *side_ref,
1627 move_index: PokemonMoveIndex::M2,
1628 }));
1629 }
1630 if active.moves.m3.disabled {
1631 active.moves.m3.disabled = false;
1632 vec_to_add_to.push(Instruction::EnableMove(EnableMoveInstruction {
1633 side_ref: *side_ref,
1634 move_index: PokemonMoveIndex::M3,
1635 }));
1636 }
1637 }
1638
1639 pub fn reset_toxic_count(
1640 &mut self,
1641 side_ref: &SideReference,
1642 vec_to_add_to: &mut Vec<Instruction>,
1643 ) {
1644 let side = self.get_side(side_ref);
1645 if side.side_conditions.toxic_count > 0 {
1646 vec_to_add_to.push(Instruction::ChangeSideCondition(
1647 ChangeSideConditionInstruction {
1648 side_ref: *side_ref,
1649 side_condition: PokemonSideCondition::ToxicCount,
1650 amount: -1 * side.side_conditions.toxic_count,
1651 },
1652 ));
1653 side.side_conditions.toxic_count = 0;
1654 }
1655 }
1656
1657 pub fn remove_volatile_statuses_on_switch(
1658 &mut self,
1659 side_ref: &SideReference,
1660 vec_to_add_to: &mut Vec<Instruction>,
1661 baton_passing: bool,
1662 ) {
1663 let side = self.get_side(side_ref);
1664
1665 let mut volatile_statuses = std::mem::take(&mut side.volatile_statuses);
1668
1669 let active = side.get_active();
1670 volatile_statuses.retain(|pkmn_volatile_status| {
1671 let should_retain = match pkmn_volatile_status {
1672 PokemonVolatileStatus::SUBSTITUTE => baton_passing,
1673 PokemonVolatileStatus::LEECHSEED => baton_passing,
1674 PokemonVolatileStatus::TYPECHANGE => {
1675 if active.base_types != active.types {
1676 vec_to_add_to.push(Instruction::ChangeType(ChangeType {
1677 side_ref: *side_ref,
1678 new_types: active.base_types,
1679 old_types: active.types,
1680 }));
1681 active.types = active.base_types;
1682 }
1683 false
1684 }
1685 _ => false,
1686 };
1687
1688 if !should_retain {
1689 vec_to_add_to.push(Instruction::RemoveVolatileStatus(
1690 RemoveVolatileStatusInstruction {
1691 side_ref: *side_ref,
1692 volatile_status: *pkmn_volatile_status,
1693 },
1694 ));
1695 }
1696 should_retain
1697 });
1698
1699 side.volatile_statuses = volatile_statuses;
1701 }
1702
1703 pub fn reset_boosts(&mut self, side_ref: &SideReference, vec_to_add_to: &mut Vec<Instruction>) {
1704 let side = self.get_side(side_ref);
1705
1706 if side.attack_boost != 0 {
1707 vec_to_add_to.push(Instruction::Boost(BoostInstruction {
1708 side_ref: *side_ref,
1709 stat: PokemonBoostableStat::Attack,
1710 amount: -1 * side.attack_boost,
1711 }));
1712 side.attack_boost = 0;
1713 }
1714
1715 if side.defense_boost != 0 {
1716 vec_to_add_to.push(Instruction::Boost(BoostInstruction {
1717 side_ref: *side_ref,
1718 stat: PokemonBoostableStat::Defense,
1719 amount: -1 * side.defense_boost,
1720 }));
1721 side.defense_boost = 0;
1722 }
1723
1724 if side.special_attack_boost != 0 {
1725 vec_to_add_to.push(Instruction::Boost(BoostInstruction {
1726 side_ref: *side_ref,
1727 stat: PokemonBoostableStat::SpecialAttack,
1728 amount: -1 * side.special_attack_boost,
1729 }));
1730 side.special_attack_boost = 0;
1731 }
1732
1733 if side.special_defense_boost != 0 {
1734 vec_to_add_to.push(Instruction::Boost(BoostInstruction {
1735 side_ref: *side_ref,
1736 stat: PokemonBoostableStat::SpecialDefense,
1737 amount: -1 * side.special_defense_boost,
1738 }));
1739 side.special_defense_boost = 0;
1740 }
1741
1742 if side.speed_boost != 0 {
1743 vec_to_add_to.push(Instruction::Boost(BoostInstruction {
1744 side_ref: *side_ref,
1745 stat: PokemonBoostableStat::Speed,
1746 amount: -1 * side.speed_boost,
1747 }));
1748 side.speed_boost = 0;
1749 }
1750
1751 if side.evasion_boost != 0 {
1752 vec_to_add_to.push(Instruction::Boost(BoostInstruction {
1753 side_ref: *side_ref,
1754 stat: PokemonBoostableStat::Evasion,
1755 amount: -1 * side.evasion_boost,
1756 }));
1757 side.evasion_boost = 0;
1758 }
1759
1760 if side.accuracy_boost != 0 {
1761 vec_to_add_to.push(Instruction::Boost(BoostInstruction {
1762 side_ref: *side_ref,
1763 stat: PokemonBoostableStat::Accuracy,
1764 amount: -1 * side.accuracy_boost,
1765 }));
1766 side.accuracy_boost = 0;
1767 }
1768 }
1769
1770 pub fn terrain_is_active(&self, terrain: &Terrain) -> bool {
1771 &self.terrain.terrain_type == terrain && self.terrain.turns_remaining > 0
1772 }
1773
1774 pub fn weather_is_active(&self, weather: &Weather) -> bool {
1775 let s1_active = self.side_one.get_active_immutable();
1776 let s2_active = self.side_two.get_active_immutable();
1777 &self.weather.weather_type == weather
1778 && s1_active.ability != Abilities::AIRLOCK
1779 && s1_active.ability != Abilities::CLOUDNINE
1780 && s2_active.ability != Abilities::AIRLOCK
1781 && s2_active.ability != Abilities::CLOUDNINE
1782 }
1783
1784 fn _state_contains_any_move(&self, moves: &[Choices]) -> bool {
1785 for s in [&self.side_one, &self.side_two] {
1786 for pkmn in s.pokemon.into_iter() {
1787 for mv in pkmn.moves.into_iter() {
1788 if moves.contains(&mv.id) {
1789 return true;
1790 }
1791 }
1792 }
1793 }
1794
1795 false
1796 }
1797
1798 pub fn set_damage_dealt_flag(&mut self) {
1799 if self._state_contains_any_move(&[
1800 Choices::COUNTER,
1801 Choices::MIRRORCOAT,
1802 Choices::METALBURST,
1803 Choices::COMEUPPANCE,
1804 Choices::FOCUSPUNCH,
1805 ]) {
1806 self.use_damage_dealt = true
1807 }
1808 }
1809
1810 pub fn set_last_used_move_flag(&mut self) {
1811 if self._state_contains_any_move(&[
1812 Choices::ENCORE,
1813 Choices::FAKEOUT,
1814 Choices::FIRSTIMPRESSION,
1815 Choices::BLOODMOON,
1816 Choices::GIGATONHAMMER,
1817 ]) {
1818 self.use_last_used_move = true
1819 }
1820 }
1821
1822 pub fn set_conditional_mechanics(&mut self) {
1823 self.set_damage_dealt_flag();
1829 self.set_last_used_move_flag();
1830 }
1831
1832 fn damage(&mut self, side_ref: &SideReference, amount: i16) {
1833 let active = self.get_side(&side_ref).get_active();
1834
1835 active.hp -= amount;
1836 }
1837
1838 fn heal(&mut self, side_ref: &SideReference, amount: i16) {
1839 let active = self.get_side(&side_ref).get_active();
1840
1841 active.hp += amount;
1842 }
1843
1844 fn switch(
1845 &mut self,
1846 side_ref: &SideReference,
1847 next_active_index: PokemonIndex,
1848 _: PokemonIndex,
1849 ) {
1850 let side = self.get_side(&side_ref);
1851 side.active_index = next_active_index;
1852 }
1853
1854 fn reverse_switch(
1855 &mut self,
1856 side_ref: &SideReference,
1857 _: PokemonIndex,
1858 previous_active_index: PokemonIndex,
1859 ) {
1860 let side = self.get_side(&side_ref);
1861 side.active_index = previous_active_index;
1862 }
1863
1864 fn apply_volatile_status(
1865 &mut self,
1866 side_ref: &SideReference,
1867 volatile_status: PokemonVolatileStatus,
1868 ) {
1869 self.get_side(&side_ref)
1870 .volatile_statuses
1871 .insert(volatile_status);
1872 }
1873
1874 fn remove_volatile_status(
1875 &mut self,
1876 side_ref: &SideReference,
1877 volatile_status: PokemonVolatileStatus,
1878 ) {
1879 self.get_side(&side_ref)
1880 .volatile_statuses
1881 .remove(&volatile_status);
1882 }
1883
1884 fn change_status(
1885 &mut self,
1886 side_ref: &SideReference,
1887 pokemon_index: PokemonIndex,
1888 new_status: PokemonStatus,
1889 ) {
1890 let pkmn = &mut self.get_side(&side_ref).pokemon[pokemon_index];
1891 pkmn.status = new_status;
1892 }
1893
1894 fn apply_boost(&mut self, side_ref: &SideReference, stat: &PokemonBoostableStat, amount: i8) {
1895 let side = self.get_side(&side_ref);
1896 match stat {
1897 PokemonBoostableStat::Attack => side.attack_boost += amount,
1898 PokemonBoostableStat::Defense => side.defense_boost += amount,
1899 PokemonBoostableStat::SpecialAttack => side.special_attack_boost += amount,
1900 PokemonBoostableStat::SpecialDefense => side.special_defense_boost += amount,
1901 PokemonBoostableStat::Speed => side.speed_boost += amount,
1902 PokemonBoostableStat::Evasion => side.evasion_boost += amount,
1903 PokemonBoostableStat::Accuracy => side.accuracy_boost += amount,
1904 }
1905 }
1906
1907 fn increment_side_condition(
1908 &mut self,
1909 side_ref: &SideReference,
1910 side_condition: &PokemonSideCondition,
1911 amount: i8,
1912 ) {
1913 let side = self.get_side(&side_ref);
1914
1915 match side_condition {
1916 PokemonSideCondition::AuroraVeil => side.side_conditions.aurora_veil += amount,
1917 PokemonSideCondition::CraftyShield => side.side_conditions.crafty_shield += amount,
1918 PokemonSideCondition::HealingWish => side.side_conditions.healing_wish += amount,
1919 PokemonSideCondition::LightScreen => side.side_conditions.light_screen += amount,
1920 PokemonSideCondition::LuckyChant => side.side_conditions.lucky_chant += amount,
1921 PokemonSideCondition::LunarDance => side.side_conditions.lunar_dance += amount,
1922 PokemonSideCondition::MatBlock => side.side_conditions.mat_block += amount,
1923 PokemonSideCondition::Mist => side.side_conditions.mist += amount,
1924 PokemonSideCondition::Protect => side.side_conditions.protect += amount,
1925 PokemonSideCondition::QuickGuard => side.side_conditions.quick_guard += amount,
1926 PokemonSideCondition::Reflect => side.side_conditions.reflect += amount,
1927 PokemonSideCondition::Safeguard => side.side_conditions.safeguard += amount,
1928 PokemonSideCondition::Spikes => side.side_conditions.spikes += amount,
1929 PokemonSideCondition::Stealthrock => side.side_conditions.stealth_rock += amount,
1930 PokemonSideCondition::StickyWeb => side.side_conditions.sticky_web += amount,
1931 PokemonSideCondition::Tailwind => side.side_conditions.tailwind += amount,
1932 PokemonSideCondition::ToxicCount => side.side_conditions.toxic_count += amount,
1933 PokemonSideCondition::ToxicSpikes => side.side_conditions.toxic_spikes += amount,
1934 PokemonSideCondition::WideGuard => side.side_conditions.wide_guard += amount,
1935 }
1936 }
1937
1938 fn change_types(
1939 &mut self,
1940 side_reference: &SideReference,
1941 new_types: (PokemonType, PokemonType),
1942 ) {
1943 self.get_side(side_reference).get_active().types = new_types;
1944 }
1945
1946 fn change_item(&mut self, side_reference: &SideReference, new_item: Items) {
1947 self.get_side(side_reference).get_active().item = new_item;
1948 }
1949
1950 fn change_weather(&mut self, weather_type: Weather, turns_remaining: i8) {
1951 self.weather.weather_type = weather_type;
1952 self.weather.turns_remaining = turns_remaining;
1953 }
1954
1955 fn change_terrain(&mut self, terrain_type: Terrain, turns_remaining: i8) {
1956 self.terrain.terrain_type = terrain_type;
1957 self.terrain.turns_remaining = turns_remaining;
1958 }
1959
1960 fn enable_move(&mut self, side_reference: &SideReference, move_index: &PokemonMoveIndex) {
1961 self.get_side(side_reference).get_active().moves[move_index].disabled = false;
1962 }
1963
1964 fn disable_move(&mut self, side_reference: &SideReference, move_index: &PokemonMoveIndex) {
1965 self.get_side(side_reference).get_active().moves[move_index].disabled = true;
1966 }
1967
1968 fn set_wish(&mut self, side_reference: &SideReference, wish_amount_change: i16) {
1969 self.get_side(side_reference).wish.0 = 2;
1970 self.get_side(side_reference).wish.1 += wish_amount_change;
1971 }
1972
1973 fn unset_wish(&mut self, side_reference: &SideReference, wish_amount_change: i16) {
1974 self.get_side(side_reference).wish.0 = 0;
1975 self.get_side(side_reference).wish.1 -= wish_amount_change;
1976 }
1977
1978 fn increment_wish(&mut self, side_reference: &SideReference) {
1979 self.get_side(side_reference).wish.0 += 1;
1980 }
1981
1982 fn decrement_wish(&mut self, side_reference: &SideReference) {
1983 self.get_side(side_reference).wish.0 -= 1;
1984 }
1985
1986 fn set_future_sight(&mut self, side_reference: &SideReference, pokemon_index: PokemonIndex) {
1987 let side = self.get_side(side_reference);
1988 side.future_sight.0 = 3;
1989 side.future_sight.1 = pokemon_index;
1990 }
1991
1992 fn unset_future_sight(
1993 &mut self,
1994 side_reference: &SideReference,
1995 previous_pokemon_index: PokemonIndex,
1996 ) {
1997 let side = self.get_side(side_reference);
1998 side.future_sight.0 = 0;
1999 side.future_sight.1 = previous_pokemon_index;
2000 }
2001
2002 fn increment_future_sight(&mut self, side_reference: &SideReference) {
2003 self.get_side(side_reference).future_sight.0 += 1;
2004 }
2005
2006 fn decrement_future_sight(&mut self, side_reference: &SideReference) {
2007 self.get_side(side_reference).future_sight.0 -= 1;
2008 }
2009
2010 fn damage_substitute(&mut self, side_reference: &SideReference, amount: i16) {
2011 self.get_side(side_reference).substitute_health -= amount;
2012 }
2013
2014 fn heal_substitute(&mut self, side_reference: &SideReference, amount: i16) {
2015 self.get_side(side_reference).substitute_health += amount;
2016 }
2017
2018 fn set_substitute_health(&mut self, side_reference: &SideReference, amount: i16) {
2019 self.get_side(side_reference).substitute_health += amount;
2020 }
2021
2022 fn decrement_rest_turn(&mut self, side_reference: &SideReference) {
2023 self.get_side(side_reference).get_active().rest_turns -= 1;
2024 }
2025
2026 fn increment_rest_turn(&mut self, side_reference: &SideReference) {
2027 self.get_side(side_reference).get_active().rest_turns += 1;
2028 }
2029
2030 fn set_rest_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].rest_turns = amount;
2037 }
2038
2039 fn set_sleep_turn(
2040 &mut self,
2041 side_reference: &SideReference,
2042 pokemon_index: PokemonIndex,
2043 amount: i8,
2044 ) {
2045 self.get_side(side_reference).pokemon[pokemon_index].sleep_turns = amount;
2046 }
2047
2048 fn toggle_trickroom(&mut self, new_turns_remaining: i8) {
2049 self.trick_room.active = !self.trick_room.active;
2050 self.trick_room.turns_remaining = new_turns_remaining;
2051 }
2052
2053 fn set_last_used_move(&mut self, side_reference: &SideReference, last_used_move: LastUsedMove) {
2054 match side_reference {
2055 SideReference::SideOne => self.side_one.last_used_move = last_used_move,
2056 SideReference::SideTwo => self.side_two.last_used_move = last_used_move,
2057 }
2058 }
2059
2060 fn decrement_pp(
2061 &mut self,
2062 side_reference: &SideReference,
2063 move_index: &PokemonMoveIndex,
2064 amount: &i8,
2065 ) {
2066 match side_reference {
2067 SideReference::SideOne => self.side_one.get_active().moves[move_index].pp -= amount,
2068 SideReference::SideTwo => self.side_two.get_active().moves[move_index].pp -= amount,
2069 }
2070 }
2071
2072 fn increment_pp(
2073 &mut self,
2074 side_reference: &SideReference,
2075 move_index: &PokemonMoveIndex,
2076 amount: &i8,
2077 ) {
2078 match side_reference {
2079 SideReference::SideOne => self.side_one.get_active().moves[move_index].pp += amount,
2080 SideReference::SideTwo => self.side_two.get_active().moves[move_index].pp += amount,
2081 }
2082 }
2083
2084 fn set_damage_dealt(
2085 side: &mut Side,
2086 damage_change: i16,
2087 move_category: MoveCategory,
2088 toggle_hit_substitute: bool,
2089 ) {
2090 side.damage_dealt.damage += damage_change;
2091 side.damage_dealt.move_category = move_category;
2092 if toggle_hit_substitute {
2093 side.damage_dealt.hit_substitute = !side.damage_dealt.hit_substitute;
2094 }
2095 }
2096
2097 pub fn apply_instructions(&mut self, instructions: &Vec<Instruction>) {
2098 for i in instructions {
2099 self.apply_one_instruction(i)
2100 }
2101 }
2102
2103 pub fn apply_one_instruction(&mut self, instruction: &Instruction) {
2104 match instruction {
2105 Instruction::Damage(instruction) => {
2106 self.damage(&instruction.side_ref, instruction.damage_amount)
2107 }
2108 Instruction::Switch(instruction) => self.switch(
2109 &instruction.side_ref,
2110 instruction.next_index,
2111 instruction.previous_index,
2112 ),
2113 Instruction::ApplyVolatileStatus(instruction) => {
2114 self.apply_volatile_status(&instruction.side_ref, instruction.volatile_status)
2115 }
2116 Instruction::RemoveVolatileStatus(instruction) => {
2117 self.remove_volatile_status(&instruction.side_ref, instruction.volatile_status)
2118 }
2119 Instruction::ChangeStatus(instruction) => self.change_status(
2120 &instruction.side_ref,
2121 instruction.pokemon_index,
2122 instruction.new_status,
2123 ),
2124 Instruction::Boost(instruction) => {
2125 self.apply_boost(&instruction.side_ref, &instruction.stat, instruction.amount)
2126 }
2127 Instruction::ChangeSideCondition(instruction) => self.increment_side_condition(
2128 &instruction.side_ref,
2129 &instruction.side_condition,
2130 instruction.amount,
2131 ),
2132 Instruction::ChangeWeather(instruction) => self.change_weather(
2133 instruction.new_weather,
2134 instruction.new_weather_turns_remaining,
2135 ),
2136 Instruction::DecrementWeatherTurnsRemaining => {
2137 self.weather.turns_remaining -= 1;
2138 }
2139 Instruction::ChangeTerrain(instruction) => self.change_terrain(
2140 instruction.new_terrain,
2141 instruction.new_terrain_turns_remaining,
2142 ),
2143 Instruction::DecrementTerrainTurnsRemaining => {
2144 self.terrain.turns_remaining -= 1;
2145 }
2146 Instruction::ChangeType(instruction) => {
2147 self.change_types(&instruction.side_ref, instruction.new_types)
2148 }
2149 Instruction::ChangeAbility(instruction) => {
2150 self.get_side(&instruction.side_ref).get_active().ability =
2151 instruction.new_ability.clone();
2152 }
2153 Instruction::Heal(instruction) => {
2154 self.heal(&instruction.side_ref, instruction.heal_amount)
2155 }
2156 Instruction::ChangeItem(instruction) => {
2157 self.change_item(&instruction.side_ref, instruction.new_item)
2158 }
2159 Instruction::ChangeAttack(instruction) => {
2160 self.get_side(&instruction.side_ref).get_active().attack += instruction.amount;
2161 }
2162 Instruction::ChangeDefense(instruction) => {
2163 self.get_side(&instruction.side_ref).get_active().defense += instruction.amount;
2164 }
2165 Instruction::ChangeSpecialAttack(instruction) => {
2166 self.get_side(&instruction.side_ref)
2167 .get_active()
2168 .special_attack += instruction.amount;
2169 }
2170 Instruction::ChangeSpecialDefense(instruction) => {
2171 self.get_side(&instruction.side_ref)
2172 .get_active()
2173 .special_defense += instruction.amount;
2174 }
2175 Instruction::ChangeSpeed(instruction) => {
2176 self.get_side(&instruction.side_ref).get_active().speed += instruction.amount;
2177 }
2178 Instruction::EnableMove(instruction) => {
2179 self.enable_move(&instruction.side_ref, &instruction.move_index)
2180 }
2181 Instruction::DisableMove(instruction) => {
2182 self.disable_move(&instruction.side_ref, &instruction.move_index)
2183 }
2184 Instruction::ChangeWish(instruction) => {
2185 self.set_wish(&instruction.side_ref, instruction.wish_amount_change);
2186 }
2187 Instruction::DecrementWish(instruction) => {
2188 self.decrement_wish(&instruction.side_ref);
2189 }
2190 Instruction::SetFutureSight(instruction) => {
2191 self.set_future_sight(&instruction.side_ref, instruction.pokemon_index);
2192 }
2193 Instruction::DecrementFutureSight(instruction) => {
2194 self.decrement_future_sight(&instruction.side_ref);
2195 }
2196 Instruction::DamageSubstitute(instruction) => {
2197 self.damage_substitute(&instruction.side_ref, instruction.damage_amount);
2198 }
2199 Instruction::ChangeSubstituteHealth(instruction) => {
2200 self.set_substitute_health(&instruction.side_ref, instruction.health_change);
2201 }
2202 Instruction::SetRestTurns(instruction) => {
2203 self.set_rest_turn(
2204 &instruction.side_ref,
2205 instruction.pokemon_index,
2206 instruction.new_turns,
2207 );
2208 }
2209 Instruction::SetSleepTurns(instruction) => {
2210 self.set_sleep_turn(
2211 &instruction.side_ref,
2212 instruction.pokemon_index,
2213 instruction.new_turns,
2214 );
2215 }
2216 Instruction::DecrementRestTurns(instruction) => {
2217 self.decrement_rest_turn(&instruction.side_ref);
2218 }
2219 Instruction::ToggleTrickRoom(instruction) => {
2220 self.toggle_trickroom(instruction.new_trickroom_turns_remaining)
2221 }
2222 Instruction::DecrementTrickRoomTurnsRemaining => {
2223 self.trick_room.turns_remaining -= 1;
2224 }
2225 Instruction::ToggleSideOneForceSwitch => self.side_one.toggle_force_switch(),
2226 Instruction::ToggleSideTwoForceSwitch => self.side_two.toggle_force_switch(),
2227 Instruction::SetSideOneMoveSecondSwitchOutMove(instruction) => {
2228 self.side_one.switch_out_move_second_saved_move = instruction.new_choice;
2229 }
2230 Instruction::SetSideTwoMoveSecondSwitchOutMove(instruction) => {
2231 self.side_two.switch_out_move_second_saved_move = instruction.new_choice;
2232 }
2233 Instruction::ToggleBatonPassing(instruction) => match instruction.side_ref {
2234 SideReference::SideOne => {
2235 self.side_one.baton_passing = !self.side_one.baton_passing
2236 }
2237 SideReference::SideTwo => {
2238 self.side_two.baton_passing = !self.side_two.baton_passing
2239 }
2240 },
2241 Instruction::ToggleTerastallized(instruction) => match instruction.side_ref {
2242 SideReference::SideOne => self.side_one.get_active().terastallized ^= true,
2243 SideReference::SideTwo => self.side_two.get_active().terastallized ^= true,
2244 },
2245 Instruction::SetLastUsedMove(instruction) => {
2246 self.set_last_used_move(&instruction.side_ref, instruction.last_used_move)
2247 }
2248 Instruction::SetDamageDealtSideOne(instruction) => State::set_damage_dealt(
2249 &mut self.side_one,
2250 instruction.damage_change,
2251 instruction.move_category,
2252 instruction.toggle_hit_substitute,
2253 ),
2254 Instruction::SetDamageDealtSideTwo(instruction) => State::set_damage_dealt(
2255 &mut self.side_two,
2256 instruction.damage_change,
2257 instruction.move_category,
2258 instruction.toggle_hit_substitute,
2259 ),
2260 Instruction::DecrementPP(instruction) => self.decrement_pp(
2261 &instruction.side_ref,
2262 &instruction.move_index,
2263 &instruction.amount,
2264 ),
2265 Instruction::FormeChange(instruction) => {
2266 self.get_side(&instruction.side_ref).get_active().id = instruction.new_forme;
2267 }
2268 }
2269 }
2270
2271 pub fn reverse_instructions(&mut self, instructions: &Vec<Instruction>) {
2272 for i in instructions.iter().rev() {
2273 self.reverse_one_instruction(i);
2274 }
2275 }
2276
2277 pub fn reverse_one_instruction(&mut self, instruction: &Instruction) {
2278 match instruction {
2279 Instruction::Damage(instruction) => {
2280 self.heal(&instruction.side_ref, instruction.damage_amount)
2281 }
2282 Instruction::Switch(instruction) => self.reverse_switch(
2283 &instruction.side_ref,
2284 instruction.next_index,
2285 instruction.previous_index,
2286 ),
2287 Instruction::ApplyVolatileStatus(instruction) => {
2288 self.remove_volatile_status(&instruction.side_ref, instruction.volatile_status)
2289 }
2290 Instruction::RemoveVolatileStatus(instruction) => {
2291 self.apply_volatile_status(&instruction.side_ref, instruction.volatile_status)
2292 }
2293 Instruction::ChangeStatus(instruction) => self.change_status(
2294 &instruction.side_ref,
2295 instruction.pokemon_index,
2296 instruction.old_status,
2297 ),
2298 Instruction::Boost(instruction) => self.apply_boost(
2299 &instruction.side_ref,
2300 &instruction.stat,
2301 -1 * instruction.amount,
2302 ),
2303 Instruction::ChangeSideCondition(instruction) => self.increment_side_condition(
2304 &instruction.side_ref,
2305 &instruction.side_condition,
2306 -1 * instruction.amount,
2307 ),
2308 Instruction::ChangeWeather(instruction) => self.change_weather(
2309 instruction.previous_weather,
2310 instruction.previous_weather_turns_remaining,
2311 ),
2312 Instruction::DecrementWeatherTurnsRemaining => {
2313 self.weather.turns_remaining += 1;
2314 }
2315 Instruction::ChangeTerrain(instruction) => self.change_terrain(
2316 instruction.previous_terrain,
2317 instruction.previous_terrain_turns_remaining,
2318 ),
2319 Instruction::DecrementTerrainTurnsRemaining => {
2320 self.terrain.turns_remaining += 1;
2321 }
2322 Instruction::ChangeType(instruction) => {
2323 self.change_types(&instruction.side_ref, instruction.old_types)
2324 }
2325 Instruction::ChangeAbility(instruction) => {
2326 self.get_side(&instruction.side_ref).get_active().ability =
2327 instruction.old_ability.clone();
2328 }
2329 Instruction::EnableMove(instruction) => {
2330 self.disable_move(&instruction.side_ref, &instruction.move_index)
2331 }
2332 Instruction::DisableMove(instruction) => {
2333 self.enable_move(&instruction.side_ref, &instruction.move_index)
2334 }
2335 Instruction::Heal(instruction) => {
2336 self.damage(&instruction.side_ref, instruction.heal_amount)
2337 }
2338 Instruction::ChangeItem(instruction) => {
2339 self.change_item(&instruction.side_ref, instruction.current_item)
2340 }
2341 Instruction::ChangeAttack(instruction) => {
2342 self.get_side(&instruction.side_ref).get_active().attack -= instruction.amount;
2343 }
2344 Instruction::ChangeDefense(instruction) => {
2345 self.get_side(&instruction.side_ref).get_active().defense -= instruction.amount;
2346 }
2347 Instruction::ChangeSpecialAttack(instruction) => {
2348 self.get_side(&instruction.side_ref)
2349 .get_active()
2350 .special_attack -= instruction.amount;
2351 }
2352 Instruction::ChangeSpecialDefense(instruction) => {
2353 self.get_side(&instruction.side_ref)
2354 .get_active()
2355 .special_defense -= instruction.amount;
2356 }
2357 Instruction::ChangeSpeed(instruction) => {
2358 self.get_side(&instruction.side_ref).get_active().speed -= instruction.amount;
2359 }
2360 Instruction::ChangeWish(instruction) => {
2361 self.unset_wish(&instruction.side_ref, instruction.wish_amount_change)
2362 }
2363 Instruction::DecrementWish(instruction) => self.increment_wish(&instruction.side_ref),
2364 Instruction::SetFutureSight(instruction) => {
2365 self.unset_future_sight(&instruction.side_ref, instruction.previous_pokemon_index)
2366 }
2367 Instruction::DecrementFutureSight(instruction) => {
2368 self.increment_future_sight(&instruction.side_ref)
2369 }
2370 Instruction::DamageSubstitute(instruction) => {
2371 self.heal_substitute(&instruction.side_ref, instruction.damage_amount);
2372 }
2373 Instruction::ChangeSubstituteHealth(instruction) => {
2374 self.set_substitute_health(&instruction.side_ref, -1 * instruction.health_change);
2375 }
2376 Instruction::SetRestTurns(instruction) => {
2377 self.set_rest_turn(
2378 &instruction.side_ref,
2379 instruction.pokemon_index,
2380 instruction.previous_turns,
2381 );
2382 }
2383 Instruction::SetSleepTurns(instruction) => {
2384 self.set_sleep_turn(
2385 &instruction.side_ref,
2386 instruction.pokemon_index,
2387 instruction.previous_turns,
2388 );
2389 }
2390 Instruction::DecrementRestTurns(instruction) => {
2391 self.increment_rest_turn(&instruction.side_ref);
2392 }
2393 Instruction::ToggleTrickRoom(instruction) => {
2394 self.toggle_trickroom(instruction.previous_trickroom_turns_remaining)
2395 }
2396 Instruction::DecrementTrickRoomTurnsRemaining => {
2397 self.trick_room.turns_remaining += 1;
2398 }
2399 Instruction::ToggleSideOneForceSwitch => self.side_one.toggle_force_switch(),
2400 Instruction::ToggleSideTwoForceSwitch => self.side_two.toggle_force_switch(),
2401 Instruction::SetSideOneMoveSecondSwitchOutMove(instruction) => {
2402 self.side_one.switch_out_move_second_saved_move = instruction.previous_choice;
2403 }
2404 Instruction::SetSideTwoMoveSecondSwitchOutMove(instruction) => {
2405 self.side_two.switch_out_move_second_saved_move = instruction.previous_choice;
2406 }
2407 Instruction::ToggleBatonPassing(instruction) => match instruction.side_ref {
2408 SideReference::SideOne => {
2409 self.side_one.baton_passing = !self.side_one.baton_passing
2410 }
2411 SideReference::SideTwo => {
2412 self.side_two.baton_passing = !self.side_two.baton_passing
2413 }
2414 },
2415 Instruction::ToggleTerastallized(instruction) => match instruction.side_ref {
2416 SideReference::SideOne => self.side_one.get_active().terastallized ^= true,
2417 SideReference::SideTwo => self.side_two.get_active().terastallized ^= true,
2418 },
2419 Instruction::SetLastUsedMove(instruction) => {
2420 self.set_last_used_move(&instruction.side_ref, instruction.previous_last_used_move)
2421 }
2422 Instruction::SetDamageDealtSideOne(instruction) => State::set_damage_dealt(
2423 &mut self.side_one,
2424 -1 * instruction.damage_change,
2425 instruction.previous_move_category,
2426 instruction.toggle_hit_substitute,
2427 ),
2428 Instruction::SetDamageDealtSideTwo(instruction) => State::set_damage_dealt(
2429 &mut self.side_two,
2430 -1 * instruction.damage_change,
2431 instruction.previous_move_category,
2432 instruction.toggle_hit_substitute,
2433 ),
2434 Instruction::DecrementPP(instruction) => self.increment_pp(
2435 &instruction.side_ref,
2436 &instruction.move_index,
2437 &instruction.amount,
2438 ),
2439 Instruction::FormeChange(instruction) => {
2440 self.get_side(&instruction.side_ref).get_active().id = instruction.previous_forme;
2441 }
2442 }
2443 }
2444}
2445
2446impl Move {
2447 pub fn serialize(&self) -> String {
2448 format!("{:?};{};{}", self.id, self.disabled, self.pp)
2449 }
2450 pub fn deserialize(serialized: &str) -> Move {
2451 let split: Vec<&str> = serialized.split(";").collect();
2452 Move {
2453 id: Choices::from_str(split[0]).unwrap(),
2454 disabled: split[1].parse::<bool>().unwrap(),
2455 pp: split[2].parse::<i8>().unwrap(),
2456 choice: MOVES
2457 .get(&Choices::from_str(split[0]).unwrap())
2458 .unwrap()
2459 .to_owned(),
2460 }
2461 }
2462}
2463
2464impl Pokemon {
2465 pub fn serialize(&self) -> String {
2466 let evs_str = format!(
2467 "{};{};{};{};{};{}",
2468 self.evs.0, self.evs.1, self.evs.2, self.evs.3, self.evs.4, self.evs.5
2469 );
2470 format!(
2471 "{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}",
2472 self.id,
2473 self.level,
2474 self.types.0.to_string(),
2475 self.types.1.to_string(),
2476 self.base_types.0.to_string(),
2477 self.base_types.1.to_string(),
2478 self.hp,
2479 self.maxhp,
2480 self.ability.to_string(),
2481 self.base_ability.to_string(),
2482 self.item.to_string(),
2483 self.nature.to_string(),
2484 evs_str,
2485 self.attack,
2486 self.defense,
2487 self.special_attack,
2488 self.special_defense,
2489 self.speed,
2490 self.status.to_string(),
2491 self.rest_turns,
2492 self.sleep_turns,
2493 self.weight_kg,
2494 self.moves.m0.serialize(),
2495 self.moves.m1.serialize(),
2496 self.moves.m2.serialize(),
2497 self.moves.m3.serialize(),
2498 self.terastallized,
2499 self.tera_type.to_string(),
2500 )
2501 }
2502
2503 pub fn deserialize(serialized: &str) -> Pokemon {
2504 let split: Vec<&str> = serialized.split(",").collect();
2505 let evs = if split[12] != "" {
2506 let mut ev_iter = split[12].split(";");
2507 (
2508 ev_iter.next().unwrap().parse::<u8>().unwrap(),
2509 ev_iter.next().unwrap().parse::<u8>().unwrap(),
2510 ev_iter.next().unwrap().parse::<u8>().unwrap(),
2511 ev_iter.next().unwrap().parse::<u8>().unwrap(),
2512 ev_iter.next().unwrap().parse::<u8>().unwrap(),
2513 ev_iter.next().unwrap().parse::<u8>().unwrap(),
2514 )
2515 } else {
2516 (85, 85, 85, 85, 85, 85)
2517 };
2518 Pokemon {
2519 id: PokemonName::from_str(split[0]).unwrap(),
2520 level: split[1].parse::<i8>().unwrap(),
2521 types: (
2522 PokemonType::from_str(split[2]).unwrap(),
2523 PokemonType::from_str(split[3]).unwrap(),
2524 ),
2525 base_types: (
2526 PokemonType::from_str(split[4]).unwrap(),
2527 PokemonType::from_str(split[5]).unwrap(),
2528 ),
2529 hp: split[6].parse::<i16>().unwrap(),
2530 maxhp: split[7].parse::<i16>().unwrap(),
2531 ability: Abilities::from_str(split[8]).unwrap(),
2532 base_ability: Abilities::from_str(split[9]).unwrap(),
2533 item: Items::from_str(split[10]).unwrap(),
2534 nature: PokemonNature::from_str(split[11]).unwrap(),
2535 evs,
2536 attack: split[13].parse::<i16>().unwrap(),
2537 defense: split[14].parse::<i16>().unwrap(),
2538 special_attack: split[15].parse::<i16>().unwrap(),
2539 special_defense: split[16].parse::<i16>().unwrap(),
2540 speed: split[17].parse::<i16>().unwrap(),
2541 status: PokemonStatus::from_str(split[18]).unwrap(),
2542 rest_turns: split[19].parse::<i8>().unwrap(),
2543 sleep_turns: split[20].parse::<i8>().unwrap(),
2544 weight_kg: split[21].parse::<f32>().unwrap(),
2545 moves: PokemonMoves {
2546 m0: Move::deserialize(split[22]),
2547 m1: Move::deserialize(split[23]),
2548 m2: Move::deserialize(split[24]),
2549 m3: Move::deserialize(split[25]),
2550 },
2551 terastallized: split[26].parse::<bool>().unwrap(),
2552 tera_type: PokemonType::from_str(split[27]).unwrap(),
2553 }
2554 }
2555}
2556
2557impl LastUsedMove {
2558 pub fn serialize(&self) -> String {
2559 match self {
2560 LastUsedMove::Move(move_index) => format!("move:{}", move_index.serialize()),
2561 LastUsedMove::Switch(pkmn_index) => format!("switch:{}", pkmn_index.serialize()),
2562 LastUsedMove::None => "move:none".to_string(),
2563 }
2564 }
2565 pub fn deserialize(serialized: &str) -> LastUsedMove {
2566 let split: Vec<&str> = serialized.split(":").collect();
2567 match split[0] {
2568 "move" => {
2569 if split[1] == "none" {
2570 LastUsedMove::None
2571 } else {
2572 LastUsedMove::Move(PokemonMoveIndex::deserialize(split[1]))
2573 }
2574 }
2575 "switch" => LastUsedMove::Switch(PokemonIndex::deserialize(split[1])),
2576 _ => panic!("Invalid LastUsedMove: {}", serialized),
2577 }
2578 }
2579}
2580
2581impl PokemonIndex {
2582 pub fn serialize(&self) -> String {
2583 match self {
2584 PokemonIndex::P0 => "0".to_string(),
2585 PokemonIndex::P1 => "1".to_string(),
2586 PokemonIndex::P2 => "2".to_string(),
2587 PokemonIndex::P3 => "3".to_string(),
2588 PokemonIndex::P4 => "4".to_string(),
2589 PokemonIndex::P5 => "5".to_string(),
2590 }
2591 }
2592
2593 pub fn deserialize(serialized: &str) -> PokemonIndex {
2594 match serialized {
2595 "0" => PokemonIndex::P0,
2596 "1" => PokemonIndex::P1,
2597 "2" => PokemonIndex::P2,
2598 "3" => PokemonIndex::P3,
2599 "4" => PokemonIndex::P4,
2600 "5" => PokemonIndex::P5,
2601 _ => panic!("Invalid PokemonIndex: {}", serialized),
2602 }
2603 }
2604}
2605
2606impl PokemonMoveIndex {
2607 pub fn serialize(&self) -> String {
2608 match self {
2609 PokemonMoveIndex::M0 => "0".to_string(),
2610 PokemonMoveIndex::M1 => "1".to_string(),
2611 PokemonMoveIndex::M2 => "2".to_string(),
2612 PokemonMoveIndex::M3 => "3".to_string(),
2613 }
2614 }
2615
2616 pub fn deserialize(serialized: &str) -> PokemonMoveIndex {
2617 match serialized {
2618 "0" => PokemonMoveIndex::M0,
2619 "1" => PokemonMoveIndex::M1,
2620 "2" => PokemonMoveIndex::M2,
2621 "3" => PokemonMoveIndex::M3,
2622 _ => panic!("Invalid PokemonMoveIndex: {}", serialized),
2623 }
2624 }
2625}
2626
2627impl SideConditions {
2628 pub fn serialize(&self) -> String {
2629 format!(
2630 "{};{};{};{};{};{};{};{};{};{};{};{};{};{};{};{};{};{};{}",
2631 self.aurora_veil,
2632 self.crafty_shield,
2633 self.healing_wish,
2634 self.light_screen,
2635 self.lucky_chant,
2636 self.lunar_dance,
2637 self.mat_block,
2638 self.mist,
2639 self.protect,
2640 self.quick_guard,
2641 self.reflect,
2642 self.safeguard,
2643 self.spikes,
2644 self.stealth_rock,
2645 self.sticky_web,
2646 self.tailwind,
2647 self.toxic_count,
2648 self.toxic_spikes,
2649 self.wide_guard,
2650 )
2651 }
2652 pub fn deserialize(serialized: &str) -> SideConditions {
2653 let split: Vec<&str> = serialized.split(";").collect();
2654 SideConditions {
2655 aurora_veil: split[0].parse::<i8>().unwrap(),
2656 crafty_shield: split[1].parse::<i8>().unwrap(),
2657 healing_wish: split[2].parse::<i8>().unwrap(),
2658 light_screen: split[3].parse::<i8>().unwrap(),
2659 lucky_chant: split[4].parse::<i8>().unwrap(),
2660 lunar_dance: split[5].parse::<i8>().unwrap(),
2661 mat_block: split[6].parse::<i8>().unwrap(),
2662 mist: split[7].parse::<i8>().unwrap(),
2663 protect: split[8].parse::<i8>().unwrap(),
2664 quick_guard: split[9].parse::<i8>().unwrap(),
2665 reflect: split[10].parse::<i8>().unwrap(),
2666 safeguard: split[11].parse::<i8>().unwrap(),
2667 spikes: split[12].parse::<i8>().unwrap(),
2668 stealth_rock: split[13].parse::<i8>().unwrap(),
2669 sticky_web: split[14].parse::<i8>().unwrap(),
2670 tailwind: split[15].parse::<i8>().unwrap(),
2671 toxic_count: split[16].parse::<i8>().unwrap(),
2672 toxic_spikes: split[17].parse::<i8>().unwrap(),
2673 wide_guard: split[18].parse::<i8>().unwrap(),
2674 }
2675 }
2676}
2677
2678impl Side {
2679 pub fn serialize(&self) -> String {
2680 let mut vs_string = String::new();
2681 for vs in &self.volatile_statuses {
2682 vs_string.push_str(&vs.to_string());
2683 vs_string.push_str(":");
2684 }
2685 format!(
2686 "{}={}={}={}={}={}={}={}={}={}={}={}={}={}={}={}={}={}={}={}={}={}={}={}={}={}={}",
2687 self.pokemon.p0.serialize(),
2688 self.pokemon.p1.serialize(),
2689 self.pokemon.p2.serialize(),
2690 self.pokemon.p3.serialize(),
2691 self.pokemon.p4.serialize(),
2692 self.pokemon.p5.serialize(),
2693 self.active_index.serialize(),
2694 self.side_conditions.serialize(),
2695 vs_string,
2696 self.substitute_health,
2697 self.attack_boost,
2698 self.defense_boost,
2699 self.special_attack_boost,
2700 self.special_defense_boost,
2701 self.speed_boost,
2702 self.accuracy_boost,
2703 self.evasion_boost,
2704 self.wish.0,
2705 self.wish.1,
2706 self.future_sight.0,
2707 self.future_sight.1.serialize(),
2708 self.force_switch,
2709 self.switch_out_move_second_saved_move.to_string(),
2710 self.baton_passing,
2711 self.force_trapped,
2712 self.last_used_move.serialize(),
2713 self.slow_uturn_move,
2714 )
2715 }
2716 pub fn deserialize(serialized: &str) -> Side {
2717 let split: Vec<&str> = serialized.split("=").collect();
2718
2719 let mut vs_hashset = HashSet::new();
2720 if split[8] != "" {
2721 for item in split[8].split(":") {
2722 vs_hashset.insert(PokemonVolatileStatus::from_str(item).unwrap());
2723 }
2724 }
2725 Side {
2726 pokemon: SidePokemon {
2727 p0: Pokemon::deserialize(split[0]),
2728 p1: Pokemon::deserialize(split[1]),
2729 p2: Pokemon::deserialize(split[2]),
2730 p3: Pokemon::deserialize(split[3]),
2731 p4: Pokemon::deserialize(split[4]),
2732 p5: Pokemon::deserialize(split[5]),
2733 },
2734 active_index: PokemonIndex::deserialize(split[6]),
2735 side_conditions: SideConditions::deserialize(split[7]),
2736 volatile_statuses: vs_hashset,
2737 substitute_health: split[9].parse::<i16>().unwrap(),
2738 attack_boost: split[10].parse::<i8>().unwrap(),
2739 defense_boost: split[11].parse::<i8>().unwrap(),
2740 special_attack_boost: split[12].parse::<i8>().unwrap(),
2741 special_defense_boost: split[13].parse::<i8>().unwrap(),
2742 speed_boost: split[14].parse::<i8>().unwrap(),
2743 accuracy_boost: split[15].parse::<i8>().unwrap(),
2744 evasion_boost: split[16].parse::<i8>().unwrap(),
2745 wish: (
2746 split[17].parse::<i8>().unwrap(),
2747 split[18].parse::<i16>().unwrap(),
2748 ),
2749 future_sight: (
2750 split[19].parse::<i8>().unwrap(),
2751 PokemonIndex::deserialize(split[20]),
2752 ),
2753 force_switch: split[21].parse::<bool>().unwrap(),
2754 switch_out_move_second_saved_move: Choices::from_str(split[22]).unwrap(),
2755 baton_passing: split[23].parse::<bool>().unwrap(),
2756 force_trapped: split[24].parse::<bool>().unwrap(),
2757 last_used_move: LastUsedMove::deserialize(split[25]),
2758 damage_dealt: DamageDealt::default(),
2759 slow_uturn_move: split[26].parse::<bool>().unwrap(),
2760 }
2761 }
2762}
2763
2764impl StateWeather {
2765 pub fn serialize(&self) -> String {
2766 format!("{:?};{}", self.weather_type, self.turns_remaining)
2767 }
2768 pub fn deserialize(serialized: &str) -> StateWeather {
2769 let split: Vec<&str> = serialized.split(";").collect();
2770 StateWeather {
2771 weather_type: Weather::from_str(split[0]).unwrap(),
2772 turns_remaining: split[1].parse::<i8>().unwrap(),
2773 }
2774 }
2775}
2776
2777impl StateTerrain {
2778 pub fn serialize(&self) -> String {
2779 format!("{:?};{}", self.terrain_type, self.turns_remaining)
2780 }
2781 pub fn deserialize(serialized: &str) -> StateTerrain {
2782 let split: Vec<&str> = serialized.split(";").collect();
2783 StateTerrain {
2784 terrain_type: Terrain::from_str(split[0]).unwrap(),
2785 turns_remaining: split[1].parse::<i8>().unwrap(),
2786 }
2787 }
2788}
2789
2790impl StateTrickRoom {
2791 pub fn serialize(&self) -> String {
2792 format!("{};{}", self.active, self.turns_remaining)
2793 }
2794 pub fn deserialize(serialized: &str) -> StateTrickRoom {
2795 let split: Vec<&str> = serialized.split(";").collect();
2796 StateTrickRoom {
2797 active: split[0].parse::<bool>().unwrap(),
2798 turns_remaining: split[1].parse::<i8>().unwrap(),
2799 }
2800 }
2801}
2802
2803impl State {
2804 pub fn serialize(&self) -> String {
2805 format!(
2806 "{}/{}/{}/{}/{}/{}",
2807 self.side_one.serialize(),
2808 self.side_two.serialize(),
2809 self.weather.serialize(),
2810 self.terrain.serialize(),
2811 self.trick_room.serialize(),
2812 self.team_preview
2813 )
2814 }
2815
2816 pub fn deserialize(serialized: &str) -> State {
3002 let split: Vec<&str> = serialized.split("/").collect();
3003 let mut state = State {
3004 side_one: Side::deserialize(split[0]),
3005 side_two: Side::deserialize(split[1]),
3006 weather: StateWeather::deserialize(split[2]),
3007 terrain: StateTerrain::deserialize(split[3]),
3008 trick_room: StateTrickRoom::deserialize(split[4]),
3009 team_preview: split[5].parse::<bool>().unwrap(),
3010 use_damage_dealt: false,
3011 use_last_used_move: false,
3012 };
3013 state.set_conditional_mechanics();
3014 state
3015 }
3016}