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