poke_engine/genx/
abilities.rs

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