poke_engine/
abilities.rs

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
352// https://bulbapedia.bulbagarden.net/wiki/Ignoring_Abilities#Ignorable_Abilities
353fn 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        // Incomplete: IceFace should not stop secondaries, but setting base_power to 0 makes
519        // secondaries not apply in this engine
520        Abilities::ICEFACE => {
521            if defending_pkmn.id == PokemonName::EISCUE && choice.category == MoveCategory::Physical
522            {
523                // hacky - changing the move to a status move makes it do no
524                // damage but preserve secondary effects
525                // due to some bad choices I made I cannot just set base_power to 0
526                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        // Technically incorrect
539        // A move missing should not trigger this formechange
540        #[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            // Not complete: Toxic Spikes are not applied if a substitute is hit
921            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    // revert ability on switch-out to base_ability if they are not the same
1143    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        // Shed skin only has a 1/3 chance of activating at the end of the turn
1364        // but I'm not going to branch on that here
1365        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    // trace copying an ability needs to happen before the ability check to activate on switch-in
1396    // e.g. tracing intimidate will activate intimidate
1397    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            // no need to check for boost at +6 because we are switching in
1499            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            // no need to check for boost at +6 because we are switching in
1627            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                // not the right place to put this, but good enough
2216                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            // This engine doesn't distinguish "targetting other pkmn" versus "targetting the side"
2839            // Thankfully it is a short list of moves that target the opponent side
2840            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}