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