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