1use super::abilities::Abilities;
2use super::choice_effects::charge_volatile_to_choice;
3use super::items::Items;
4use crate::choices::{Choices, MoveCategory};
5use crate::define_enum_with_from_str;
6use crate::instruction::{
7 ChangeSideConditionInstruction, ChangeStatInstruction, ChangeType,
8 ChangeVolatileStatusDurationInstruction, Instruction, RemoveVolatileStatusInstruction,
9 StateInstructions,
10};
11use crate::pokemon::PokemonName;
12use crate::state::{
13 LastUsedMove, Pokemon, PokemonBoostableStat, PokemonIndex, PokemonMoveIndex,
14 PokemonSideCondition, PokemonStatus, PokemonType, Side, SideReference, State,
15};
16use core::panic;
17use std::collections::HashSet;
18
19fn common_pkmn_stat_calc(stat: u16, ev: u16, level: u16) -> u16 {
20 ((2 * stat + 31 + (ev / 4)) * level) / 100
22}
23
24fn multiply_boost(boost_num: i8, stat_value: i16) -> i16 {
25 match boost_num {
26 -6 => stat_value * 2 / 8,
27 -5 => stat_value * 2 / 7,
28 -4 => stat_value * 2 / 6,
29 -3 => stat_value * 2 / 5,
30 -2 => stat_value * 2 / 4,
31 -1 => stat_value * 2 / 3,
32 0 => stat_value,
33 1 => stat_value * 3 / 2,
34 2 => stat_value * 4 / 2,
35 3 => stat_value * 5 / 2,
36 4 => stat_value * 6 / 2,
37 5 => stat_value * 7 / 2,
38 6 => stat_value * 8 / 2,
39 _ => panic!("Invalid boost number: {}", boost_num),
40 }
41}
42
43#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
44pub enum MoveChoice {
45 MoveTera(PokemonMoveIndex),
46 Move(PokemonMoveIndex),
47 Switch(PokemonIndex),
48 None,
49}
50
51impl MoveChoice {
52 pub fn to_string(&self, side: &Side) -> String {
53 match self {
54 MoveChoice::MoveTera(index) => {
55 format!("{}-tera", side.get_active_immutable().moves[&index].id).to_lowercase()
56 }
57 MoveChoice::Move(index) => {
58 format!("{}", side.get_active_immutable().moves[&index].id).to_lowercase()
59 }
60 MoveChoice::Switch(index) => format!("{}", side.pokemon[*index].id).to_lowercase(),
61 MoveChoice::None => "No Move".to_string(),
62 }
63 }
64 pub fn from_string(s: &str, side: &Side) -> Option<MoveChoice> {
65 let s = s.to_lowercase();
66 if s == "none" {
67 return Some(MoveChoice::None);
68 }
69
70 let mut pkmn_iter = side.pokemon.into_iter();
71 while let Some(pkmn) = pkmn_iter.next() {
72 if pkmn.id.to_string().to_lowercase() == s
73 && pkmn_iter.pokemon_index != side.active_index
74 {
75 return Some(MoveChoice::Switch(pkmn_iter.pokemon_index));
76 }
77 }
78
79 let mut move_iter = side.get_active_immutable().moves.into_iter();
83 let mut move_name = s;
84 if move_name.ends_with("-tera") {
85 move_name = move_name[..move_name.len() - 5].to_string();
86 while let Some(mv) = move_iter.next() {
87 if format!("{:?}", mv.id).to_lowercase() == move_name {
88 return Some(MoveChoice::MoveTera(move_iter.pokemon_move_index));
89 }
90 }
91 } else {
92 while let Some(mv) = move_iter.next() {
93 if format!("{:?}", mv.id).to_lowercase() == move_name {
94 return Some(MoveChoice::Move(move_iter.pokemon_move_index));
95 }
96 }
97 }
98
99 None
100 }
101}
102
103define_enum_with_from_str! {
104 #[repr(u8)]
105 #[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
106 PokemonVolatileStatus {
107 NONE,
108 AQUARING,
109 ATTRACT,
110 AUTOTOMIZE,
111 BANEFULBUNKER,
112 BIDE,
113 BOUNCE,
114 BURNINGBULWARK,
115 CHARGE,
116 CONFUSION,
117 CURSE,
118 DEFENSECURL,
119 DESTINYBOND,
120 DIG,
121 DISABLE,
122 DIVE,
123 ELECTRIFY,
124 ELECTROSHOT,
125 EMBARGO,
126 ENCORE,
127 ENDURE,
128 FLASHFIRE,
129 FLINCH,
130 FLY,
131 FOCUSENERGY,
132 FOLLOWME,
133 FORESIGHT,
134 FREEZESHOCK,
135 GASTROACID,
136 GEOMANCY,
137 GLAIVERUSH,
138 GRUDGE,
139 HEALBLOCK,
140 HELPINGHAND,
141 ICEBURN,
142 IMPRISON,
143 INGRAIN,
144 KINGSSHIELD,
145 LASERFOCUS,
146 LEECHSEED,
147 LIGHTSCREEN,
148 LOCKEDMOVE,
149 MAGICCOAT,
150 MAGNETRISE,
151 MAXGUARD,
152 METEORBEAM,
153 MINIMIZE,
154 MIRACLEEYE,
155 MUSTRECHARGE,
156 NIGHTMARE,
157 NORETREAT,
158 OCTOLOCK,
159 PARTIALLYTRAPPED,
160 PERISH4,
161 PERISH3,
162 PERISH2,
163 PERISH1,
164 PHANTOMFORCE,
165 POWDER,
166 POWERSHIFT,
167 POWERTRICK,
168 PROTECT,
169 PROTOSYNTHESISATK,
170 PROTOSYNTHESISDEF,
171 PROTOSYNTHESISSPA,
172 PROTOSYNTHESISSPD,
173 PROTOSYNTHESISSPE,
174 QUARKDRIVEATK,
175 QUARKDRIVEDEF,
176 QUARKDRIVESPA,
177 QUARKDRIVESPD,
178 QUARKDRIVESPE,
179 RAGE,
180 RAGEPOWDER,
181 RAZORWIND,
182 REFLECT,
183 ROOST,
184 SALTCURE,
185 SHADOWFORCE,
186 SKULLBASH,
187 SKYATTACK,
188 SKYDROP,
189 SILKTRAP,
190 SLOWSTART,
191 SMACKDOWN,
192 SNATCH,
193 SOLARBEAM,
194 SOLARBLADE,
195 SPARKLINGARIA,
196 SPIKYSHIELD,
197 SPOTLIGHT,
198 STOCKPILE,
199 SUBSTITUTE,
200 SYRUPBOMB,
201 TARSHOT,
202 TAUNT,
203 TELEKINESIS,
204 THROATCHOP,
205 TRUANT,
206 TORMENT,
207 TYPECHANGE,
208 UNBURDEN,
209 UPROAR,
210 YAWN,
211 },
212 default = NONE
213}
214
215define_enum_with_from_str! {
216 #[repr(u8)]
217 #[derive(Debug, PartialEq, Copy, Clone)]
218 Weather {
219 NONE,
220 SUN,
221 RAIN,
222 SAND,
223 HAIL,
224 SNOW,
225 HARSHSUN,
226 HEAVYRAIN,
227 }
228}
229
230define_enum_with_from_str! {
231 #[repr(u8)]
232 #[derive(Debug, PartialEq, Copy, Clone)]
233 Terrain {
234 NONE,
235 ELECTRICTERRAIN,
236 PSYCHICTERRAIN,
237 MISTYTERRAIN,
238 GRASSYTERRAIN,
239 }
240}
241
242impl Pokemon {
243 pub fn recalculate_stats(
244 &mut self,
245 side_ref: &SideReference,
246 instructions: &mut StateInstructions,
247 ) {
248 let stats = self.calculate_stats_from_base_stats();
250 if stats.1 != self.attack {
251 let ins = Instruction::ChangeAttack(ChangeStatInstruction {
252 side_ref: *side_ref,
253 amount: stats.1 - self.attack,
254 });
255 self.attack = stats.1;
256 instructions.instruction_list.push(ins);
257 }
258 if stats.2 != self.defense {
259 let ins = Instruction::ChangeDefense(ChangeStatInstruction {
260 side_ref: *side_ref,
261 amount: stats.2 - self.defense,
262 });
263 self.defense = stats.2;
264 instructions.instruction_list.push(ins);
265 }
266 if stats.3 != self.special_attack {
267 let ins = Instruction::ChangeSpecialAttack(ChangeStatInstruction {
268 side_ref: *side_ref,
269 amount: stats.3 - self.special_attack,
270 });
271 self.special_attack = stats.3;
272 instructions.instruction_list.push(ins);
273 }
274 if stats.4 != self.special_defense {
275 let ins = Instruction::ChangeSpecialDefense(ChangeStatInstruction {
276 side_ref: *side_ref,
277 amount: stats.4 - self.special_defense,
278 });
279 self.special_defense = stats.4;
280 instructions.instruction_list.push(ins);
281 }
282 if stats.5 != self.speed {
283 let ins = Instruction::ChangeSpeed(ChangeStatInstruction {
284 side_ref: *side_ref,
285 amount: stats.5 - self.speed,
286 });
287 self.speed = stats.5;
288 instructions.instruction_list.push(ins);
289 }
290 }
291 pub fn calculate_stats_from_base_stats(&self) -> (i16, i16, i16, i16, i16, i16) {
292 let base_stats = self.id.base_stats();
293 (
294 (common_pkmn_stat_calc(base_stats.0 as u16, self.evs.0 as u16, self.level as u16)
295 + self.level as u16
296 + 10) as i16,
297 (common_pkmn_stat_calc(base_stats.1 as u16, self.evs.1 as u16, self.level as u16) + 5)
298 as i16,
299 (common_pkmn_stat_calc(base_stats.2 as u16, self.evs.2 as u16, self.level as u16) + 5)
300 as i16,
301 (common_pkmn_stat_calc(base_stats.3 as u16, self.evs.3 as u16, self.level as u16) + 5)
302 as i16,
303 (common_pkmn_stat_calc(base_stats.4 as u16, self.evs.4 as u16, self.level as u16) + 5)
304 as i16,
305 (common_pkmn_stat_calc(base_stats.5 as u16, self.evs.5 as u16, self.level as u16) + 5)
306 as i16,
307 )
308 }
309 pub fn add_available_moves(
310 &self,
311 vec: &mut Vec<MoveChoice>,
312 last_used_move: &LastUsedMove,
313 encored: bool,
314 taunted: bool,
315 can_tera: bool,
316 ) {
317 let mut iter = self.moves.into_iter();
318 while let Some(p) = iter.next() {
319 if !p.disabled && p.pp > 0 {
320 match last_used_move {
321 LastUsedMove::Move(last_used_move) => {
322 if encored && last_used_move != &iter.pokemon_move_index {
323 continue;
324 } else if (self.moves[last_used_move].id == Choices::BLOODMOON
325 || self.moves[last_used_move].id == Choices::GIGATONHAMMER)
326 && &iter.pokemon_move_index == last_used_move
327 {
328 continue;
329 }
330 }
331 _ => {
332 }
336 }
337 if (self.item == Items::ASSAULTVEST || taunted)
338 && self.moves[&iter.pokemon_move_index].choice.category == MoveCategory::Status
339 {
340 continue;
341 }
342 vec.push(MoveChoice::Move(iter.pokemon_move_index));
343 if can_tera {
344 vec.push(MoveChoice::MoveTera(iter.pokemon_move_index));
345 }
346 }
347 }
348 }
349
350 pub fn add_move_from_choice(&self, vec: &mut Vec<MoveChoice>, choice: Choices) {
351 let mut iter = self.moves.into_iter();
352 while let Some(p) = iter.next() {
353 if p.id == choice {
354 vec.push(MoveChoice::Move(iter.pokemon_move_index));
355 }
356 }
357 }
358
359 #[cfg(feature = "terastallization")]
360 pub fn has_type(&self, pkmn_type: &PokemonType) -> bool {
361 if self.terastallized {
362 pkmn_type == &self.tera_type
363 } else {
364 pkmn_type == &self.types.0 || pkmn_type == &self.types.1
365 }
366 }
367
368 #[cfg(not(feature = "terastallization"))]
369 pub fn has_type(&self, pkmn_type: &PokemonType) -> bool {
370 pkmn_type == &self.types.0 || pkmn_type == &self.types.1
371 }
372
373 pub fn item_is_permanent(&self) -> bool {
374 match self.item {
375 Items::LUSTROUSGLOBE => self.id == PokemonName::PALKIAORIGIN,
376 Items::GRISEOUSCORE => self.id == PokemonName::GIRATINAORIGIN,
377 Items::ADAMANTCRYSTAL => self.id == PokemonName::DIALGAORIGIN,
378 Items::RUSTEDSWORD => {
379 self.id == PokemonName::ZACIANCROWNED || self.id == PokemonName::ZACIAN
380 }
381 Items::RUSTEDSHIELD => {
382 self.id == PokemonName::ZAMAZENTACROWNED || self.id == PokemonName::ZAMAZENTA
383 }
384 Items::SPLASHPLATE => self.id == PokemonName::ARCEUSWATER,
385 Items::TOXICPLATE => self.id == PokemonName::ARCEUSPOISON,
386 Items::EARTHPLATE => self.id == PokemonName::ARCEUSGROUND,
387 Items::STONEPLATE => self.id == PokemonName::ARCEUSROCK,
388 Items::INSECTPLATE => self.id == PokemonName::ARCEUSBUG,
389 Items::SPOOKYPLATE => self.id == PokemonName::ARCEUSGHOST,
390 Items::IRONPLATE => self.id == PokemonName::ARCEUSSTEEL,
391 Items::FLAMEPLATE => self.id == PokemonName::ARCEUSFIRE,
392 Items::MEADOWPLATE => self.id == PokemonName::ARCEUSGRASS,
393 Items::ZAPPLATE => self.id == PokemonName::ARCEUSELECTRIC,
394 Items::MINDPLATE => self.id == PokemonName::ARCEUSPSYCHIC,
395 Items::ICICLEPLATE => self.id == PokemonName::ARCEUSICE,
396 Items::DRACOPLATE => self.id == PokemonName::ARCEUSDRAGON,
397 Items::DREADPLATE => self.id == PokemonName::ARCEUSDARK,
398 Items::FISTPLATE => self.id == PokemonName::ARCEUSFIGHTING,
399 Items::BLANKPLATE => self.id == PokemonName::ARCEUS,
400 Items::SKYPLATE => self.id == PokemonName::ARCEUSFLYING,
401 Items::PIXIEPLATE => self.id == PokemonName::ARCEUSFAIRY,
402 Items::BUGMEMORY => self.id == PokemonName::SILVALLYBUG,
403 Items::FIGHTINGMEMORY => self.id == PokemonName::SILVALLYFIGHTING,
404 Items::GHOSTMEMORY => self.id == PokemonName::SILVALLYGHOST,
405 Items::PSYCHICMEMORY => self.id == PokemonName::SILVALLYPSYCHIC,
406 Items::FLYINGMEMORY => self.id == PokemonName::SILVALLYFLYING,
407 Items::STEELMEMORY => self.id == PokemonName::SILVALLYSTEEL,
408 Items::ICEMEMORY => self.id == PokemonName::SILVALLYICE,
409 Items::POISONMEMORY => self.id == PokemonName::SILVALLYPOISON,
410 Items::FIREMEMORY => self.id == PokemonName::SILVALLYFIRE,
411 Items::DRAGONMEMORY => self.id == PokemonName::SILVALLYDRAGON,
412 Items::GROUNDMEMORY => self.id == PokemonName::SILVALLYGROUND,
413 Items::WATERMEMORY => self.id == PokemonName::SILVALLYWATER,
414 Items::DARKMEMORY => self.id == PokemonName::SILVALLYDARK,
415 Items::ROCKMEMORY => self.id == PokemonName::SILVALLYROCK,
416 Items::GRASSMEMORY => self.id == PokemonName::SILVALLYGRASS,
417 Items::FAIRYMEMORY => self.id == PokemonName::SILVALLYFAIRY,
418 Items::ELECTRICMEMORY => self.id == PokemonName::SILVALLYELECTRIC,
419 Items::CORNERSTONEMASK => {
420 self.id == PokemonName::OGERPONCORNERSTONE
421 || self.id == PokemonName::OGERPONCORNERSTONETERA
422 }
423 Items::HEARTHFLAMEMASK => {
424 self.id == PokemonName::OGERPONHEARTHFLAME
425 || self.id == PokemonName::OGERPONHEARTHFLAMETERA
426 }
427 Items::WELLSPRINGMASK => {
428 self.id == PokemonName::OGERPONWELLSPRING
429 || self.id == PokemonName::OGERPONWELLSPRINGTERA
430 }
431 _ => false,
432 }
433 }
434
435 pub fn item_can_be_removed(&self) -> bool {
436 if self.ability == Abilities::STICKYHOLD {
437 return false;
438 }
439 !self.item_is_permanent()
440 }
441
442 pub fn is_grounded(&self) -> bool {
443 if self.item == Items::IRONBALL {
444 return true;
445 }
446 if self.has_type(&PokemonType::FLYING)
447 || self.ability == Abilities::LEVITATE
448 || self.item == Items::AIRBALLOON
449 {
450 return false;
451 }
452 true
453 }
454
455 pub fn volatile_status_can_be_applied(
456 &self,
457 volatile_status: &PokemonVolatileStatus,
458 active_volatiles: &HashSet<PokemonVolatileStatus>,
459 first_move: bool,
460 ) -> bool {
461 if active_volatiles.contains(volatile_status) || self.hp == 0 {
462 return false;
463 }
464 match volatile_status {
465 PokemonVolatileStatus::LEECHSEED => {
466 if self.has_type(&PokemonType::GRASS)
467 || active_volatiles.contains(&PokemonVolatileStatus::SUBSTITUTE)
468 {
469 return false;
470 }
471 true
472 }
473 PokemonVolatileStatus::CONFUSION => {
474 if active_volatiles.contains(&PokemonVolatileStatus::SUBSTITUTE) {
475 return false;
476 }
477 true
478 }
479 PokemonVolatileStatus::SUBSTITUTE => self.hp > self.maxhp / 4,
480 PokemonVolatileStatus::FLINCH => {
481 if !first_move || [Abilities::INNERFOCUS].contains(&self.ability) {
482 return false;
483 }
484 true
485 }
486 PokemonVolatileStatus::PROTECT => first_move,
487 PokemonVolatileStatus::TAUNT
488 | PokemonVolatileStatus::TORMENT
489 | PokemonVolatileStatus::ENCORE
490 | PokemonVolatileStatus::DISABLE
491 | PokemonVolatileStatus::HEALBLOCK
492 | PokemonVolatileStatus::ATTRACT => self.ability != Abilities::AROMAVEIL,
493 _ => true,
494 }
495 }
496
497 pub fn immune_to_stats_lowered_by_opponent(
498 &self,
499 stat: &PokemonBoostableStat,
500 volatiles: &HashSet<PokemonVolatileStatus>,
501 ) -> bool {
502 if [
503 Abilities::CLEARBODY,
504 Abilities::WHITESMOKE,
505 Abilities::FULLMETALBODY,
506 ]
507 .contains(&self.ability)
508 || ([Items::CLEARAMULET].contains(&self.item))
509 {
510 return true;
511 }
512
513 if volatiles.contains(&PokemonVolatileStatus::SUBSTITUTE) {
514 return true;
515 }
516
517 if stat == &PokemonBoostableStat::Attack && self.ability == Abilities::HYPERCUTTER {
518 return true;
519 } else if stat == &PokemonBoostableStat::Accuracy && self.ability == Abilities::KEENEYE {
520 return true;
521 }
522
523 false
524 }
525}
526
527impl Side {
528 pub fn active_is_charging_move(&self) -> Option<PokemonMoveIndex> {
529 for volatile in self.volatile_statuses.iter() {
530 if let Some(choice) = charge_volatile_to_choice(volatile) {
531 let mut iter = self.get_active_immutable().moves.into_iter();
532 while let Some(mv) = iter.next() {
533 if mv.id == choice {
534 return Some(iter.pokemon_move_index);
535 }
536 }
537 }
538 }
539 None
540 }
541
542 pub fn calculate_highest_stat(&self) -> PokemonBoostableStat {
543 let mut highest_stat = PokemonBoostableStat::Attack;
544 let mut highest_stat_value = self.calculate_boosted_stat(PokemonBoostableStat::Attack);
545 for stat in [
546 PokemonBoostableStat::Defense,
547 PokemonBoostableStat::SpecialAttack,
548 PokemonBoostableStat::SpecialDefense,
549 PokemonBoostableStat::Speed,
550 ] {
551 let stat_value = self.calculate_boosted_stat(stat);
552 if stat_value > highest_stat_value {
553 highest_stat = stat;
554 highest_stat_value = stat_value;
555 }
556 }
557 highest_stat
558 }
559 pub fn get_boost_from_boost_enum(&self, boost_enum: &PokemonBoostableStat) -> i8 {
560 match boost_enum {
561 PokemonBoostableStat::Attack => self.attack_boost,
562 PokemonBoostableStat::Defense => self.defense_boost,
563 PokemonBoostableStat::SpecialAttack => self.special_attack_boost,
564 PokemonBoostableStat::SpecialDefense => self.special_defense_boost,
565 PokemonBoostableStat::Speed => self.speed_boost,
566 PokemonBoostableStat::Evasion => self.evasion_boost,
567 PokemonBoostableStat::Accuracy => self.accuracy_boost,
568 }
569 }
570
571 pub fn calculate_boosted_stat(&self, stat: PokemonBoostableStat) -> i16 {
572 let active = self.get_active_immutable();
577 match stat {
578 PokemonBoostableStat::Attack => {
579 #[cfg(feature = "gen4")]
580 let boost = if active.ability == Abilities::SIMPLE {
581 (self.attack_boost * 2).min(6).max(-6)
582 } else {
583 self.attack_boost
584 };
585
586 #[cfg(not(feature = "gen4"))]
587 let boost = self.attack_boost;
588
589 multiply_boost(boost, active.attack)
590 }
591 PokemonBoostableStat::Defense => {
592 #[cfg(feature = "gen4")]
593 let boost = if active.ability == Abilities::SIMPLE {
594 (self.defense_boost * 2).min(6).max(-6)
595 } else {
596 self.defense_boost
597 };
598 #[cfg(not(feature = "gen4"))]
599 let boost = self.defense_boost;
600
601 multiply_boost(boost, active.defense)
602 }
603 PokemonBoostableStat::SpecialAttack => {
604 #[cfg(feature = "gen4")]
605 let boost = if active.ability == Abilities::SIMPLE {
606 (self.special_attack_boost * 2).min(6).max(-6)
607 } else {
608 self.special_attack_boost
609 };
610 #[cfg(not(feature = "gen4"))]
611 let boost = self.special_attack_boost;
612
613 multiply_boost(boost, active.special_attack)
614 }
615 PokemonBoostableStat::SpecialDefense => {
616 #[cfg(feature = "gen4")]
617 let boost = if active.ability == Abilities::SIMPLE {
618 (self.special_defense_boost * 2).min(6).max(-6)
619 } else {
620 self.special_defense_boost
621 };
622 #[cfg(not(feature = "gen4"))]
623 let boost = self.special_defense_boost;
624
625 multiply_boost(boost, active.special_defense)
626 }
627 PokemonBoostableStat::Speed => {
628 #[cfg(feature = "gen4")]
629 let boost = if active.ability == Abilities::SIMPLE {
630 (self.speed_boost * 2).min(6).max(-6)
631 } else {
632 self.speed_boost
633 };
634 #[cfg(not(feature = "gen4"))]
635 let boost = self.speed_boost;
636
637 multiply_boost(boost, active.speed)
638 }
639 _ => {
640 panic!("Not implemented")
641 }
642 }
643 }
644
645 pub fn has_alive_non_rested_sleeping_pkmn(&self) -> bool {
646 for p in self.pokemon.into_iter() {
647 if p.status == PokemonStatus::SLEEP && p.hp > 0 && p.rest_turns == 0 {
648 return true;
649 }
650 }
651 false
652 }
653
654 #[cfg(not(feature = "terastallization"))]
655 pub fn can_use_tera(&self) -> bool {
656 false
657 }
658
659 #[cfg(feature = "terastallization")]
660 pub fn can_use_tera(&self) -> bool {
661 for p in self.pokemon.into_iter() {
662 if p.terastallized {
663 return false;
664 }
665 }
666 true
667 }
668
669 pub fn add_switches(&self, vec: &mut Vec<MoveChoice>) {
670 let mut iter = self.pokemon.into_iter();
671 while let Some(p) = iter.next() {
672 if p.hp > 0 && iter.pokemon_index != self.active_index {
673 vec.push(MoveChoice::Switch(iter.pokemon_index));
674 }
675 }
676 if vec.len() == 0 {
677 vec.push(MoveChoice::None);
678 }
679 }
680
681 pub fn trapped(&self, opponent_active: &Pokemon) -> bool {
682 let active_pkmn = self.get_active_immutable();
683 if self
684 .volatile_statuses
685 .contains(&PokemonVolatileStatus::LOCKEDMOVE)
686 || self
687 .volatile_statuses
688 .contains(&PokemonVolatileStatus::NORETREAT)
689 {
690 return true;
691 }
692 if active_pkmn.item == Items::SHEDSHELL || active_pkmn.has_type(&PokemonType::GHOST) {
693 return false;
694 } else if self
695 .volatile_statuses
696 .contains(&PokemonVolatileStatus::PARTIALLYTRAPPED)
697 {
698 return true;
699 } else if opponent_active.ability == Abilities::SHADOWTAG {
700 return true;
701 } else if opponent_active.ability == Abilities::ARENATRAP && active_pkmn.is_grounded() {
702 return true;
703 } else if opponent_active.ability == Abilities::MAGNETPULL
704 && active_pkmn.has_type(&PokemonType::STEEL)
705 {
706 return true;
707 }
708 false
709 }
710
711 pub fn num_fainted_pkmn(&self) -> i8 {
712 let mut count = 0;
713 for p in self.pokemon.into_iter() {
714 if p.hp == 0 {
715 count += 1;
716 }
717 }
718 count
719 }
720}
721
722impl State {
723 pub fn root_get_all_options(&self) -> (Vec<MoveChoice>, Vec<MoveChoice>) {
724 if self.team_preview {
725 let mut s1_options = Vec::with_capacity(6);
726 let mut s2_options = Vec::with_capacity(6);
727
728 let mut pkmn_iter = self.side_one.pokemon.into_iter();
729 while let Some(_) = pkmn_iter.next() {
730 if self.side_one.pokemon[pkmn_iter.pokemon_index].hp > 0 {
731 s1_options.push(MoveChoice::Switch(pkmn_iter.pokemon_index));
732 }
733 }
734 let mut pkmn_iter = self.side_two.pokemon.into_iter();
735 while let Some(_) = pkmn_iter.next() {
736 if self.side_two.pokemon[pkmn_iter.pokemon_index].hp > 0 {
737 s2_options.push(MoveChoice::Switch(pkmn_iter.pokemon_index));
738 }
739 }
740 return (s1_options, s2_options);
741 }
742
743 let (mut s1_options, mut s2_options) = self.get_all_options();
744
745 if self.side_one.force_trapped {
746 s1_options.retain(|x| match x {
747 MoveChoice::Move(_) | MoveChoice::MoveTera(_) => true,
748 MoveChoice::Switch(_) => false,
749 MoveChoice::None => true,
750 });
751 }
752 if self.side_one.slow_uturn_move {
753 s1_options.clear();
754 let encored = self
755 .side_one
756 .volatile_statuses
757 .contains(&PokemonVolatileStatus::ENCORE);
758 let taunted = self
759 .side_one
760 .volatile_statuses
761 .contains(&PokemonVolatileStatus::TAUNT);
762 self.side_one.get_active_immutable().add_available_moves(
763 &mut s1_options,
764 &self.side_one.last_used_move,
765 encored,
766 taunted,
767 self.side_one.can_use_tera(),
768 );
769 }
770
771 if self.side_two.force_trapped {
772 s2_options.retain(|x| match x {
773 MoveChoice::Move(_) | MoveChoice::MoveTera(_) => true,
774 MoveChoice::Switch(_) => false,
775 MoveChoice::None => true,
776 });
777 }
778 if self.side_two.slow_uturn_move {
779 s2_options.clear();
780 let encored = self
781 .side_two
782 .volatile_statuses
783 .contains(&PokemonVolatileStatus::ENCORE);
784 let taunted = self
785 .side_two
786 .volatile_statuses
787 .contains(&PokemonVolatileStatus::TAUNT);
788 self.side_two.get_active_immutable().add_available_moves(
789 &mut s2_options,
790 &self.side_two.last_used_move,
791 encored,
792 taunted,
793 self.side_two.can_use_tera(),
794 );
795 }
796
797 if s1_options.len() == 0 {
798 s1_options.push(MoveChoice::None);
799 }
800 if s2_options.len() == 0 {
801 s2_options.push(MoveChoice::None);
802 }
803
804 (s1_options, s2_options)
805 }
806
807 pub fn get_all_options(&self) -> (Vec<MoveChoice>, Vec<MoveChoice>) {
808 let mut side_one_options: Vec<MoveChoice> = Vec::with_capacity(9);
809 let mut side_two_options: Vec<MoveChoice> = Vec::with_capacity(9);
810
811 let side_one_active = self.side_one.get_active_immutable();
812 let side_two_active = self.side_two.get_active_immutable();
813
814 if self.side_one.force_switch {
815 self.side_one.add_switches(&mut side_one_options);
816 if self.side_two.switch_out_move_second_saved_move == Choices::NONE {
817 side_two_options.push(MoveChoice::None);
818 } else {
819 self.side_two.get_active_immutable().add_move_from_choice(
820 &mut side_two_options,
821 self.side_two.switch_out_move_second_saved_move,
822 );
823 }
824 return (side_one_options, side_two_options);
825 }
826
827 if self.side_two.force_switch {
828 self.side_two.add_switches(&mut side_two_options);
829 if self.side_one.switch_out_move_second_saved_move == Choices::NONE {
830 side_one_options.push(MoveChoice::None);
831 } else {
832 self.side_one.get_active_immutable().add_move_from_choice(
833 &mut side_one_options,
834 self.side_one.switch_out_move_second_saved_move,
835 );
836 }
837 return (side_one_options, side_two_options);
838 }
839
840 let side_one_force_switch = self.side_one.get_active_immutable().hp <= 0;
841 let side_two_force_switch = self.side_two.get_active_immutable().hp <= 0;
842
843 if side_one_force_switch && side_two_force_switch {
844 self.side_one.add_switches(&mut side_one_options);
845 self.side_two.add_switches(&mut side_two_options);
846 return (side_one_options, side_two_options);
847 }
848 if side_one_force_switch {
849 self.side_one.add_switches(&mut side_one_options);
850 side_two_options.push(MoveChoice::None);
851 return (side_one_options, side_two_options);
852 }
853 if side_two_force_switch {
854 side_one_options.push(MoveChoice::None);
855 self.side_two.add_switches(&mut side_two_options);
856 return (side_one_options, side_two_options);
857 }
858
859 if self
860 .side_one
861 .volatile_statuses
862 .contains(&PokemonVolatileStatus::MUSTRECHARGE)
863 {
864 side_one_options.push(MoveChoice::None);
865 } else if let Some(mv_index) = self.side_one.active_is_charging_move() {
866 side_one_options.push(MoveChoice::Move(mv_index));
867 } else {
868 let encored = self
869 .side_one
870 .volatile_statuses
871 .contains(&PokemonVolatileStatus::ENCORE);
872 let taunted = self
873 .side_one
874 .volatile_statuses
875 .contains(&PokemonVolatileStatus::TAUNT);
876 self.side_one.get_active_immutable().add_available_moves(
877 &mut side_one_options,
878 &self.side_one.last_used_move,
879 encored,
880 taunted,
881 self.side_one.can_use_tera(),
882 );
883 if !self.side_one.trapped(side_two_active) {
884 self.side_one.add_switches(&mut side_one_options);
885 }
886 }
887
888 if self
889 .side_two
890 .volatile_statuses
891 .contains(&PokemonVolatileStatus::MUSTRECHARGE)
892 {
893 side_two_options.push(MoveChoice::None);
894 } else if let Some(mv_index) = self.side_two.active_is_charging_move() {
895 side_two_options.push(MoveChoice::Move(mv_index));
896 } else {
897 let encored = self
898 .side_two
899 .volatile_statuses
900 .contains(&PokemonVolatileStatus::ENCORE);
901 let taunted = self
902 .side_two
903 .volatile_statuses
904 .contains(&PokemonVolatileStatus::TAUNT);
905 self.side_two.get_active_immutable().add_available_moves(
906 &mut side_two_options,
907 &self.side_two.last_used_move,
908 encored,
909 taunted,
910 self.side_two.can_use_tera(),
911 );
912 if !self.side_two.trapped(side_one_active) {
913 self.side_two.add_switches(&mut side_two_options);
914 }
915 }
916
917 if side_one_options.len() == 0 {
918 side_one_options.push(MoveChoice::None);
919 }
920 if side_two_options.len() == 0 {
921 side_two_options.push(MoveChoice::None);
922 }
923
924 (side_one_options, side_two_options)
925 }
926
927 pub fn reset_toxic_count(
928 &mut self,
929 side_ref: &SideReference,
930 vec_to_add_to: &mut Vec<Instruction>,
931 ) {
932 let side = self.get_side(side_ref);
933 if side.side_conditions.toxic_count > 0 {
934 vec_to_add_to.push(Instruction::ChangeSideCondition(
935 ChangeSideConditionInstruction {
936 side_ref: *side_ref,
937 side_condition: PokemonSideCondition::ToxicCount,
938 amount: -1 * side.side_conditions.toxic_count,
939 },
940 ));
941 side.side_conditions.toxic_count = 0;
942 }
943 }
944
945 pub fn remove_volatile_statuses_on_switch(
946 &mut self,
947 side_ref: &SideReference,
948 instructions: &mut Vec<Instruction>,
949 baton_passing: bool,
950 shed_tailing: bool,
951 ) {
952 let side = self.get_side(side_ref);
953
954 let mut volatile_statuses = std::mem::take(&mut side.volatile_statuses);
957
958 volatile_statuses.retain(|pkmn_volatile_status| {
959 let should_retain = match pkmn_volatile_status {
960 PokemonVolatileStatus::SUBSTITUTE => baton_passing || shed_tailing,
961 PokemonVolatileStatus::LEECHSEED => baton_passing,
962 PokemonVolatileStatus::TYPECHANGE => {
963 let active = side.get_active();
964 if active.base_types != active.types {
965 instructions.push(Instruction::ChangeType(ChangeType {
966 side_ref: *side_ref,
967 new_types: active.base_types,
968 old_types: active.types,
969 }));
970 active.types = active.base_types;
971 }
972 false
973 }
974 PokemonVolatileStatus::LOCKEDMOVE => {
976 instructions.push(Instruction::ChangeVolatileStatusDuration(
977 ChangeVolatileStatusDurationInstruction {
978 side_ref: *side_ref,
979 volatile_status: *pkmn_volatile_status,
980 amount: -1 * side.volatile_status_durations.lockedmove,
981 },
982 ));
983 side.volatile_status_durations.lockedmove = 0;
984 false
985 }
986 PokemonVolatileStatus::YAWN => {
987 instructions.push(Instruction::ChangeVolatileStatusDuration(
988 ChangeVolatileStatusDurationInstruction {
989 side_ref: *side_ref,
990 volatile_status: *pkmn_volatile_status,
991 amount: -1 * side.volatile_status_durations.yawn,
992 },
993 ));
994 side.volatile_status_durations.yawn = 0;
995 false
996 }
997 PokemonVolatileStatus::TAUNT => {
998 instructions.push(Instruction::ChangeVolatileStatusDuration(
999 ChangeVolatileStatusDurationInstruction {
1000 side_ref: *side_ref,
1001 volatile_status: *pkmn_volatile_status,
1002 amount: -1 * side.volatile_status_durations.taunt,
1003 },
1004 ));
1005 side.volatile_status_durations.taunt = 0;
1006 false
1007 }
1008 _ => false,
1009 };
1010
1011 if !should_retain {
1012 instructions.push(Instruction::RemoveVolatileStatus(
1013 RemoveVolatileStatusInstruction {
1014 side_ref: *side_ref,
1015 volatile_status: *pkmn_volatile_status,
1016 },
1017 ));
1018 }
1019 should_retain
1020 });
1021
1022 side.volatile_statuses = volatile_statuses;
1024 }
1025
1026 pub fn terrain_is_active(&self, terrain: &Terrain) -> bool {
1027 &self.terrain.terrain_type == terrain && self.terrain.turns_remaining > 0
1028 }
1029
1030 pub fn weather_is_active(&self, weather: &Weather) -> bool {
1031 let s1_active = self.side_one.get_active_immutable();
1032 let s2_active = self.side_two.get_active_immutable();
1033 &self.weather.weather_type == weather
1034 && s1_active.ability != Abilities::AIRLOCK
1035 && s1_active.ability != Abilities::CLOUDNINE
1036 && s2_active.ability != Abilities::AIRLOCK
1037 && s2_active.ability != Abilities::CLOUDNINE
1038 }
1039
1040 fn _state_contains_any_move(&self, moves: &[Choices]) -> bool {
1041 for s in [&self.side_one, &self.side_two] {
1042 for pkmn in s.pokemon.into_iter() {
1043 for mv in pkmn.moves.into_iter() {
1044 if moves.contains(&mv.id) {
1045 return true;
1046 }
1047 }
1048 }
1049 }
1050
1051 false
1052 }
1053
1054 pub fn set_damage_dealt_flag(&mut self) {
1055 if self._state_contains_any_move(&[
1056 Choices::COUNTER,
1057 Choices::MIRRORCOAT,
1058 Choices::METALBURST,
1059 Choices::COMEUPPANCE,
1060 Choices::FOCUSPUNCH,
1061 ]) {
1062 self.use_damage_dealt = true
1063 }
1064 }
1065
1066 pub fn set_last_used_move_flag(&mut self) {
1067 if self._state_contains_any_move(&[
1068 Choices::ENCORE,
1069 Choices::FAKEOUT,
1070 Choices::FIRSTIMPRESSION,
1071 Choices::BLOODMOON,
1072 Choices::GIGATONHAMMER,
1073 ]) {
1074 self.use_last_used_move = true
1075 }
1076 }
1077
1078 pub fn set_conditional_mechanics(&mut self) {
1079 self.set_damage_dealt_flag();
1085 self.set_last_used_move_flag();
1086 }
1087}