1#![allow(unused_variables)]
2use crate::choices::{
3 Boost, Choice, Choices, Effect, Heal, MoveCategory, MoveTarget, Secondary, StatBoosts,
4 VolatileStatus,
5};
6use crate::damage_calc::type_effectiveness_modifier;
7use crate::define_enum_with_from_str;
8use crate::generate_instructions::{add_remove_status_instructions, get_boost_instruction};
9use crate::instruction::{
10 ApplyVolatileStatusInstruction, BoostInstruction, ChangeAbilityInstruction,
11 ChangeItemInstruction, ChangeSideConditionInstruction, ChangeStatusInstruction, ChangeTerrain,
12 ChangeType, ChangeWeather, DamageInstruction, FormeChangeInstruction, HealInstruction,
13 Instruction, StateInstructions,
14};
15use crate::items::{get_choice_move_disable_instructions, Items};
16use crate::pokemon::PokemonName;
17use crate::state::{PokemonBoostableStat, PokemonSideCondition, PokemonType, Side, Terrain};
18use crate::state::{PokemonStatus, State};
19use crate::state::{PokemonVolatileStatus, SideReference, Weather};
20use std::cmp;
21
22#[cfg(any(feature = "gen3", feature = "gen4", feature = "gen5"))]
23pub const WEATHER_ABILITY_TURNS: i8 = -1;
24
25#[cfg(any(feature = "gen6", feature = "gen7", feature = "gen8", feature = "gen9"))]
26pub const WEATHER_ABILITY_TURNS: i8 = 5;
27
28define_enum_with_from_str! {
29 #[repr(u16)]
30 #[derive(PartialEq, Debug, Clone)]
31 Abilities {
32 NONE,
33 ARMORTAIL,
34 RIPEN,
35 TANGLEDFEET,
36 DRAGONSMAW,
37 CLEARBODY,
38 GALVANIZE,
39 VITALSPIRIT,
40 AERILATE,
41 DEFIANT,
42 CUTECHARM,
43 NEUROFORCE,
44 SOUNDPROOF,
45 RKSSYSTEM,
46 POISONPOINT,
47 STAKEOUT,
48 UNNERVE,
49 ROCKHEAD,
50 AURABREAK,
51 MIMICRY,
52 BULLETPROOF,
53 POWEROFALCHEMY,
54 TECHNICIAN,
55 MULTISCALE,
56 ARENATRAP,
57 BATTLEBOND,
58 DISGUISE,
59 EARLYBIRD,
60 LIGHTNINGROD,
61 MAGICIAN,
62 REFRIGERATE,
63 FRIENDGUARD,
64 NOABILITY,
65 GULPMISSILE,
66 POWERCONSTRUCT,
67 FORECAST,
68 PRANKSTER,
69 PROTEAN,
70 ASONEGLASTRIER,
71 SHADOWTAG,
72 SHARPNESS,
73 WINDRIDER,
74 SKILLLINK,
75 INTREPIDSWORD,
76 SOULHEART,
77 SWIFTSWIM,
78 EARTHEATER,
79 SUPERLUCK,
80 SUPREMEOVERLORD,
81 INSOMNIA,
82 DANCER,
83 STEAMENGINE,
84 ANGERPOINT,
85 CONTRARY,
86 MAGMAARMOR,
87 HUNGERSWITCH,
88 RECEIVER,
89 ZENMODE,
90 EMERGENCYEXIT,
91 ILLUSION,
92 WEAKARMOR,
93 DROUGHT,
94 INNARDSOUT,
95 SHIELDSDOWN,
96 ADAPTABILITY,
97 CORROSION,
98 LONGREACH,
99 PUREPOWER,
100 TINTEDLENS,
101 QUEENLYMAJESTY,
102 DESOLATELAND,
103 MOXIE,
104 SAPSIPPER,
105 SLUSHRUSH,
106 BIGPECKS,
107 STALL,
108 WHITESMOKE,
109 FLAREBOOST,
110 SHADOWSHIELD,
111 LIQUIDVOICE,
112 MISTYSURGE,
113 MULTITYPE,
114 NOGUARD,
115 TORRENT,
116 DELTASTREAM,
117 KLUTZ,
118 LIBERO,
119 SERENEGRACE,
120 CURSEDBODY,
121 UNAWARE,
122 LIGHTMETAL,
123 MARVELSCALE,
124 TELEPATHY,
125 QUICKDRAW,
126 HYPERCUTTER,
127 SYMBIOSIS,
128 PLUS,
129 MIRRORARMOR,
130 PASTELVEIL,
131 TOUGHCLAWS,
132 EFFECTSPORE,
133 MUMMY,
134 BADDREAMS,
135 MAGICGUARD,
136 SANDSTREAM,
137 POWERSPOT,
138 FLAMEBODY,
139 RECKLESS,
140 PRESSURE,
141 GOOEY,
142 IMMUNITY,
143 LEAFGUARD,
144 HUGEPOWER,
145 SOLARPOWER,
146 SCHOOLING,
147 MOTORDRIVE,
148 ANTICIPATION,
149 MERCILESS,
150 TRACE,
151 NATURALCURE,
152 HARVEST,
153 SUCTIONCUPS,
154 ICEFACE,
155 ROUGHSKIN,
156 WONDERGUARD,
157 WATERVEIL,
158 FAIRYAURA,
159 SANDSPIT,
160 SEEDSOWER,
161 TOXICDEBRIS,
162 INTIMIDATE,
163 DAUNTLESSSHIELD,
164 AROMAVEIL,
165 AIRLOCK,
166 NORMALIZE,
167 DARKAURA,
168 VICTORYSTAR,
169 GRASSYSURGE,
170 STURDY,
171 PICKPOCKET,
172 ELECTRICSURGE,
173 HADRONENGINE,
174 ORICHALCUMPULSE,
175 RUNAWAY,
176 OBLIVIOUS,
177 SURGESURFER,
178 LEVITATE,
179 ASONESPECTRIER,
180 PICKUP,
181 ICEBODY,
182 CURIOUSMEDICINE,
183 FLOWERVEIL,
184 STATIC,
185 WONDERSKIN,
186 OVERGROW,
187 PROPELLERTAIL,
188 THICKFAT,
189 GLUTTONY,
190 KEENEYE,
191 MOUNTAINEER,
192 FLASHFIRE,
193 COMPOUNDEYES,
194 STEELWORKER,
195 COMATOSE,
196 BALLFETCH,
197 DAZZLING,
198 DOWNLOAD,
199 TRANSISTOR,
200 MOLDBREAKER,
201 MYCELIUMMIGHT,
202 LIQUIDOOZE,
203 POISONHEAL,
204 PRISMARMOR,
205 SNIPER,
206 STENCH,
207 COMPETITIVE,
208 SWARM,
209 STALWART,
210 ILLUMINATE,
211 TURBOBLAZE,
212 GORILLATACTICS,
213 SPEEDBOOST,
214 GUARDDOG,
215 HEATPROOF,
216 SNOWCLOAK,
217 TERAVOLT,
218 CHILLINGNEIGH,
219 SHIELDDUST,
220 RIVALRY,
221 PRIMORDIALSEA,
222 SCREENCLEANER,
223 MAGNETPULL,
224 HONEYGATHER,
225 COTTONDOWN,
226 GRASSPELT,
227 BATTLEARMOR,
228 BEASTBOOST,
229 BERSERK,
230 MINUS,
231 RAINDISH,
232 SYNCHRONIZE,
233 FILTER,
234 TRUANT,
235 FURCOAT,
236 FULLMETALBODY,
237 REGENERATOR,
238 FOREWARN,
239 IRONBARBS,
240 STAMINA,
241 SANDRUSH,
242 COLORCHANGE,
243 BLAZE,
244 ANALYTIC,
245 TANGLINGHAIR,
246 CLOUDNINE,
247 STEELYSPIRIT,
248 QUICKFEET,
249 MAGICBOUNCE,
250 MEGALAUNCHER,
251 HEAVYMETAL,
252 STORMDRAIN,
253 PIXILATE,
254 WATERCOMPACTION,
255 JUSTIFIED,
256 SLOWSTART,
257 SNOWWARNING,
258 FLOWERGIFT,
259 SHEDSKIN,
260 WIMPOUT,
261 ICESCALES,
262 INFILTRATOR,
263 LIMBER,
264 PSYCHICSURGE,
265 DEFEATIST,
266 WATERABSORB,
267 IMPOSTER,
268 DRYSKIN,
269 FLUFFY,
270 UNBURDEN,
271 CHEEKPOUCH,
272 STANCECHANGE,
273 MOODY,
274 ROCKYPAYLOAD,
275 PUNKROCK,
276 SANDVEIL,
277 PARENTALBOND,
278 STRONGJAW,
279 BATTERY,
280 HEALER,
281 STEADFAST,
282 DAMP,
283 PERISHBODY,
284 TRIAGE,
285 SHEERFORCE,
286 OWNTEMPO,
287 FRISK,
288 VOLTABSORB,
289 GALEWINGS,
290 AFTERMATH,
291 STICKYHOLD,
292 GRIMNEIGH,
293 IRONFIST,
294 REBOUND,
295 UNSEENFIST,
296 SOLIDROCK,
297 HUSTLE,
298 HYDRATION,
299 SCRAPPY,
300 MINDSEYE,
301 OVERCOAT,
302 NEUTRALIZINGGAS,
303 SWEETVEIL,
304 DRIZZLE,
305 INNERFOCUS,
306 POISONTOUCH,
307 WANDERINGSPIRIT,
308 GUTS,
309 SHELLARMOR,
310 RATTLED,
311 WATERBUBBLE,
312 SANDFORCE,
313 TOXICBOOST,
314 PERSISTENT,
315 CHLOROPHYLL,
316 SIMPLE,
317 PURIFYINGSALT,
318 EMBODYASPECTWELLSPRING,
319 EMBODYASPECTCORNERSTONE,
320 EMBODYASPECTHEARTHFLAME,
321 EMBODYASPECTTEAL,
322 ANGERSHELL,
323 BEADSOFRUIN,
324 COMMANDER,
325 COSTAR,
326 CUDCHEW,
327 ELECTROMORPHOSIS,
328 EMBODYASPECT,
329 GOODASGOLD,
330 HOSPITALITY,
331 LINGERINGAROMA,
332 OPPORTUNIST,
333 POISONPUPPETEER,
334 PROTOSYNTHESIS,
335 QUARKDRIVE,
336 SUPERSWEETSYRUP,
337 SWORDOFRUIN,
338 TABLETSOFRUIN,
339 TERASHELL,
340 TERASHIFT,
341 TERAFORMZERO,
342 THERMALEXCHANGE,
343 TOXICCHAIN,
344 VESSELOFRUIN,
345 WELLBAKEDBODY,
346 WINDPOWER,
347 ZEROTOHERO,
348 },
349 default = NONE
350}
351
352fn mold_breaker_ignores(ability: &Abilities) -> bool {
354 match ability {
355 Abilities::BATTLEARMOR
356 | Abilities::CLEARBODY
357 | Abilities::ARMORTAIL
358 | Abilities::EARTHEATER
359 | Abilities::GUARDDOG
360 | Abilities::GOODASGOLD
361 | Abilities::ILLUMINATE
362 | Abilities::MINDSEYE
363 | Abilities::PURIFYINGSALT
364 | Abilities::TERASHELL
365 | Abilities::TABLETSOFRUIN
366 | Abilities::THERMALEXCHANGE
367 | Abilities::WELLBAKEDBODY
368 | Abilities::VESSELOFRUIN
369 | Abilities::DAMP
370 | Abilities::DRYSKIN
371 | Abilities::FILTER
372 | Abilities::FLASHFIRE
373 | Abilities::FLOWERGIFT
374 | Abilities::HEATPROOF
375 | Abilities::HYPERCUTTER
376 | Abilities::IMMUNITY
377 | Abilities::INNERFOCUS
378 | Abilities::INSOMNIA
379 | Abilities::KEENEYE
380 | Abilities::LEAFGUARD
381 | Abilities::LEVITATE
382 | Abilities::LIGHTNINGROD
383 | Abilities::LIMBER
384 | Abilities::MAGMAARMOR
385 | Abilities::MARVELSCALE
386 | Abilities::MOTORDRIVE
387 | Abilities::WINDRIDER
388 | Abilities::OBLIVIOUS
389 | Abilities::OWNTEMPO
390 | Abilities::SANDVEIL
391 | Abilities::SHELLARMOR
392 | Abilities::SHIELDDUST
393 | Abilities::SIMPLE
394 | Abilities::SNOWCLOAK
395 | Abilities::SOLIDROCK
396 | Abilities::SOUNDPROOF
397 | Abilities::STICKYHOLD
398 | Abilities::STORMDRAIN
399 | Abilities::STURDY
400 | Abilities::SUCTIONCUPS
401 | Abilities::TANGLEDFEET
402 | Abilities::THICKFAT
403 | Abilities::UNAWARE
404 | Abilities::VITALSPIRIT
405 | Abilities::VOLTABSORB
406 | Abilities::WATERABSORB
407 | Abilities::WATERVEIL
408 | Abilities::WHITESMOKE
409 | Abilities::WONDERGUARD
410 | Abilities::BIGPECKS
411 | Abilities::CONTRARY
412 | Abilities::FRIENDGUARD
413 | Abilities::HEAVYMETAL
414 | Abilities::LIGHTMETAL
415 | Abilities::MAGICBOUNCE
416 | Abilities::MULTISCALE
417 | Abilities::SAPSIPPER
418 | Abilities::TELEPATHY
419 | Abilities::WONDERSKIN
420 | Abilities::AURABREAK
421 | Abilities::AROMAVEIL
422 | Abilities::BULLETPROOF
423 | Abilities::FLOWERVEIL
424 | Abilities::FURCOAT
425 | Abilities::OVERCOAT
426 | Abilities::SWEETVEIL
427 | Abilities::DAZZLING
428 | Abilities::DISGUISE
429 | Abilities::FLUFFY
430 | Abilities::QUEENLYMAJESTY
431 | Abilities::WATERBUBBLE
432 | Abilities::ICESCALES
433 | Abilities::ICEFACE
434 | Abilities::MIRRORARMOR
435 | Abilities::PASTELVEIL
436 | Abilities::PUNKROCK
437 | Abilities::FAIRYAURA
438 | Abilities::DARKAURA => true,
439 _ => false,
440 }
441}
442
443fn protosynthesus_or_quarkdrive_on_switch_in(
444 thing_is_active: bool,
445 volatile: PokemonVolatileStatus,
446 instructions: &mut StateInstructions,
447 attacking_side: &mut Side,
448 side_ref: &SideReference,
449) {
450 let active_pkmn = attacking_side.get_active();
451 if thing_is_active {
452 instructions
453 .instruction_list
454 .push(Instruction::ApplyVolatileStatus(
455 ApplyVolatileStatusInstruction {
456 side_ref: *side_ref,
457 volatile_status: volatile,
458 },
459 ));
460 attacking_side.volatile_statuses.insert(volatile);
461 } else if active_pkmn.item == Items::BOOSTERENERGY {
462 instructions
463 .instruction_list
464 .push(Instruction::ChangeItem(ChangeItemInstruction {
465 side_ref: *side_ref,
466 current_item: Items::BOOSTERENERGY,
467 new_item: Items::NONE,
468 }));
469 instructions
470 .instruction_list
471 .push(Instruction::ApplyVolatileStatus(
472 ApplyVolatileStatusInstruction {
473 side_ref: *side_ref,
474 volatile_status: volatile,
475 },
476 ));
477 active_pkmn.item = Items::NONE;
478 attacking_side.volatile_statuses.insert(volatile);
479 }
480}
481
482fn protosynthesis_volatile_from_side(side: &Side) -> PokemonVolatileStatus {
483 match side.calculate_highest_stat() {
484 PokemonBoostableStat::Attack => PokemonVolatileStatus::PROTOSYNTHESISATK,
485 PokemonBoostableStat::Defense => PokemonVolatileStatus::PROTOSYNTHESISDEF,
486 PokemonBoostableStat::SpecialAttack => PokemonVolatileStatus::PROTOSYNTHESISSPA,
487 PokemonBoostableStat::SpecialDefense => PokemonVolatileStatus::PROTOSYNTHESISSPD,
488 PokemonBoostableStat::Speed => PokemonVolatileStatus::PROTOSYNTHESISSPE,
489 _ => panic!("Invalid stat for protosynthesis"),
490 }
491}
492
493fn quarkdrive_volatile_from_side(side: &Side) -> PokemonVolatileStatus {
494 match side.calculate_highest_stat() {
495 PokemonBoostableStat::Attack => PokemonVolatileStatus::QUARKDRIVEATK,
496 PokemonBoostableStat::Defense => PokemonVolatileStatus::QUARKDRIVEDEF,
497 PokemonBoostableStat::SpecialAttack => PokemonVolatileStatus::QUARKDRIVESPA,
498 PokemonBoostableStat::SpecialDefense => PokemonVolatileStatus::QUARKDRIVESPD,
499 PokemonBoostableStat::Speed => PokemonVolatileStatus::QUARKDRIVESPE,
500 _ => panic!("Invalid stat for quarkdrive"),
501 }
502}
503
504pub fn ability_before_move(
505 state: &mut State,
506 choice: &mut Choice,
507 side_ref: &SideReference,
508 instructions: &mut StateInstructions,
509) {
510 let (attacking_side, defending_side) = state.get_both_sides(side_ref);
511 let active_pkmn = attacking_side.get_active();
512 let defending_pkmn = defending_side.get_active();
513
514 match defending_pkmn.ability {
515 Abilities::NEUTRALIZINGGAS => {
516 return;
517 }
518 Abilities::ICEFACE => {
521 if defending_pkmn.id == PokemonName::EISCUE && choice.category == MoveCategory::Physical
522 {
523 choice.category = MoveCategory::Status;
527 instructions.instruction_list.push(Instruction::FormeChange(
528 FormeChangeInstruction {
529 side_ref: side_ref.get_other_side(),
530 previous_forme: defending_pkmn.id,
531 new_forme: PokemonName::EISCUENOICE,
532 },
533 ));
534 defending_pkmn.id = PokemonName::EISCUENOICE;
535 defending_pkmn.recalculate_stats(&side_ref.get_other_side(), instructions);
536 }
537 }
538 #[cfg(not(any(feature = "gen8", feature = "gen9")))]
541 Abilities::DISGUISE
542 if (choice.category == MoveCategory::Physical
543 || choice.category == MoveCategory::Special)
544 && (defending_pkmn.id == PokemonName::MIMIKYU
545 || defending_pkmn.id == PokemonName::MIMIKYUTOTEM) =>
546 {
547 choice.base_power = 0.0;
548 instructions
549 .instruction_list
550 .push(Instruction::FormeChange(FormeChangeInstruction {
551 side_ref: side_ref.get_other_side(),
552 previous_forme: defending_pkmn.id,
553 new_forme: PokemonName::MIMIKYUBUSTED,
554 }));
555 defending_pkmn.id = PokemonName::MIMIKYUBUSTED;
556 }
557 #[cfg(any(feature = "gen8", feature = "gen9"))]
558 Abilities::DISGUISE
559 if (choice.category == MoveCategory::Physical
560 || choice.category == MoveCategory::Special)
561 && (defending_pkmn.id == PokemonName::MIMIKYU
562 || defending_pkmn.id == PokemonName::MIMIKYUTOTEM) =>
563 {
564 choice.base_power = 0.0;
565 instructions
566 .instruction_list
567 .push(Instruction::FormeChange(FormeChangeInstruction {
568 side_ref: side_ref.get_other_side(),
569 previous_forme: defending_pkmn.id,
570 new_forme: PokemonName::MIMIKYUBUSTED,
571 }));
572 defending_pkmn.id = PokemonName::MIMIKYUBUSTED;
573 let dmg = cmp::min(defending_pkmn.hp, defending_pkmn.maxhp / 8);
574 instructions
575 .instruction_list
576 .push(Instruction::Damage(DamageInstruction {
577 side_ref: side_ref.get_other_side(),
578 damage_amount: dmg,
579 }));
580 defending_pkmn.hp -= dmg;
581 }
582 _ => {}
583 }
584 match active_pkmn.ability {
585 Abilities::GULPMISSILE => {
586 if active_pkmn.id == PokemonName::CRAMORANT
587 && (choice.move_id == Choices::SURF || choice.move_id == Choices::DIVE)
588 {
589 let new_forme = if active_pkmn.hp > active_pkmn.maxhp / 2 {
590 PokemonName::CRAMORANTGULPING
591 } else {
592 PokemonName::CRAMORANTGORGING
593 };
594 instructions.instruction_list.push(Instruction::FormeChange(
595 FormeChangeInstruction {
596 side_ref: *side_ref,
597 new_forme,
598 previous_forme: PokemonName::CRAMORANT,
599 },
600 ));
601 active_pkmn.id = new_forme;
602 }
603 }
604 #[cfg(feature = "gen9")]
605 Abilities::PROTEAN | Abilities::LIBERO => {
606 if !attacking_side
607 .volatile_statuses
608 .contains(&PokemonVolatileStatus::TYPECHANGE)
609 {
610 let active_pkmn = attacking_side.get_active();
611 if !active_pkmn.has_type(&choice.move_type) && !active_pkmn.terastallized {
612 instructions
613 .instruction_list
614 .push(Instruction::ChangeType(ChangeType {
615 side_ref: *side_ref,
616 new_types: (choice.move_type, PokemonType::TYPELESS),
617 old_types: active_pkmn.types,
618 }));
619 active_pkmn.types = (choice.move_type, PokemonType::TYPELESS);
620
621 instructions
622 .instruction_list
623 .push(Instruction::ApplyVolatileStatus(
624 ApplyVolatileStatusInstruction {
625 side_ref: *side_ref,
626 volatile_status: PokemonVolatileStatus::TYPECHANGE,
627 },
628 ));
629 attacking_side
630 .volatile_statuses
631 .insert(PokemonVolatileStatus::TYPECHANGE);
632 }
633 }
634 }
635 #[cfg(any(feature = "gen6", feature = "gen7", feature = "gen8"))]
636 Abilities::PROTEAN | Abilities::LIBERO => {
637 if !active_pkmn.has_type(&choice.move_type) && !active_pkmn.terastallized {
638 instructions
639 .instruction_list
640 .push(Instruction::ChangeType(ChangeType {
641 side_ref: *side_ref,
642 new_types: (choice.move_type, PokemonType::TYPELESS),
643 old_types: active_pkmn.types,
644 }));
645 active_pkmn.types = (choice.move_type, PokemonType::TYPELESS);
646 if !attacking_side
647 .volatile_statuses
648 .contains(&PokemonVolatileStatus::TYPECHANGE)
649 {
650 instructions
651 .instruction_list
652 .push(Instruction::ApplyVolatileStatus(
653 ApplyVolatileStatusInstruction {
654 side_ref: *side_ref,
655 volatile_status: PokemonVolatileStatus::TYPECHANGE,
656 },
657 ));
658 attacking_side
659 .volatile_statuses
660 .insert(PokemonVolatileStatus::TYPECHANGE);
661 }
662 }
663 }
664 Abilities::GORILLATACTICS => {
665 let ins = get_choice_move_disable_instructions(active_pkmn, side_ref, &choice.move_id);
666 for i in ins {
667 state.apply_one_instruction(&i);
668 instructions.instruction_list.push(i);
669 }
670 }
671 _ => {}
672 }
673}
674
675pub fn ability_after_damage_hit(
676 state: &mut State,
677 choice: &mut Choice,
678 side_ref: &SideReference,
679 damage_dealt: i16,
680 instructions: &mut StateInstructions,
681) {
682 let (attacking_side, defending_side) = state.get_both_sides(side_ref);
683 let active_pkmn = attacking_side.get_active();
684 if defending_side.get_active_immutable().ability == Abilities::NEUTRALIZINGGAS
685 || active_pkmn.ability == Abilities::NEUTRALIZINGGAS
686 {
687 return;
688 }
689 match active_pkmn.ability {
690 Abilities::BATTLEBOND => {
691 if damage_dealt > 0 && defending_side.get_active_immutable().hp == 0 {
692 let mut instructions_vec = Vec::with_capacity(3);
693 if let Some(boost_instruction) = get_boost_instruction(
694 &attacking_side,
695 &PokemonBoostableStat::Attack,
696 &1,
697 side_ref,
698 side_ref,
699 ) {
700 instructions_vec.push(boost_instruction);
701 }
702 if let Some(boost_instruction) = get_boost_instruction(
703 &attacking_side,
704 &PokemonBoostableStat::SpecialAttack,
705 &1,
706 side_ref,
707 side_ref,
708 ) {
709 instructions_vec.push(boost_instruction);
710 }
711 if let Some(boost_instruction) = get_boost_instruction(
712 &attacking_side,
713 &PokemonBoostableStat::Speed,
714 &1,
715 side_ref,
716 side_ref,
717 ) {
718 instructions_vec.push(boost_instruction);
719 }
720 for i in instructions_vec {
721 state.apply_one_instruction(&i);
722 instructions.instruction_list.push(i);
723 }
724 }
725 }
726 Abilities::MAGICIAN | Abilities::PICKPOCKET => {
727 let defending_pkmn = defending_side.get_active();
728 if damage_dealt > 0
729 && defending_pkmn.item_can_be_removed()
730 && active_pkmn.item == Items::NONE
731 {
732 instructions.instruction_list.push(Instruction::ChangeItem(
733 ChangeItemInstruction {
734 side_ref: *side_ref,
735 current_item: active_pkmn.item,
736 new_item: defending_pkmn.item,
737 },
738 ));
739 active_pkmn.item = defending_pkmn.item;
740 instructions.instruction_list.push(Instruction::ChangeItem(
741 ChangeItemInstruction {
742 side_ref: side_ref.get_other_side(),
743 current_item: defending_pkmn.item,
744 new_item: Items::NONE,
745 },
746 ));
747 defending_pkmn.item = Items::NONE;
748 }
749 }
750 Abilities::MOXIE | Abilities::CHILLINGNEIGH | Abilities::ASONEGLASTRIER => {
751 if damage_dealt > 0 && defending_side.get_active_immutable().hp == 0 {
752 if let Some(boost_instruction) = get_boost_instruction(
753 &attacking_side,
754 &PokemonBoostableStat::Attack,
755 &1,
756 side_ref,
757 side_ref,
758 ) {
759 state.apply_one_instruction(&boost_instruction);
760 instructions.instruction_list.push(boost_instruction);
761 }
762 }
763 }
764 Abilities::GRIMNEIGH | Abilities::ASONESPECTRIER => {
765 if damage_dealt > 0 && defending_side.get_active_immutable().hp == 0 {
766 if let Some(boost_instruction) = get_boost_instruction(
767 &attacking_side,
768 &PokemonBoostableStat::SpecialAttack,
769 &1,
770 side_ref,
771 side_ref,
772 ) {
773 state.apply_one_instruction(&boost_instruction);
774 instructions.instruction_list.push(boost_instruction);
775 }
776 }
777 }
778 Abilities::BEASTBOOST => {
779 if damage_dealt > 0 && defending_side.get_active_immutable().hp == 0 {
780 let highest_stat = &attacking_side.calculate_highest_stat();
781 if let Some(boost_instruction) =
782 get_boost_instruction(&attacking_side, highest_stat, &1, side_ref, side_ref)
783 {
784 state.apply_one_instruction(&boost_instruction);
785 instructions.instruction_list.push(boost_instruction);
786 }
787 }
788 }
789 _ => {}
790 }
791 let (attacking_side, defending_side) = state.get_both_sides(side_ref);
792 let attacking_pkmn = attacking_side.get_active();
793 let defending_pkmn = defending_side.get_active();
794 match defending_pkmn.ability {
795 Abilities::MUMMY | Abilities::LINGERINGAROMA | Abilities::WANDERINGSPIRIT => {
796 if choice.flags.contact {
797 instructions
798 .instruction_list
799 .push(Instruction::ChangeAbility(ChangeAbilityInstruction {
800 side_ref: *side_ref,
801 new_ability: Abilities::MUMMY,
802 old_ability: attacking_pkmn.ability.clone(),
803 }));
804 attacking_pkmn.ability = Abilities::MUMMY;
805 }
806 }
807 Abilities::GULPMISSILE => {
808 if damage_dealt > 0
809 && [PokemonName::CRAMORANTGORGING, PokemonName::CRAMORANTGULPING]
810 .contains(&defending_pkmn.id)
811 {
812 instructions.instruction_list.push(Instruction::FormeChange(
813 FormeChangeInstruction {
814 side_ref: side_ref.get_other_side(),
815 previous_forme: defending_pkmn.id,
816 new_forme: PokemonName::CRAMORANT,
817 },
818 ));
819
820 let damage_dealt = cmp::min(attacking_pkmn.maxhp / 4, attacking_pkmn.hp);
821 instructions
822 .instruction_list
823 .push(Instruction::Damage(DamageInstruction {
824 side_ref: *side_ref,
825 damage_amount: damage_dealt,
826 }));
827 attacking_pkmn.hp -= damage_dealt;
828
829 if defending_pkmn.id == PokemonName::CRAMORANTGULPING {
830 if let Some(boost_instruction) = get_boost_instruction(
831 &attacking_side,
832 &PokemonBoostableStat::Defense,
833 &-1,
834 &side_ref.get_other_side(),
835 side_ref,
836 ) {
837 state.apply_one_instruction(&boost_instruction);
838 instructions.instruction_list.push(boost_instruction);
839 }
840 } else if defending_pkmn.id == PokemonName::CRAMORANTGORGING {
841 choice.add_or_create_secondaries(Secondary {
842 chance: 100.0,
843 target: MoveTarget::User,
844 effect: Effect::Status(PokemonStatus::PARALYZE),
845 })
846 }
847 }
848 }
849 Abilities::COLORCHANGE => {
850 if damage_dealt > 0
851 && defending_pkmn.hp != 0
852 && !defending_pkmn.has_type(&choice.move_type)
853 {
854 let change_type_instruction = Instruction::ChangeType(ChangeType {
855 side_ref: side_ref.get_other_side(),
856 new_types: (choice.move_type, PokemonType::TYPELESS),
857 old_types: defending_pkmn.types,
858 });
859 defending_pkmn.types = (choice.move_type, PokemonType::TYPELESS);
860 instructions.instruction_list.push(change_type_instruction);
861 }
862 }
863 Abilities::STAMINA => {
864 if damage_dealt > 0 && defending_pkmn.hp != 0 {
865 if let Some(boost_instruction) = get_boost_instruction(
866 &defending_side,
867 &PokemonBoostableStat::Defense,
868 &1,
869 side_ref,
870 &side_ref.get_other_side(),
871 ) {
872 state.apply_one_instruction(&boost_instruction);
873 instructions.instruction_list.push(boost_instruction);
874 }
875 }
876 }
877 Abilities::COTTONDOWN => {
878 if damage_dealt > 0 {
879 if let Some(boost_instruction) = get_boost_instruction(
880 &attacking_side,
881 &PokemonBoostableStat::Speed,
882 &-1,
883 &side_ref.get_other_side(),
884 side_ref,
885 ) {
886 state.apply_one_instruction(&boost_instruction);
887 instructions.instruction_list.push(boost_instruction);
888 }
889 }
890 }
891 Abilities::SANDSPIT => {
892 if damage_dealt > 0 && state.weather.weather_type != Weather::SAND {
893 instructions
894 .instruction_list
895 .push(Instruction::ChangeWeather(ChangeWeather {
896 new_weather: Weather::SAND,
897 new_weather_turns_remaining: WEATHER_ABILITY_TURNS,
898 previous_weather: state.weather.weather_type,
899 previous_weather_turns_remaining: state.weather.turns_remaining,
900 }));
901 state.weather.weather_type = Weather::SAND;
902 state.weather.turns_remaining = WEATHER_ABILITY_TURNS;
903 }
904 }
905 Abilities::SEEDSOWER => {
906 if damage_dealt > 0 && state.terrain.terrain_type != Terrain::GRASSYTERRAIN {
907 instructions
908 .instruction_list
909 .push(Instruction::ChangeTerrain(ChangeTerrain {
910 new_terrain: Terrain::GRASSYTERRAIN,
911 new_terrain_turns_remaining: 5,
912 previous_terrain: state.terrain.terrain_type,
913 previous_terrain_turns_remaining: state.terrain.turns_remaining,
914 }));
915 state.terrain.terrain_type = Terrain::GRASSYTERRAIN;
916 state.terrain.turns_remaining = 5;
917 }
918 }
919 Abilities::TOXICDEBRIS => {
920 if damage_dealt > 0
922 && choice.category == MoveCategory::Physical
923 && attacking_side.side_conditions.toxic_spikes < 2
924 {
925 instructions
926 .instruction_list
927 .push(Instruction::ChangeSideCondition(
928 ChangeSideConditionInstruction {
929 side_ref: *side_ref,
930 side_condition: PokemonSideCondition::ToxicSpikes,
931 amount: 1,
932 },
933 ));
934 attacking_side.side_conditions.toxic_spikes += 1;
935 }
936 }
937 Abilities::BERSERK => {
938 if damage_dealt > 0
939 && defending_pkmn.hp < defending_pkmn.maxhp / 2
940 && defending_pkmn.hp + damage_dealt >= defending_pkmn.maxhp / 2
941 {
942 if let Some(boost_instruction) = get_boost_instruction(
943 &defending_side,
944 &PokemonBoostableStat::SpecialAttack,
945 &1,
946 &side_ref.get_other_side(),
947 &side_ref.get_other_side(),
948 ) {
949 state.apply_one_instruction(&boost_instruction);
950 instructions.instruction_list.push(boost_instruction);
951 }
952 }
953 }
954 Abilities::ROUGHSKIN | Abilities::IRONBARBS => {
955 if damage_dealt > 0 && choice.flags.contact {
956 #[cfg(feature = "gen3")]
957 let damage_dealt = cmp::min(attacking_pkmn.maxhp / 16, attacking_pkmn.hp);
958
959 #[cfg(not(feature = "gen3"))]
960 let damage_dealt = cmp::min(attacking_pkmn.maxhp / 8, attacking_pkmn.hp);
961
962 instructions
963 .instruction_list
964 .push(Instruction::Damage(DamageInstruction {
965 side_ref: *side_ref,
966 damage_amount: damage_dealt,
967 }));
968 attacking_pkmn.hp -= damage_dealt;
969 }
970 }
971 Abilities::AFTERMATH => {
972 if damage_dealt > 0
973 && defending_side.get_active_immutable().hp == 0
974 && choice.flags.contact
975 {
976 let damage_dealt = cmp::min(attacking_pkmn.maxhp / 4, attacking_pkmn.hp);
977 instructions
978 .instruction_list
979 .push(Instruction::Damage(DamageInstruction {
980 side_ref: *side_ref,
981 damage_amount: damage_dealt,
982 }));
983 attacking_pkmn.hp -= damage_dealt;
984 }
985 }
986 Abilities::INNARDSOUT => {
987 if damage_dealt > 0 && defending_side.get_active_immutable().hp == 0 {
988 let damage_dealt = cmp::min(damage_dealt, attacking_pkmn.hp);
989 instructions
990 .instruction_list
991 .push(Instruction::Damage(DamageInstruction {
992 side_ref: *side_ref,
993 damage_amount: damage_dealt,
994 }));
995 attacking_pkmn.hp -= damage_dealt;
996 }
997 }
998 Abilities::PERISHBODY => {
999 if damage_dealt > 0 && choice.flags.contact {
1000 for side_ref in [SideReference::SideOne, SideReference::SideTwo] {
1001 let side = state.get_side(&side_ref);
1002 let pkmn = side.get_active();
1003 if pkmn.hp != 0
1004 && pkmn.ability != Abilities::SOUNDPROOF
1005 && !(side
1006 .volatile_statuses
1007 .contains(&PokemonVolatileStatus::PERISH4)
1008 || side
1009 .volatile_statuses
1010 .contains(&PokemonVolatileStatus::PERISH3)
1011 || side
1012 .volatile_statuses
1013 .contains(&PokemonVolatileStatus::PERISH2)
1014 || side
1015 .volatile_statuses
1016 .contains(&PokemonVolatileStatus::PERISH1))
1017 {
1018 instructions
1019 .instruction_list
1020 .push(Instruction::ApplyVolatileStatus(
1021 ApplyVolatileStatusInstruction {
1022 side_ref: side_ref,
1023 volatile_status: PokemonVolatileStatus::PERISH4,
1024 },
1025 ));
1026 side.volatile_statuses
1027 .insert(PokemonVolatileStatus::PERISH4);
1028 }
1029 }
1030 }
1031 }
1032 _ => {}
1033 }
1034}
1035
1036pub fn ability_on_switch_out(
1037 state: &mut State,
1038 side_ref: &SideReference,
1039 instructions: &mut StateInstructions,
1040) {
1041 let (attacking_side, defending_side) = state.get_both_sides(side_ref);
1042 let active_pkmn = attacking_side.get_active();
1043 if defending_side.get_active_immutable().ability == Abilities::NEUTRALIZINGGAS {
1044 return;
1045 }
1046 match active_pkmn.ability {
1047 Abilities::GULPMISSILE => {
1048 if active_pkmn.id != PokemonName::CRAMORANT {
1049 instructions.instruction_list.push(Instruction::FormeChange(
1050 FormeChangeInstruction {
1051 side_ref: *side_ref,
1052 new_forme: PokemonName::CRAMORANT,
1053 previous_forme: active_pkmn.id,
1054 },
1055 ));
1056 active_pkmn.id = PokemonName::CRAMORANT;
1057 }
1058 }
1059 Abilities::ZEROTOHERO => {
1060 if active_pkmn.id == PokemonName::PALAFIN {
1061 instructions.instruction_list.push(Instruction::FormeChange(
1062 FormeChangeInstruction {
1063 side_ref: *side_ref,
1064 new_forme: PokemonName::PALAFINHERO,
1065 previous_forme: PokemonName::PALAFIN,
1066 },
1067 ));
1068 active_pkmn.id = PokemonName::PALAFINHERO;
1069 active_pkmn.recalculate_stats(side_ref, instructions);
1070 }
1071 }
1072 Abilities::HUNGERSWITCH => {
1073 if active_pkmn.id == PokemonName::MORPEKOHANGRY && !active_pkmn.terastallized {
1074 instructions.instruction_list.push(Instruction::FormeChange(
1075 FormeChangeInstruction {
1076 side_ref: *side_ref,
1077 new_forme: PokemonName::MORPEKO,
1078 previous_forme: PokemonName::MORPEKOHANGRY,
1079 },
1080 ));
1081 active_pkmn.id = PokemonName::MORPEKO;
1082 }
1083 }
1084 Abilities::NATURALCURE => {
1085 if active_pkmn.status != PokemonStatus::NONE {
1086 let status = active_pkmn.status.clone();
1087 active_pkmn.status = PokemonStatus::NONE;
1088 instructions
1089 .instruction_list
1090 .push(Instruction::ChangeStatus(ChangeStatusInstruction {
1091 side_ref: *side_ref,
1092 pokemon_index: attacking_side.active_index,
1093 old_status: status,
1094 new_status: PokemonStatus::NONE,
1095 }));
1096 }
1097 }
1098 Abilities::REGENERATOR => {
1099 let hp_recovered = cmp::min(active_pkmn.maxhp / 3, active_pkmn.maxhp - active_pkmn.hp);
1100
1101 if hp_recovered > 0 && active_pkmn.hp > 0 {
1102 instructions
1103 .instruction_list
1104 .push(Instruction::Heal(HealInstruction {
1105 side_ref: *side_ref,
1106 heal_amount: hp_recovered,
1107 }));
1108 active_pkmn.hp += hp_recovered;
1109 }
1110 }
1111 Abilities::PRIMORDIALSEA => {
1112 if state.weather.weather_type == Weather::HEAVYRAIN {
1113 instructions
1114 .instruction_list
1115 .push(Instruction::ChangeWeather(ChangeWeather {
1116 new_weather: Weather::NONE,
1117 new_weather_turns_remaining: -1,
1118 previous_weather: state.weather.weather_type,
1119 previous_weather_turns_remaining: state.weather.turns_remaining,
1120 }));
1121 state.weather.weather_type = Weather::NONE;
1122 state.weather.turns_remaining = -1;
1123 }
1124 }
1125 Abilities::DESOLATELAND => {
1126 if state.weather.weather_type == Weather::HARSHSUN {
1127 instructions
1128 .instruction_list
1129 .push(Instruction::ChangeWeather(ChangeWeather {
1130 new_weather: Weather::NONE,
1131 new_weather_turns_remaining: -1,
1132 previous_weather: state.weather.weather_type,
1133 previous_weather_turns_remaining: state.weather.turns_remaining,
1134 }));
1135 state.weather.weather_type = Weather::NONE;
1136 state.weather.turns_remaining = -1;
1137 }
1138 }
1139 _ => {}
1140 }
1141
1142 let active_pkmn = state.get_side(side_ref).get_active();
1144 if active_pkmn.ability != active_pkmn.base_ability {
1145 instructions
1146 .instruction_list
1147 .push(Instruction::ChangeAbility(ChangeAbilityInstruction {
1148 side_ref: *side_ref,
1149 new_ability: active_pkmn.base_ability.clone(),
1150 old_ability: active_pkmn.ability.clone(),
1151 }));
1152 active_pkmn.ability = active_pkmn.base_ability.clone();
1153 }
1154}
1155
1156pub fn ability_end_of_turn(
1157 state: &mut State,
1158 side_ref: &SideReference,
1159 instructions: &mut StateInstructions,
1160) {
1161 let (attacking_side, defending_side) = state.get_both_sides(side_ref);
1162 let active_pkmn = attacking_side.get_active();
1163 if defending_side.get_active_immutable().ability == Abilities::NEUTRALIZINGGAS {
1164 return;
1165 }
1166 match active_pkmn.ability {
1167 Abilities::HUNGERSWITCH => {
1168 if active_pkmn.id == PokemonName::MORPEKO && !active_pkmn.terastallized {
1169 instructions.instruction_list.push(Instruction::FormeChange(
1170 FormeChangeInstruction {
1171 side_ref: *side_ref,
1172 new_forme: PokemonName::MORPEKOHANGRY,
1173 previous_forme: PokemonName::MORPEKO,
1174 },
1175 ));
1176 active_pkmn.id = PokemonName::MORPEKOHANGRY;
1177 } else if active_pkmn.id == PokemonName::MORPEKOHANGRY && !active_pkmn.terastallized {
1178 instructions.instruction_list.push(Instruction::FormeChange(
1179 FormeChangeInstruction {
1180 side_ref: *side_ref,
1181 new_forme: PokemonName::MORPEKO,
1182 previous_forme: PokemonName::MORPEKOHANGRY,
1183 },
1184 ));
1185 active_pkmn.id = PokemonName::MORPEKO;
1186 }
1187 }
1188 Abilities::SHIELDSDOWN => {
1189 if active_pkmn.hp <= active_pkmn.maxhp / 2
1190 && active_pkmn.id == PokemonName::MINIORMETEOR
1191 {
1192 instructions.instruction_list.push(Instruction::FormeChange(
1193 FormeChangeInstruction {
1194 side_ref: *side_ref,
1195 new_forme: PokemonName::MINIOR,
1196 previous_forme: active_pkmn.id,
1197 },
1198 ));
1199 active_pkmn.id = PokemonName::MINIOR;
1200 active_pkmn.recalculate_stats(side_ref, instructions);
1201 }
1202 if active_pkmn.hp > active_pkmn.maxhp / 2 && active_pkmn.id != PokemonName::MINIORMETEOR
1203 {
1204 instructions.instruction_list.push(Instruction::FormeChange(
1205 FormeChangeInstruction {
1206 side_ref: *side_ref,
1207 new_forme: PokemonName::MINIORMETEOR,
1208 previous_forme: active_pkmn.id,
1209 },
1210 ));
1211 active_pkmn.id = PokemonName::MINIORMETEOR;
1212 active_pkmn.recalculate_stats(side_ref, instructions);
1213 }
1214 }
1215 Abilities::SCHOOLING => {
1216 if active_pkmn.hp <= active_pkmn.maxhp / 4
1217 && active_pkmn.id == PokemonName::WISHIWASHISCHOOL
1218 {
1219 instructions.instruction_list.push(Instruction::FormeChange(
1220 FormeChangeInstruction {
1221 side_ref: *side_ref,
1222 new_forme: PokemonName::WISHIWASHI,
1223 previous_forme: active_pkmn.id,
1224 },
1225 ));
1226 active_pkmn.id = PokemonName::WISHIWASHI;
1227 active_pkmn.recalculate_stats(side_ref, instructions);
1228 }
1229 if active_pkmn.hp > active_pkmn.maxhp / 4 && active_pkmn.id == PokemonName::WISHIWASHI {
1230 instructions.instruction_list.push(Instruction::FormeChange(
1231 FormeChangeInstruction {
1232 side_ref: *side_ref,
1233 new_forme: PokemonName::WISHIWASHISCHOOL,
1234 previous_forme: active_pkmn.id,
1235 },
1236 ));
1237 active_pkmn.id = PokemonName::WISHIWASHISCHOOL;
1238 active_pkmn.recalculate_stats(side_ref, instructions);
1239 }
1240 }
1241 Abilities::BADDREAMS => {
1242 let defender = defending_side.get_active();
1243 if defender.status == PokemonStatus::SLEEP {
1244 let damage_dealt = cmp::min(defender.maxhp / 8, defender.hp);
1245 instructions
1246 .instruction_list
1247 .push(Instruction::Damage(DamageInstruction {
1248 side_ref: side_ref.get_other_side(),
1249 damage_amount: damage_dealt,
1250 }));
1251 defender.hp -= damage_dealt;
1252 }
1253 }
1254 Abilities::SOLARPOWER => {
1255 if state.weather_is_active(&Weather::HARSHSUN) || state.weather_is_active(&Weather::SUN)
1256 {
1257 let active_pkmn = state.get_side(side_ref).get_active();
1258 let damage_dealt =
1259 cmp::min(active_pkmn.maxhp / 8, active_pkmn.maxhp - active_pkmn.hp);
1260 if damage_dealt > 0 {
1261 instructions
1262 .instruction_list
1263 .push(Instruction::Damage(DamageInstruction {
1264 side_ref: *side_ref,
1265 damage_amount: damage_dealt,
1266 }));
1267 active_pkmn.hp -= damage_dealt;
1268 }
1269 }
1270 }
1271 Abilities::ICEBODY => {
1272 if state.weather_is_active(&Weather::HAIL) {
1273 let active_pkmn = state.get_side(side_ref).get_active();
1274 let health_recovered =
1275 cmp::min(active_pkmn.maxhp / 16, active_pkmn.maxhp - active_pkmn.hp);
1276 if health_recovered > 0 {
1277 instructions
1278 .instruction_list
1279 .push(Instruction::Heal(HealInstruction {
1280 side_ref: *side_ref,
1281 heal_amount: health_recovered,
1282 }));
1283 active_pkmn.hp += health_recovered;
1284 }
1285 }
1286 }
1287 Abilities::POISONHEAL => {
1288 if active_pkmn.hp < active_pkmn.maxhp
1289 && (active_pkmn.status == PokemonStatus::POISON
1290 || active_pkmn.status == PokemonStatus::TOXIC)
1291 {
1292 let heal_amount =
1293 cmp::min(active_pkmn.maxhp / 8, active_pkmn.maxhp - active_pkmn.hp);
1294 let ins = Instruction::Heal(HealInstruction {
1295 side_ref: side_ref.clone(),
1296 heal_amount: heal_amount,
1297 });
1298 active_pkmn.hp += heal_amount;
1299 instructions.instruction_list.push(ins);
1300 }
1301 }
1302 Abilities::SPEEDBOOST => {
1303 if attacking_side.speed_boost < 6 {
1304 let ins = Instruction::Boost(BoostInstruction {
1305 side_ref: side_ref.clone(),
1306 stat: PokemonBoostableStat::Speed,
1307 amount: 1,
1308 });
1309 attacking_side.speed_boost += 1;
1310 instructions.instruction_list.push(ins);
1311 }
1312 }
1313 Abilities::RAINDISH => {
1314 if state.weather_is_active(&Weather::RAIN)
1315 || state.weather_is_active(&Weather::HEAVYRAIN)
1316 {
1317 let active_pkmn = state.get_side(side_ref).get_active();
1318 let health_recovered =
1319 cmp::min(active_pkmn.maxhp / 16, active_pkmn.maxhp - active_pkmn.hp);
1320 if health_recovered > 0 {
1321 instructions
1322 .instruction_list
1323 .push(Instruction::Heal(HealInstruction {
1324 side_ref: *side_ref,
1325 heal_amount: health_recovered,
1326 }));
1327 active_pkmn.hp += health_recovered;
1328 }
1329 }
1330 }
1331 Abilities::DRYSKIN => {
1332 if state.weather_is_active(&Weather::RAIN) {
1333 let active_pkmn = state.get_side(side_ref).get_active();
1334 if active_pkmn.hp < active_pkmn.maxhp {
1335 let heal_amount =
1336 cmp::min(active_pkmn.maxhp / 8, active_pkmn.maxhp - active_pkmn.hp);
1337 let ins = Instruction::Heal(HealInstruction {
1338 side_ref: side_ref.clone(),
1339 heal_amount: heal_amount,
1340 });
1341 active_pkmn.hp += heal_amount;
1342 instructions.instruction_list.push(ins);
1343 }
1344 }
1345 }
1346 Abilities::HYDRATION => {
1347 if active_pkmn.status != PokemonStatus::NONE
1348 && (state.weather_is_active(&Weather::RAIN)
1349 || state.weather_is_active(&Weather::HEAVYRAIN))
1350 {
1351 let attacking_side = state.get_side(side_ref);
1352 let active_index = attacking_side.active_index;
1353 let active_pkmn = attacking_side.get_active();
1354
1355 add_remove_status_instructions(
1356 instructions,
1357 active_index,
1358 *side_ref,
1359 attacking_side,
1360 );
1361 }
1362 }
1363 Abilities::SHEDSKIN => {
1366 if active_pkmn.status != PokemonStatus::NONE {
1367 let attacking_side = state.get_side(side_ref);
1368 let active_index = attacking_side.active_index;
1369 let active_pkmn = attacking_side.get_active();
1370
1371 add_remove_status_instructions(
1372 instructions,
1373 active_index,
1374 *side_ref,
1375 attacking_side,
1376 );
1377 }
1378 }
1379 _ => {}
1380 }
1381}
1382
1383pub fn ability_on_switch_in(
1384 state: &mut State,
1385 side_ref: &SideReference,
1386 instructions: &mut StateInstructions,
1387) {
1388 let (attacking_side, defending_side) = state.get_both_sides(side_ref);
1389 let active_pkmn = attacking_side.get_active();
1390 let defending_pkmn = defending_side.get_active_immutable();
1391 if defending_pkmn.ability == Abilities::NEUTRALIZINGGAS {
1392 return;
1393 }
1394
1395 if active_pkmn.ability == Abilities::TRACE && active_pkmn.ability != defending_pkmn.ability {
1398 instructions
1399 .instruction_list
1400 .push(Instruction::ChangeAbility(ChangeAbilityInstruction {
1401 side_ref: *side_ref,
1402 new_ability: defending_pkmn.ability.clone(),
1403 old_ability: active_pkmn.ability.clone(),
1404 }));
1405 active_pkmn.ability = defending_pkmn.ability.clone();
1406 }
1407
1408 match active_pkmn.ability {
1409 Abilities::ICEFACE => {
1410 if active_pkmn.id == PokemonName::EISCUENOICE && state.weather_is_active(&Weather::HAIL)
1411 || state.weather_is_active(&Weather::SNOW)
1412 {
1413 let active_pkmn = state.get_side(side_ref).get_active();
1414 instructions.instruction_list.push(Instruction::FormeChange(
1415 FormeChangeInstruction {
1416 side_ref: *side_ref,
1417 previous_forme: active_pkmn.id,
1418 new_forme: PokemonName::EISCUE,
1419 },
1420 ));
1421 active_pkmn.id = PokemonName::EISCUE;
1422 active_pkmn.recalculate_stats(side_ref, instructions);
1423 }
1424 }
1425 Abilities::PROTOSYNTHESIS => {
1426 let sun_is_active = state.weather_is_active(&Weather::SUN);
1427 let attacking_side = state.get_side(side_ref);
1428 let volatile = protosynthesis_volatile_from_side(&attacking_side);
1429 protosynthesus_or_quarkdrive_on_switch_in(
1430 sun_is_active,
1431 volatile,
1432 instructions,
1433 attacking_side,
1434 side_ref,
1435 );
1436 }
1437 Abilities::QUARKDRIVE => {
1438 let electric_terrain_is_active = state.terrain_is_active(&Terrain::ELECTRICTERRAIN);
1439 let attacking_side = state.get_side(side_ref);
1440 let volatile = quarkdrive_volatile_from_side(&attacking_side);
1441 protosynthesus_or_quarkdrive_on_switch_in(
1442 electric_terrain_is_active,
1443 volatile,
1444 instructions,
1445 attacking_side,
1446 side_ref,
1447 );
1448 }
1449 Abilities::EMBODYASPECTTEAL => {
1450 if let Some(boost_instruction) = get_boost_instruction(
1451 &attacking_side,
1452 &PokemonBoostableStat::Speed,
1453 &1,
1454 side_ref,
1455 side_ref,
1456 ) {
1457 state.apply_one_instruction(&boost_instruction);
1458 instructions.instruction_list.push(boost_instruction);
1459 }
1460 }
1461 Abilities::EMBODYASPECTWELLSPRING => {
1462 if let Some(boost_instruction) = get_boost_instruction(
1463 &attacking_side,
1464 &PokemonBoostableStat::SpecialDefense,
1465 &1,
1466 side_ref,
1467 side_ref,
1468 ) {
1469 state.apply_one_instruction(&boost_instruction);
1470 instructions.instruction_list.push(boost_instruction);
1471 }
1472 }
1473 Abilities::EMBODYASPECTCORNERSTONE => {
1474 if let Some(boost_instruction) = get_boost_instruction(
1475 &attacking_side,
1476 &PokemonBoostableStat::Defense,
1477 &1,
1478 side_ref,
1479 side_ref,
1480 ) {
1481 state.apply_one_instruction(&boost_instruction);
1482 instructions.instruction_list.push(boost_instruction);
1483 }
1484 }
1485 Abilities::EMBODYASPECTHEARTHFLAME => {
1486 if let Some(boost_instruction) = get_boost_instruction(
1487 &attacking_side,
1488 &PokemonBoostableStat::Attack,
1489 &1,
1490 side_ref,
1491 side_ref,
1492 ) {
1493 state.apply_one_instruction(&boost_instruction);
1494 instructions.instruction_list.push(boost_instruction);
1495 }
1496 }
1497 Abilities::INTREPIDSWORD => {
1498 attacking_side.attack_boost += 1;
1500 instructions
1501 .instruction_list
1502 .push(Instruction::Boost(BoostInstruction {
1503 side_ref: *side_ref,
1504 stat: PokemonBoostableStat::Attack,
1505 amount: 1,
1506 }));
1507 }
1508 Abilities::SLOWSTART => {
1509 attacking_side
1510 .volatile_statuses
1511 .insert(PokemonVolatileStatus::SLOWSTART);
1512 instructions
1513 .instruction_list
1514 .push(Instruction::ApplyVolatileStatus(
1515 ApplyVolatileStatusInstruction {
1516 side_ref: *side_ref,
1517 volatile_status: PokemonVolatileStatus::SLOWSTART,
1518 },
1519 ));
1520 }
1521 Abilities::DROUGHT | Abilities::ORICHALCUMPULSE => {
1522 if state.weather.weather_type != Weather::SUN {
1523 instructions
1524 .instruction_list
1525 .push(Instruction::ChangeWeather(ChangeWeather {
1526 new_weather: Weather::SUN,
1527 new_weather_turns_remaining: WEATHER_ABILITY_TURNS,
1528 previous_weather: state.weather.weather_type,
1529 previous_weather_turns_remaining: state.weather.turns_remaining,
1530 }));
1531 state.weather.weather_type = Weather::SUN;
1532 state.weather.turns_remaining = WEATHER_ABILITY_TURNS;
1533 }
1534 }
1535 Abilities::DESOLATELAND => {
1536 if state.weather.weather_type != Weather::HARSHSUN {
1537 instructions
1538 .instruction_list
1539 .push(Instruction::ChangeWeather(ChangeWeather {
1540 new_weather: Weather::HARSHSUN,
1541 new_weather_turns_remaining: -1,
1542 previous_weather: state.weather.weather_type,
1543 previous_weather_turns_remaining: state.weather.turns_remaining,
1544 }));
1545 state.weather.weather_type = Weather::HARSHSUN;
1546 state.weather.turns_remaining = -1;
1547 }
1548 }
1549 Abilities::MISTYSURGE => {
1550 if state.terrain.terrain_type != Terrain::MISTYTERRAIN {
1551 instructions
1552 .instruction_list
1553 .push(Instruction::ChangeTerrain(ChangeTerrain {
1554 new_terrain: Terrain::MISTYTERRAIN,
1555 new_terrain_turns_remaining: 5,
1556 previous_terrain: state.terrain.terrain_type,
1557 previous_terrain_turns_remaining: state.terrain.turns_remaining,
1558 }));
1559 state.terrain.terrain_type = Terrain::MISTYTERRAIN;
1560 state.terrain.turns_remaining = 5;
1561 }
1562 }
1563 Abilities::SANDSTREAM => {
1564 if state.weather.weather_type != Weather::SAND {
1565 instructions
1566 .instruction_list
1567 .push(Instruction::ChangeWeather(ChangeWeather {
1568 new_weather: Weather::SAND,
1569 new_weather_turns_remaining: WEATHER_ABILITY_TURNS,
1570 previous_weather: state.weather.weather_type,
1571 previous_weather_turns_remaining: state.weather.turns_remaining,
1572 }));
1573 state.weather.weather_type = Weather::SAND;
1574 state.weather.turns_remaining = WEATHER_ABILITY_TURNS;
1575 }
1576 }
1577 Abilities::INTIMIDATE => {
1578 if let Some(boost_instruction) = get_boost_instruction(
1579 &defending_side,
1580 &PokemonBoostableStat::Attack,
1581 &-1,
1582 side_ref,
1583 &side_ref.get_other_side(),
1584 ) {
1585 let defender = defending_side.get_active_immutable();
1586 let mut adrenaline_orb_item_instruction = None;
1587 let mut adrenaline_orb_boost_instruction = None;
1588 if defender.item == Items::ADRENALINEORB {
1589 if let Some(boost_ins) = get_boost_instruction(
1590 &defending_side,
1591 &PokemonBoostableStat::Speed,
1592 &1,
1593 &side_ref.get_other_side(),
1594 &side_ref.get_other_side(),
1595 ) {
1596 adrenaline_orb_boost_instruction = Some(boost_ins);
1597 adrenaline_orb_item_instruction =
1598 Some(Instruction::ChangeItem(ChangeItemInstruction {
1599 side_ref: side_ref.get_other_side(),
1600 current_item: Items::ADRENALINEORB,
1601 new_item: Items::NONE,
1602 }));
1603 }
1604 }
1605 match defender.ability {
1606 Abilities::OWNTEMPO
1607 | Abilities::OBLIVIOUS
1608 | Abilities::INNERFOCUS
1609 | Abilities::SCRAPPY => {}
1610 _ => {
1611 state.apply_one_instruction(&boost_instruction);
1612 instructions.instruction_list.push(boost_instruction);
1613 }
1614 }
1615 if let Some(ins) = adrenaline_orb_boost_instruction {
1616 state.apply_one_instruction(&ins);
1617 instructions.instruction_list.push(ins);
1618 if let Some(ins) = adrenaline_orb_item_instruction {
1619 state.apply_one_instruction(&ins);
1620 instructions.instruction_list.push(ins);
1621 }
1622 }
1623 }
1624 }
1625 Abilities::DAUNTLESSSHIELD => {
1626 attacking_side.defense_boost += 1;
1628 instructions
1629 .instruction_list
1630 .push(Instruction::Boost(BoostInstruction {
1631 side_ref: *side_ref,
1632 stat: PokemonBoostableStat::Defense,
1633 amount: 1,
1634 }));
1635 }
1636 Abilities::GRASSYSURGE => {
1637 if state.terrain.terrain_type != Terrain::GRASSYTERRAIN {
1638 instructions
1639 .instruction_list
1640 .push(Instruction::ChangeTerrain(ChangeTerrain {
1641 new_terrain: Terrain::GRASSYTERRAIN,
1642 new_terrain_turns_remaining: 5,
1643 previous_terrain: state.terrain.terrain_type,
1644 previous_terrain_turns_remaining: state.terrain.turns_remaining,
1645 }));
1646 state.terrain.terrain_type = Terrain::GRASSYTERRAIN;
1647 state.terrain.turns_remaining = 5;
1648 }
1649 }
1650 Abilities::ELECTRICSURGE | Abilities::HADRONENGINE => {
1651 if state.terrain.terrain_type != Terrain::ELECTRICTERRAIN {
1652 instructions
1653 .instruction_list
1654 .push(Instruction::ChangeTerrain(ChangeTerrain {
1655 new_terrain: Terrain::ELECTRICTERRAIN,
1656 new_terrain_turns_remaining: 5,
1657 previous_terrain: state.terrain.terrain_type,
1658 previous_terrain_turns_remaining: state.terrain.turns_remaining,
1659 }));
1660 state.terrain.terrain_type = Terrain::ELECTRICTERRAIN;
1661 state.terrain.turns_remaining = 5;
1662 }
1663 }
1664 Abilities::DOWNLOAD => {
1665 if defending_side.calculate_boosted_stat(PokemonBoostableStat::Defense)
1666 < defending_side.calculate_boosted_stat(PokemonBoostableStat::SpecialDefense)
1667 {
1668 if let Some(boost_instruction) = get_boost_instruction(
1669 &attacking_side,
1670 &PokemonBoostableStat::Attack,
1671 &1,
1672 side_ref,
1673 side_ref,
1674 ) {
1675 state.apply_one_instruction(&boost_instruction);
1676 instructions.instruction_list.push(boost_instruction);
1677 }
1678 } else {
1679 if let Some(boost_instruction) = get_boost_instruction(
1680 &attacking_side,
1681 &PokemonBoostableStat::SpecialAttack,
1682 &1,
1683 side_ref,
1684 side_ref,
1685 ) {
1686 state.apply_one_instruction(&boost_instruction);
1687 instructions.instruction_list.push(boost_instruction);
1688 }
1689 }
1690 }
1691 Abilities::PRIMORDIALSEA => {
1692 if state.weather.weather_type != Weather::HEAVYRAIN {
1693 instructions
1694 .instruction_list
1695 .push(Instruction::ChangeWeather(ChangeWeather {
1696 new_weather: Weather::HEAVYRAIN,
1697 new_weather_turns_remaining: -1,
1698 previous_weather: state.weather.weather_type,
1699 previous_weather_turns_remaining: state.weather.turns_remaining,
1700 }));
1701 state.weather.weather_type = Weather::HEAVYRAIN;
1702 state.weather.turns_remaining = -1;
1703 }
1704 }
1705 Abilities::SCREENCLEANER => {
1706 if state.side_one.side_conditions.reflect > 0 {
1707 instructions
1708 .instruction_list
1709 .push(Instruction::ChangeSideCondition(
1710 ChangeSideConditionInstruction {
1711 side_ref: SideReference::SideOne,
1712 side_condition: PokemonSideCondition::Reflect,
1713 amount: -1 * state.side_one.side_conditions.reflect,
1714 },
1715 ));
1716 state.side_one.side_conditions.reflect = 0;
1717 }
1718 if state.side_two.side_conditions.reflect > 0 {
1719 instructions
1720 .instruction_list
1721 .push(Instruction::ChangeSideCondition(
1722 ChangeSideConditionInstruction {
1723 side_ref: SideReference::SideTwo,
1724 side_condition: PokemonSideCondition::Reflect,
1725 amount: -1 * state.side_two.side_conditions.reflect,
1726 },
1727 ));
1728 state.side_two.side_conditions.reflect = 0;
1729 }
1730 if state.side_one.side_conditions.light_screen > 0 {
1731 instructions
1732 .instruction_list
1733 .push(Instruction::ChangeSideCondition(
1734 ChangeSideConditionInstruction {
1735 side_ref: SideReference::SideOne,
1736 side_condition: PokemonSideCondition::LightScreen,
1737 amount: -1 * state.side_one.side_conditions.light_screen,
1738 },
1739 ));
1740 state.side_one.side_conditions.light_screen = 0;
1741 }
1742 if state.side_two.side_conditions.light_screen > 0 {
1743 instructions
1744 .instruction_list
1745 .push(Instruction::ChangeSideCondition(
1746 ChangeSideConditionInstruction {
1747 side_ref: SideReference::SideTwo,
1748 side_condition: PokemonSideCondition::LightScreen,
1749 amount: -1 * state.side_two.side_conditions.light_screen,
1750 },
1751 ));
1752 state.side_two.side_conditions.light_screen = 0;
1753 }
1754 if state.side_one.side_conditions.aurora_veil > 0 {
1755 instructions
1756 .instruction_list
1757 .push(Instruction::ChangeSideCondition(
1758 ChangeSideConditionInstruction {
1759 side_ref: SideReference::SideOne,
1760 side_condition: PokemonSideCondition::AuroraVeil,
1761 amount: -1 * state.side_one.side_conditions.aurora_veil,
1762 },
1763 ));
1764 state.side_one.side_conditions.aurora_veil = 0;
1765 }
1766 if state.side_two.side_conditions.aurora_veil > 0 {
1767 instructions
1768 .instruction_list
1769 .push(Instruction::ChangeSideCondition(
1770 ChangeSideConditionInstruction {
1771 side_ref: SideReference::SideTwo,
1772 side_condition: PokemonSideCondition::AuroraVeil,
1773 amount: -1 * state.side_two.side_conditions.aurora_veil,
1774 },
1775 ));
1776 state.side_two.side_conditions.aurora_veil = 0;
1777 }
1778 }
1779 Abilities::SNOWWARNING => {
1780 #[cfg(feature = "gen9")]
1781 let weather_type = Weather::SNOW;
1782 #[cfg(not(feature = "gen9"))]
1783 let weather_type = Weather::HAIL;
1784
1785 if state.weather.weather_type != weather_type {
1786 instructions
1787 .instruction_list
1788 .push(Instruction::ChangeWeather(ChangeWeather {
1789 new_weather: weather_type,
1790 new_weather_turns_remaining: WEATHER_ABILITY_TURNS,
1791 previous_weather: state.weather.weather_type,
1792 previous_weather_turns_remaining: state.weather.turns_remaining,
1793 }));
1794 state.weather.weather_type = weather_type;
1795 state.weather.turns_remaining = WEATHER_ABILITY_TURNS;
1796 }
1797 }
1798 Abilities::PSYCHICSURGE => {
1799 if state.terrain.terrain_type != Terrain::PSYCHICTERRAIN {
1800 instructions
1801 .instruction_list
1802 .push(Instruction::ChangeTerrain(ChangeTerrain {
1803 new_terrain: Terrain::PSYCHICTERRAIN,
1804 new_terrain_turns_remaining: 5,
1805 previous_terrain: state.terrain.terrain_type,
1806 previous_terrain_turns_remaining: state.terrain.turns_remaining,
1807 }));
1808 state.terrain.terrain_type = Terrain::PSYCHICTERRAIN;
1809 state.terrain.turns_remaining = 5;
1810 }
1811 }
1812 Abilities::DRIZZLE => {
1813 if state.weather.weather_type != Weather::RAIN {
1814 instructions
1815 .instruction_list
1816 .push(Instruction::ChangeWeather(ChangeWeather {
1817 new_weather: Weather::RAIN,
1818 new_weather_turns_remaining: WEATHER_ABILITY_TURNS,
1819 previous_weather: state.weather.weather_type,
1820 previous_weather_turns_remaining: state.weather.turns_remaining,
1821 }));
1822 state.weather.weather_type = Weather::RAIN;
1823 state.weather.turns_remaining = WEATHER_ABILITY_TURNS;
1824 }
1825 }
1826 _ => {}
1827 }
1828}
1829
1830pub fn ability_modify_attack_being_used(
1831 state: &State,
1832 attacker_choice: &mut Choice,
1833 defender_choice: &Choice,
1834 attacking_side_ref: &SideReference,
1835) {
1836 let (attacking_side, defending_side) = state.get_both_sides_immutable(attacking_side_ref);
1837 let attacking_pkmn = attacking_side.get_active_immutable();
1838 if defending_side.get_active_immutable().ability == Abilities::NEUTRALIZINGGAS {
1839 return;
1840 }
1841 match attacking_pkmn.ability {
1842 #[cfg(any(feature = "gen9", feature = "gen8", feature = "gen7"))]
1843 Abilities::PRANKSTER => {
1844 if attacker_choice.category == MoveCategory::Status
1845 && defending_side
1846 .get_active_immutable()
1847 .has_type(&PokemonType::DARK)
1848 {
1849 attacker_choice.remove_all_effects();
1850 }
1851 }
1852 Abilities::BEADSOFRUIN => {
1853 if attacker_choice.category == MoveCategory::Special {
1854 attacker_choice.base_power *= 1.33;
1855 }
1856 }
1857 Abilities::SWORDOFRUIN => {
1858 if attacker_choice.category == MoveCategory::Physical {
1859 attacker_choice.base_power *= 1.33;
1860 }
1861 }
1862 Abilities::SHARPNESS => {
1863 if attacker_choice.flags.slicing {
1864 attacker_choice.base_power *= 1.5;
1865 }
1866 }
1867 Abilities::WATERBUBBLE => {
1868 if attacker_choice.move_type == PokemonType::WATER {
1869 attacker_choice.base_power *= 2.0;
1870 }
1871 }
1872 Abilities::DRAGONSMAW => {
1873 if attacker_choice.move_type == PokemonType::DRAGON {
1874 attacker_choice.base_power *= 1.5;
1875 }
1876 }
1877 Abilities::HADRONENGINE => {
1878 if attacker_choice.category == MoveCategory::Special
1879 && state.terrain.terrain_type == Terrain::ELECTRICTERRAIN
1880 {
1881 attacker_choice.base_power *= 1.33;
1882 }
1883 }
1884 Abilities::ORICHALCUMPULSE => {
1885 if attacker_choice.category == MoveCategory::Physical
1886 && state.weather.weather_type == Weather::SUN
1887 {
1888 attacker_choice.base_power *= 1.33;
1889 }
1890 }
1891 Abilities::GALVANIZE => {
1892 if attacker_choice.move_type == PokemonType::NORMAL {
1893 attacker_choice.move_type = PokemonType::ELECTRIC;
1894 attacker_choice.base_power *= 1.2;
1895 }
1896 }
1897 #[cfg(any(feature = "gen9", feature = "gen8", feature = "gen7"))]
1898 Abilities::AERILATE => {
1899 if attacker_choice.move_type == PokemonType::NORMAL {
1900 attacker_choice.move_type = PokemonType::FLYING;
1901 attacker_choice.base_power *= 1.2;
1902 }
1903 }
1904 #[cfg(any(feature = "gen6", feature = "gen5", feature = "gen4"))]
1905 Abilities::AERILATE => {
1906 if attacker_choice.move_type == PokemonType::NORMAL {
1907 attacker_choice.move_type = PokemonType::FLYING;
1908 attacker_choice.base_power *= 1.3;
1909 }
1910 }
1911 Abilities::NEUROFORCE => {
1912 if type_effectiveness_modifier(
1913 &attacker_choice.move_type,
1914 &defending_side.get_active_immutable(),
1915 ) > 1.0
1916 {
1917 attacker_choice.base_power *= 1.25;
1918 }
1919 }
1920 Abilities::STAKEOUT => {
1921 if defender_choice.category == MoveCategory::Switch {
1922 attacker_choice.base_power *= 2.0;
1923 }
1924 }
1925 Abilities::TECHNICIAN => {
1926 if attacker_choice.base_power <= 60.0 {
1927 attacker_choice.base_power *= 1.5;
1928 }
1929 }
1930 #[cfg(any(feature = "gen9", feature = "gen8", feature = "gen7"))]
1931 Abilities::REFRIGERATE => {
1932 if attacker_choice.move_type == PokemonType::NORMAL {
1933 attacker_choice.move_type = PokemonType::ICE;
1934 attacker_choice.base_power *= 1.2;
1935 }
1936 }
1937 #[cfg(any(feature = "gen6", feature = "gen5", feature = "gen4"))]
1938 Abilities::REFRIGERATE => {
1939 if attacker_choice.move_type == PokemonType::NORMAL {
1940 attacker_choice.move_type = PokemonType::ICE;
1941 attacker_choice.base_power *= 1.3;
1942 }
1943 }
1944 Abilities::SUPREMEOVERLORD => {
1945 let mut boost_amount = 1.0;
1946 boost_amount += 0.1 * attacking_side.num_fainted_pkmn() as f32;
1947 attacker_choice.base_power *= boost_amount;
1948 }
1949 Abilities::ADAPTABILITY => {
1950 if attacking_pkmn.has_type(&attacker_choice.move_type) {
1951 if attacking_pkmn.terastallized
1952 && attacker_choice.move_type == attacking_pkmn.tera_type
1953 {
1954 attacker_choice.base_power *= 2.25 / 2.0;
1955 } else {
1956 attacker_choice.base_power *= 2.0 / 1.5;
1957 }
1958 }
1959 }
1960 Abilities::LONGREACH => {
1961 attacker_choice.flags.contact = false;
1962 }
1963 Abilities::PUREPOWER => {
1964 if attacker_choice.category == MoveCategory::Physical {
1965 attacker_choice.base_power *= 2.0;
1966 }
1967 }
1968 Abilities::TINTEDLENS => {
1969 if type_effectiveness_modifier(
1970 &attacker_choice.move_type,
1971 &defending_side.get_active_immutable(),
1972 ) < 1.0
1973 {
1974 attacker_choice.base_power *= 2.0;
1975 }
1976 }
1977 Abilities::FLAREBOOST => {
1978 if attacking_pkmn.status == PokemonStatus::BURN {
1979 attacker_choice.base_power *= 1.5;
1980 }
1981 }
1982 Abilities::LIQUIDVOICE => {
1983 if attacker_choice.flags.sound {
1984 attacker_choice.move_type = PokemonType::WATER;
1985 }
1986 }
1987 Abilities::NOGUARD => attacker_choice.accuracy = 100.0,
1988 Abilities::TORRENT => {
1989 if attacker_choice.move_type == PokemonType::WATER
1990 && attacking_pkmn.hp <= attacking_pkmn.maxhp / 3
1991 {
1992 attacker_choice.base_power *= 1.5;
1993 }
1994 }
1995 Abilities::SERENEGRACE => {
1996 if let Some(secondaries) = &mut attacker_choice.secondaries {
1997 for secondary in secondaries.iter_mut() {
1998 secondary.chance *= 2.0;
1999 }
2000 }
2001 }
2002 Abilities::TOUGHCLAWS => {
2003 if attacker_choice.flags.contact {
2004 attacker_choice.base_power *= 1.3;
2005 }
2006 }
2007 Abilities::RECKLESS => {
2008 if attacker_choice.crash.is_some() || attacker_choice.recoil.is_some() {
2009 attacker_choice.base_power *= 1.2;
2010 }
2011 }
2012 Abilities::HUGEPOWER => {
2013 if attacker_choice.category == MoveCategory::Physical {
2014 attacker_choice.base_power *= 2.0;
2015 }
2016 }
2017 Abilities::SOLARPOWER => {
2018 if state.weather_is_active(&Weather::SUN) {
2019 attacker_choice.base_power *= 1.5;
2020 }
2021 }
2022 Abilities::FAIRYAURA => {
2023 if attacker_choice.move_type == PokemonType::FAIRY
2024 && defending_side.get_active_immutable().ability != Abilities::AURABREAK
2025 {
2026 attacker_choice.base_power *= 1.33;
2027 }
2028 }
2029 Abilities::NORMALIZE => {
2030 attacker_choice.move_type = PokemonType::NORMAL;
2031 }
2032 Abilities::DARKAURA => {
2033 if attacker_choice.move_type == PokemonType::DARK
2034 && defending_side.get_active_immutable().ability != Abilities::AURABREAK
2035 {
2036 attacker_choice.base_power *= 1.33;
2037 }
2038 }
2039 Abilities::VICTORYSTAR => {
2040 attacker_choice.accuracy *= 1.1;
2041 }
2042 Abilities::COMPOUNDEYES => {
2043 attacker_choice.accuracy *= 1.3;
2044 }
2045 Abilities::STEELWORKER | Abilities::STEELYSPIRIT => {
2046 if attacker_choice.move_type == PokemonType::STEEL {
2047 attacker_choice.base_power *= 1.5;
2048 }
2049 }
2050 #[cfg(any(
2051 feature = "gen8",
2052 feature = "gen7",
2053 feature = "gen6",
2054 feature = "gen5",
2055 feature = "gen4"
2056 ))]
2057 Abilities::TRANSISTOR => {
2058 if attacker_choice.move_type == PokemonType::ELECTRIC {
2059 attacker_choice.base_power *= 1.5;
2060 }
2061 }
2062 #[cfg(any(feature = "gen9"))]
2063 Abilities::TRANSISTOR => {
2064 if attacker_choice.move_type == PokemonType::ELECTRIC {
2065 attacker_choice.base_power *= 1.3;
2066 }
2067 }
2068 Abilities::STENCH => {
2069 let mut already_flinches = false;
2070 if let Some(secondaries) = &mut attacker_choice.secondaries {
2071 for secondary in secondaries.iter() {
2072 if secondary.effect == Effect::VolatileStatus(PokemonVolatileStatus::FLINCH) {
2073 already_flinches = true;
2074 }
2075 }
2076 }
2077 if !already_flinches {
2078 attacker_choice.add_or_create_secondaries(Secondary {
2079 chance: 10.0,
2080 target: MoveTarget::Opponent,
2081 effect: Effect::VolatileStatus(PokemonVolatileStatus::FLINCH),
2082 })
2083 }
2084 }
2085 Abilities::SWARM => {
2086 if attacker_choice.move_type == PokemonType::BUG
2087 && attacking_pkmn.hp <= attacking_pkmn.maxhp / 3
2088 {
2089 attacker_choice.base_power *= 1.5;
2090 }
2091 }
2092 Abilities::GORILLATACTICS => {
2093 if attacker_choice.category == MoveCategory::Physical {
2094 attacker_choice.base_power *= 1.5;
2095 }
2096 }
2097 Abilities::BLAZE => {
2098 if attacker_choice.move_type == PokemonType::FIRE
2099 && attacking_pkmn.hp <= attacking_pkmn.maxhp / 3
2100 {
2101 attacker_choice.base_power *= 1.5;
2102 }
2103 }
2104 Abilities::OVERGROW => {
2105 if attacker_choice.move_type == PokemonType::GRASS
2106 && attacking_pkmn.hp <= attacking_pkmn.maxhp / 3
2107 {
2108 attacker_choice.base_power *= 1.5;
2109 }
2110 }
2111 Abilities::ANALYTIC => {
2112 if !attacker_choice.first_move {
2113 attacker_choice.base_power *= 1.3;
2114 }
2115 }
2116 Abilities::MEGALAUNCHER => {
2117 if attacker_choice.flags.pulse {
2118 attacker_choice.base_power *= 1.5;
2119 };
2120 }
2121 #[cfg(any(feature = "gen9", feature = "gen8", feature = "gen7"))]
2122 Abilities::PIXILATE => {
2123 if attacker_choice.move_type == PokemonType::NORMAL {
2124 attacker_choice.move_type = PokemonType::FAIRY;
2125 attacker_choice.base_power *= 1.2;
2126 }
2127 }
2128 #[cfg(any(feature = "gen6", feature = "gen5", feature = "gen4"))]
2129 Abilities::PIXILATE => {
2130 if attacker_choice.move_type == PokemonType::NORMAL {
2131 attacker_choice.move_type = PokemonType::FAIRY;
2132 attacker_choice.base_power *= 1.3;
2133 }
2134 }
2135 Abilities::DEFEATIST => {
2136 if attacking_pkmn.hp <= attacking_pkmn.maxhp / 2 {
2137 attacker_choice.base_power *= 0.5;
2138 }
2139 }
2140 Abilities::ROCKYPAYLOAD => {
2141 if attacker_choice.move_type == PokemonType::ROCK {
2142 attacker_choice.base_power *= 1.5;
2143 }
2144 }
2145 Abilities::PUNKROCK => {
2146 if attacker_choice.flags.sound {
2147 attacker_choice.base_power *= 1.3;
2148 }
2149 }
2150 Abilities::STRONGJAW => {
2151 if attacker_choice.flags.bite {
2152 attacker_choice.base_power *= 1.5;
2153 }
2154 }
2155 Abilities::BATTERY => {
2156 if attacker_choice.category == MoveCategory::Special {
2157 attacker_choice.base_power *= 1.3;
2158 }
2159 }
2160 Abilities::SHEERFORCE => {
2161 let mut sheer_force_volatile_boosted = false;
2162 if let Some(attacker_volatile_status) = &attacker_choice.volatile_status {
2163 if attacker_volatile_status.volatile_status
2164 != PokemonVolatileStatus::PARTIALLYTRAPPED
2165 && attacker_volatile_status.volatile_status != PokemonVolatileStatus::LOCKEDMOVE
2166 && attacker_volatile_status.volatile_status != PokemonVolatileStatus::SMACKDOWN
2167 {
2168 sheer_force_volatile_boosted = true;
2169 }
2170 }
2171 if attacker_choice.secondaries.is_some() || sheer_force_volatile_boosted {
2172 attacker_choice.base_power *= 1.3;
2173 attacker_choice.secondaries = None;
2174 attacker_choice.volatile_status = None
2175 }
2176 }
2177 Abilities::IRONFIST => {
2178 if attacker_choice.flags.punch {
2179 attacker_choice.base_power *= 1.2;
2180 }
2181 }
2182 Abilities::UNSEENFIST => {
2183 if attacker_choice.flags.contact {
2184 attacker_choice.flags.protect = false
2185 }
2186 }
2187 Abilities::HUSTLE => {
2188 if attacker_choice.category == MoveCategory::Physical {
2189 attacker_choice.base_power *= 1.5;
2190 attacker_choice.accuracy *= 0.80
2191 }
2192 }
2193 Abilities::POISONTOUCH => {
2194 if attacker_choice.flags.contact {
2195 attacker_choice.add_or_create_secondaries(Secondary {
2196 chance: 30.0,
2197 target: MoveTarget::Opponent,
2198 effect: Effect::Status(PokemonStatus::POISON),
2199 })
2200 }
2201 }
2202 Abilities::TOXICCHAIN => {
2203 if attacker_choice.target == MoveTarget::Opponent {
2204 attacker_choice.add_or_create_secondaries(Secondary {
2205 chance: 30.0,
2206 target: MoveTarget::Opponent,
2207 effect: Effect::Status(PokemonStatus::TOXIC),
2208 })
2209 }
2210 }
2211 Abilities::GUTS => {
2212 if attacking_pkmn.status != PokemonStatus::NONE {
2213 attacker_choice.base_power *= 1.5;
2214
2215 if attacking_pkmn.status == PokemonStatus::BURN
2217 && attacker_choice.category == MoveCategory::Physical
2218 {
2219 attacker_choice.base_power *= 2.0;
2220 }
2221 }
2222 }
2223 Abilities::SANDFORCE => {
2224 if state.weather_is_active(&Weather::SAND)
2225 && (attacker_choice.move_type == PokemonType::ROCK
2226 || attacker_choice.move_type == PokemonType::GROUND
2227 || attacker_choice.move_type == PokemonType::STEEL)
2228 {
2229 attacker_choice.base_power *= 1.3;
2230 }
2231 }
2232 Abilities::TOXICBOOST => {
2233 if attacking_pkmn.status == PokemonStatus::POISON
2234 || attacking_pkmn.status == PokemonStatus::TOXIC
2235 {
2236 attacker_choice.base_power *= 1.5;
2237 }
2238 }
2239 _ => {}
2240 }
2241}
2242
2243pub fn ability_modify_attack_against(
2244 state: &State,
2245 attacker_choice: &mut Choice,
2246 defender_choice: &Choice,
2247 attacking_side_ref: &SideReference,
2248) {
2249 let (attacking_side, defending_side) = state.get_both_sides_immutable(attacking_side_ref);
2250 let attacking_pkmn = attacking_side.get_active_immutable();
2251 let target_pkmn = defending_side.get_active_immutable();
2252 if target_pkmn.ability == Abilities::NEUTRALIZINGGAS {
2253 return;
2254 }
2255 if (attacking_pkmn.ability == Abilities::MOLDBREAKER
2256 || (attacking_pkmn.ability == Abilities::MYCELIUMMIGHT
2257 && attacker_choice.category == MoveCategory::Status)
2258 || attacking_pkmn.ability == Abilities::TERAVOLT
2259 || attacking_pkmn.ability == Abilities::TURBOBLAZE)
2260 && mold_breaker_ignores(&target_pkmn.ability)
2261 {
2262 return;
2263 }
2264
2265 match target_pkmn.ability {
2266 Abilities::TABLETSOFRUIN => {
2267 if attacker_choice.category == MoveCategory::Physical {
2268 attacker_choice.base_power *= 0.75;
2269 }
2270 }
2271 Abilities::VESSELOFRUIN => {
2272 if attacker_choice.category == MoveCategory::Special {
2273 attacker_choice.base_power *= 0.75;
2274 }
2275 }
2276 Abilities::ARMORTAIL => {
2277 if attacker_choice.priority > 0 && attacker_choice.category != MoveCategory::Status {
2278 attacker_choice.remove_all_effects();
2279 }
2280 }
2281 Abilities::SOUNDPROOF => {
2282 if attacker_choice.flags.sound {
2283 attacker_choice.remove_all_effects();
2284 attacker_choice.accuracy = 0.0;
2285 }
2286 }
2287 Abilities::POISONPOINT => {
2288 if attacker_choice.flags.contact {
2289 attacker_choice.add_or_create_secondaries(Secondary {
2290 chance: 33.0,
2291 target: MoveTarget::User,
2292 effect: Effect::Status(PokemonStatus::POISON),
2293 })
2294 }
2295 }
2296 Abilities::BULLETPROOF => {
2297 if attacker_choice.flags.bullet {
2298 attacker_choice.remove_all_effects();
2299 attacker_choice.accuracy = 0.0;
2300 }
2301 }
2302 Abilities::MULTISCALE => {
2303 if target_pkmn.hp == target_pkmn.maxhp {
2304 attacker_choice.base_power /= 2.0;
2305 }
2306 }
2307 Abilities::LIGHTNINGROD => {
2308 if attacker_choice.move_type == PokemonType::ELECTRIC {
2309 attacker_choice.remove_all_effects();
2310 attacker_choice.accuracy = 100.0;
2311 attacker_choice.target = MoveTarget::Opponent;
2312 attacker_choice.boost = Some(Boost {
2313 boosts: StatBoosts {
2314 attack: 0,
2315 defense: 0,
2316 special_attack: 1,
2317 special_defense: 0,
2318 speed: 0,
2319 accuracy: 0,
2320 },
2321 target: MoveTarget::Opponent,
2322 });
2323 attacker_choice.category = MoveCategory::Status;
2324 }
2325 }
2326 Abilities::EARTHEATER => {
2327 if attacker_choice.move_type == PokemonType::GROUND {
2328 attacker_choice.remove_all_effects();
2329 attacker_choice.base_power = 0.0;
2330 attacker_choice.heal = Some(Heal {
2331 target: MoveTarget::Opponent,
2332 amount: 0.25,
2333 });
2334 attacker_choice.category = MoveCategory::Status;
2335 }
2336 }
2337 Abilities::STEAMENGINE => {
2338 if attacker_choice.move_type == PokemonType::WATER
2339 || attacker_choice.move_type == PokemonType::FIRE
2340 {
2341 attacker_choice.add_or_create_secondaries(Secondary {
2342 chance: 100.0,
2343 target: MoveTarget::Opponent,
2344 effect: Effect::Boost(StatBoosts {
2345 attack: 0,
2346 defense: 0,
2347 special_attack: 0,
2348 special_defense: 0,
2349 speed: 6,
2350 accuracy: 0,
2351 }),
2352 });
2353 }
2354 }
2355 Abilities::THERMALEXCHANGE => {
2356 if attacker_choice.move_type == PokemonType::FIRE {
2357 attacker_choice.add_or_create_secondaries(Secondary {
2358 chance: 100.0,
2359 target: MoveTarget::Opponent,
2360 effect: Effect::Boost(StatBoosts {
2361 attack: 1,
2362 defense: 0,
2363 special_attack: 0,
2364 special_defense: 0,
2365 speed: 0,
2366 accuracy: 0,
2367 }),
2368 });
2369 }
2370 }
2371 #[cfg(any(feature = "gen9", feature = "gen8", feature = "gen7"))]
2372 Abilities::WEAKARMOR => {
2373 if attacker_choice.category == MoveCategory::Physical {
2374 attacker_choice.add_or_create_secondaries(Secondary {
2375 chance: 100.0,
2376 target: MoveTarget::Opponent,
2377 effect: Effect::Boost(StatBoosts {
2378 attack: 0,
2379 defense: -1,
2380 special_attack: 0,
2381 special_defense: 0,
2382 speed: 2,
2383 accuracy: 0,
2384 }),
2385 });
2386 }
2387 }
2388 #[cfg(any(feature = "gen6", feature = "gen5", feature = "gen4"))]
2389 Abilities::WEAKARMOR => {
2390 if attacker_choice.category == MoveCategory::Physical {
2391 attacker_choice.add_or_create_secondaries(Secondary {
2392 chance: 100.0,
2393 target: MoveTarget::Opponent,
2394 effect: Effect::Boost(StatBoosts {
2395 attack: 0,
2396 defense: -1,
2397 special_attack: 0,
2398 special_defense: 0,
2399 speed: 1,
2400 accuracy: 0,
2401 }),
2402 });
2403 }
2404 }
2405 Abilities::QUEENLYMAJESTY => {
2406 if attacker_choice.priority > 0 {
2407 attacker_choice.remove_all_effects();
2408 attacker_choice.accuracy = 0.0;
2409 }
2410 }
2411 Abilities::SAPSIPPER => {
2412 if attacker_choice.move_type == PokemonType::GRASS {
2413 attacker_choice.remove_all_effects();
2414 attacker_choice.accuracy = 100.0;
2415 attacker_choice.target = MoveTarget::Opponent;
2416 attacker_choice.boost = Some(Boost {
2417 boosts: StatBoosts {
2418 attack: 1,
2419 defense: 0,
2420 special_attack: 0,
2421 special_defense: 0,
2422 speed: 0,
2423 accuracy: 0,
2424 },
2425 target: MoveTarget::Opponent,
2426 });
2427 attacker_choice.category = MoveCategory::Status;
2428 }
2429 }
2430 Abilities::SHADOWSHIELD => {
2431 if target_pkmn.hp == target_pkmn.maxhp {
2432 attacker_choice.base_power /= 2.0;
2433 }
2434 }
2435 Abilities::NOGUARD => {
2436 attacker_choice.accuracy = 100.0;
2437 }
2438 Abilities::MARVELSCALE => {
2439 if target_pkmn.status != PokemonStatus::NONE
2440 && attacker_choice.category == MoveCategory::Physical
2441 {
2442 attacker_choice.base_power /= 1.5;
2443 }
2444 }
2445 #[cfg(feature = "gen3")]
2446 Abilities::EFFECTSPORE => {
2447 if attacker_choice.flags.contact {
2448 attacker_choice.add_or_create_secondaries(Secondary {
2449 chance: 3.30,
2450 target: MoveTarget::User,
2451 effect: Effect::Status(PokemonStatus::POISON),
2452 });
2453 attacker_choice.add_or_create_secondaries(Secondary {
2454 chance: 3.30,
2455 target: MoveTarget::User,
2456 effect: Effect::Status(PokemonStatus::PARALYZE),
2457 });
2458 attacker_choice.add_or_create_secondaries(Secondary {
2459 chance: 3.30,
2460 target: MoveTarget::User,
2461 effect: Effect::Status(PokemonStatus::SLEEP),
2462 });
2463 }
2464 }
2465
2466 #[cfg(not(feature = "gen3"))]
2467 Abilities::EFFECTSPORE => {
2468 if attacker_choice.flags.contact {
2469 attacker_choice.add_or_create_secondaries(Secondary {
2470 chance: 9.0,
2471 target: MoveTarget::User,
2472 effect: Effect::Status(PokemonStatus::POISON),
2473 });
2474 attacker_choice.add_or_create_secondaries(Secondary {
2475 chance: 10.0,
2476 target: MoveTarget::User,
2477 effect: Effect::Status(PokemonStatus::PARALYZE),
2478 });
2479 attacker_choice.add_or_create_secondaries(Secondary {
2480 chance: 11.0,
2481 target: MoveTarget::User,
2482 effect: Effect::Status(PokemonStatus::SLEEP),
2483 });
2484 }
2485 }
2486 Abilities::FLAMEBODY => {
2487 if attacker_choice.flags.contact {
2488 attacker_choice.add_or_create_secondaries(Secondary {
2489 chance: 30.0,
2490 target: MoveTarget::User,
2491 effect: Effect::Status(PokemonStatus::BURN),
2492 });
2493 }
2494 }
2495 Abilities::GOOEY => {
2496 if attacker_choice.flags.contact {
2497 attacker_choice.add_or_create_secondaries(Secondary {
2498 chance: 100.0,
2499 target: MoveTarget::User,
2500 effect: Effect::Boost(StatBoosts {
2501 attack: 0,
2502 defense: 0,
2503 special_attack: 0,
2504 special_defense: 0,
2505 speed: -1,
2506 accuracy: 0,
2507 }),
2508 })
2509 }
2510 }
2511 Abilities::MOTORDRIVE => {
2512 if attacker_choice.move_type == PokemonType::ELECTRIC {
2513 attacker_choice.remove_all_effects();
2514 attacker_choice.accuracy = 100.0;
2515 attacker_choice.target = MoveTarget::Opponent;
2516 attacker_choice.boost = Some(Boost {
2517 boosts: StatBoosts {
2518 attack: 0,
2519 defense: 0,
2520 special_attack: 0,
2521 special_defense: 0,
2522 speed: 1,
2523 accuracy: 0,
2524 },
2525 target: MoveTarget::Opponent,
2526 });
2527 attacker_choice.category = MoveCategory::Status;
2528 }
2529 }
2530 Abilities::WINDRIDER => {
2531 if attacker_choice.flags.wind {
2532 attacker_choice.remove_all_effects();
2533 attacker_choice.accuracy = 100.0;
2534 attacker_choice.target = MoveTarget::Opponent;
2535 attacker_choice.boost = Some(Boost {
2536 boosts: StatBoosts {
2537 attack: 1,
2538 defense: 0,
2539 special_attack: 0,
2540 special_defense: 0,
2541 speed: 0,
2542 accuracy: 0,
2543 },
2544 target: MoveTarget::Opponent,
2545 });
2546 attacker_choice.category = MoveCategory::Status;
2547 }
2548 }
2549 Abilities::SUCTIONCUPS => {
2550 attacker_choice.flags.drag = false;
2551 }
2552 Abilities::WONDERGUARD => {
2553 if attacker_choice.category != MoveCategory::Status
2554 && type_effectiveness_modifier(&attacker_choice.move_type, &target_pkmn) <= 1.0
2555 {
2556 attacker_choice.remove_all_effects();
2557 attacker_choice.base_power = 0.0;
2558 }
2559 }
2560 Abilities::FAIRYAURA => {
2561 if attacker_choice.move_type == PokemonType::FAIRY {
2562 attacker_choice.base_power *= 1.33;
2563 }
2564 }
2565 Abilities::LEVITATE => {
2566 if attacker_choice.move_type == PokemonType::GROUND
2567 && attacker_choice.target == MoveTarget::Opponent
2568 && attacker_choice.move_id != Choices::THOUSANDARROWS
2569 {
2570 attacker_choice.base_power = 0.0;
2571 }
2572 }
2573 Abilities::STATIC => {
2574 if attacker_choice.flags.contact {
2575 attacker_choice.add_or_create_secondaries(Secondary {
2576 chance: 30.0,
2577 target: MoveTarget::User,
2578 effect: Effect::Status(PokemonStatus::PARALYZE),
2579 })
2580 }
2581 }
2582 Abilities::WONDERSKIN => {
2583 if attacker_choice.category == MoveCategory::Status && attacker_choice.accuracy > 50.0 {
2584 attacker_choice.accuracy = 50.0;
2585 }
2586 }
2587 Abilities::THICKFAT => {
2588 if attacker_choice.move_type == PokemonType::FIRE
2589 || attacker_choice.move_type == PokemonType::ICE
2590 {
2591 attacker_choice.base_power /= 2.0;
2592 }
2593 }
2594 Abilities::FLASHFIRE => {
2595 if attacker_choice.move_type == PokemonType::FIRE {
2596 attacker_choice.remove_all_effects();
2597 attacker_choice.volatile_status = Some(VolatileStatus {
2598 target: MoveTarget::Opponent,
2599 volatile_status: PokemonVolatileStatus::FLASHFIRE,
2600 });
2601 }
2602 }
2603 Abilities::WELLBAKEDBODY => {
2604 if attacker_choice.move_type == PokemonType::FIRE {
2605 attacker_choice.remove_all_effects();
2606 attacker_choice.boost = Some(Boost {
2607 boosts: StatBoosts {
2608 attack: 0,
2609 defense: 2,
2610 special_attack: 0,
2611 special_defense: 0,
2612 speed: 0,
2613 accuracy: 0,
2614 },
2615 target: MoveTarget::Opponent,
2616 });
2617 }
2618 }
2619 Abilities::DAZZLING => {
2620 if attacker_choice.priority > 0 {
2621 attacker_choice.accuracy = 0.0;
2622 }
2623 }
2624 Abilities::LIQUIDOOZE => {
2625 if let Some(drain) = attacker_choice.drain {
2626 attacker_choice.drain = Some(-1.0 * drain);
2627 }
2628 }
2629 Abilities::PRISMARMOR => {
2630 if type_effectiveness_modifier(&attacker_choice.move_type, &target_pkmn) > 1.0 {
2631 attacker_choice.base_power *= 0.75;
2632 }
2633 }
2634 Abilities::HEATPROOF => {
2635 if attacker_choice.move_type == PokemonType::FIRE {
2636 attacker_choice.base_power *= 0.5;
2637 }
2638 }
2639 Abilities::SHIELDDUST => {
2640 if let Some(secondaries) = &mut attacker_choice.secondaries {
2641 for secondary in secondaries.iter_mut() {
2642 if secondary.target == MoveTarget::Opponent {
2643 secondary.chance = 0.0;
2644 }
2645 }
2646 }
2647 }
2648 Abilities::GRASSPELT => {
2649 if state.terrain_is_active(&Terrain::GRASSYTERRAIN)
2650 && attacker_choice.category == MoveCategory::Physical
2651 {
2652 attacker_choice.base_power /= 1.5;
2653 }
2654 }
2655 Abilities::FILTER => {
2656 if type_effectiveness_modifier(&attacker_choice.move_type, &target_pkmn) > 1.0 {
2657 attacker_choice.base_power *= 0.75;
2658 }
2659 }
2660 Abilities::FURCOAT => {
2661 if attacker_choice.category == MoveCategory::Physical {
2662 attacker_choice.base_power *= 0.5;
2663 }
2664 }
2665 Abilities::TANGLINGHAIR => {
2666 if attacker_choice.flags.contact {
2667 attacker_choice.add_or_create_secondaries(Secondary {
2668 chance: 100.0,
2669 target: MoveTarget::User,
2670 effect: Effect::Boost(StatBoosts {
2671 attack: 0,
2672 defense: 0,
2673 special_attack: 0,
2674 special_defense: 0,
2675 speed: -1,
2676 accuracy: 0,
2677 }),
2678 })
2679 }
2680 }
2681 Abilities::MAGICBOUNCE => {
2682 if attacker_choice.flags.reflectable {
2683 attacker_choice.target = MoveTarget::User;
2684 if let Some(side_condition) = &mut attacker_choice.side_condition {
2685 if side_condition.target == MoveTarget::Opponent {
2686 side_condition.target = MoveTarget::User;
2687 }
2688 }
2689 if let Some(status) = &mut attacker_choice.status {
2690 if status.target == MoveTarget::Opponent {
2691 status.target = MoveTarget::User;
2692 }
2693 }
2694 if let Some(volatile_status) = &mut attacker_choice.volatile_status {
2695 if volatile_status.target == MoveTarget::Opponent {
2696 volatile_status.target = MoveTarget::User;
2697 }
2698 }
2699 }
2700 }
2701 Abilities::STORMDRAIN => {
2702 if attacker_choice.move_type == PokemonType::WATER {
2703 attacker_choice.remove_all_effects();
2704 attacker_choice.accuracy = 100.0;
2705 attacker_choice.target = MoveTarget::Opponent;
2706 attacker_choice.boost = Some(Boost {
2707 boosts: StatBoosts {
2708 attack: 0,
2709 defense: 0,
2710 special_attack: 1,
2711 special_defense: 0,
2712 speed: 0,
2713 accuracy: 0,
2714 },
2715 target: MoveTarget::Opponent,
2716 });
2717 attacker_choice.category = MoveCategory::Status;
2718 }
2719 }
2720 Abilities::WATERCOMPACTION => {
2721 if attacker_choice.move_type == PokemonType::WATER {
2722 attacker_choice.add_or_create_secondaries(Secondary {
2723 chance: 100.0,
2724 target: MoveTarget::Opponent,
2725 effect: Effect::Boost(StatBoosts {
2726 attack: 0,
2727 defense: 2,
2728 special_attack: 0,
2729 special_defense: 0,
2730 speed: 0,
2731 accuracy: 0,
2732 }),
2733 });
2734 }
2735 }
2736 Abilities::JUSTIFIED => {
2737 if attacker_choice.move_type == PokemonType::DARK {
2738 attacker_choice.add_or_create_secondaries(Secondary {
2739 chance: 100.0,
2740 target: MoveTarget::Opponent,
2741 effect: Effect::Boost(StatBoosts {
2742 attack: 1,
2743 defense: 0,
2744 special_attack: 0,
2745 special_defense: 0,
2746 speed: 0,
2747 accuracy: 0,
2748 }),
2749 })
2750 }
2751 }
2752 Abilities::ICESCALES => {
2753 if attacker_choice.category == MoveCategory::Special {
2754 attacker_choice.base_power *= 0.5;
2755 }
2756 }
2757 Abilities::WATERABSORB => {
2758 if attacker_choice.move_type == PokemonType::WATER {
2759 attacker_choice.remove_all_effects();
2760 attacker_choice.base_power = 0.0;
2761 attacker_choice.heal = Some(Heal {
2762 target: MoveTarget::Opponent,
2763 amount: 0.25,
2764 });
2765 attacker_choice.category = MoveCategory::Status;
2766 }
2767 }
2768 Abilities::DRYSKIN => {
2769 if attacker_choice.move_type == PokemonType::WATER {
2770 attacker_choice.remove_all_effects();
2771 attacker_choice.base_power = 0.0;
2772 attacker_choice.heal = Some(Heal {
2773 target: MoveTarget::Opponent,
2774 amount: 0.25,
2775 });
2776 attacker_choice.category = MoveCategory::Status;
2777 } else if attacker_choice.move_type == PokemonType::FIRE {
2778 attacker_choice.base_power *= 1.25;
2779 }
2780 }
2781 Abilities::FLUFFY => {
2782 if attacker_choice.flags.contact {
2783 attacker_choice.base_power *= 0.5;
2784 }
2785 if attacker_choice.move_type == PokemonType::FIRE {
2786 attacker_choice.base_power *= 2.0;
2787 }
2788 }
2789 Abilities::PUNKROCK => {
2790 if attacker_choice.flags.sound {
2791 attacker_choice.base_power /= 2.0;
2792 }
2793 }
2794 Abilities::DAMP => {
2795 if [
2796 Choices::SELFDESTRUCT,
2797 Choices::EXPLOSION,
2798 Choices::MINDBLOWN,
2799 Choices::MISTYEXPLOSION,
2800 ]
2801 .contains(&attacker_choice.move_id)
2802 {
2803 attacker_choice.accuracy = 0.0;
2804 attacker_choice.heal = None;
2805 }
2806 }
2807 Abilities::VOLTABSORB => {
2808 #[cfg(feature = "gen3")]
2809 let activate = attacker_choice.move_type == PokemonType::ELECTRIC
2810 && attacker_choice.category != MoveCategory::Status;
2811
2812 #[cfg(not(feature = "gen3"))]
2813 let activate = attacker_choice.move_type == PokemonType::ELECTRIC;
2814
2815 if activate {
2816 attacker_choice.remove_all_effects();
2817 attacker_choice.accuracy = 100.0;
2818 attacker_choice.base_power = 0.0;
2819 attacker_choice.heal = Some(Heal {
2820 target: MoveTarget::Opponent,
2821 amount: 0.25,
2822 });
2823 attacker_choice.category = MoveCategory::Status;
2824 }
2825 }
2826 Abilities::SOLIDROCK => {
2827 if type_effectiveness_modifier(&attacker_choice.move_type, &target_pkmn) > 1.0 {
2828 attacker_choice.base_power *= 0.75;
2829 }
2830 }
2831 Abilities::OVERCOAT => {
2832 if attacker_choice.flags.powder {
2833 attacker_choice.remove_all_effects();
2834 attacker_choice.accuracy = 0.0
2835 }
2836 }
2837 Abilities::GOODASGOLD => {
2838 if attacker_choice.category == MoveCategory::Status
2841 && ![
2842 Choices::STEALTHROCK,
2843 Choices::STICKYWEB,
2844 Choices::TOXICSPIKES,
2845 Choices::SPIKES,
2846 ]
2847 .contains(&attacker_choice.move_id)
2848 {
2849 attacker_choice.remove_all_effects();
2850 }
2851 }
2852 Abilities::RATTLED => {
2853 if attacker_choice.move_type == PokemonType::BUG
2854 || attacker_choice.move_type == PokemonType::DARK
2855 || attacker_choice.move_type == PokemonType::GHOST
2856 {
2857 attacker_choice.add_or_create_secondaries(Secondary {
2858 chance: 100.0,
2859 target: MoveTarget::Opponent,
2860 effect: Effect::Boost(StatBoosts {
2861 attack: 0,
2862 defense: 0,
2863 special_attack: 0,
2864 special_defense: 0,
2865 speed: 1,
2866 accuracy: 0,
2867 }),
2868 });
2869 }
2870 }
2871 Abilities::WATERBUBBLE => {
2872 if attacker_choice.move_type == PokemonType::FIRE {
2873 attacker_choice.base_power /= 2.0;
2874 }
2875 }
2876 Abilities::PURIFYINGSALT => {
2877 if attacker_choice.move_type == PokemonType::GHOST {
2878 attacker_choice.base_power /= 2.0;
2879 }
2880 }
2881 _ => {}
2882 }
2883}