poke_engine/genx/
abilities.rs

1#![allow(unused_variables)]
2use super::damage_calc::type_effectiveness_modifier;
3use super::generate_instructions::{add_remove_status_instructions, get_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                let mut instructions_vec = Vec::with_capacity(3);
691                if let Some(boost_instruction) = get_boost_instruction(
692                    &attacking_side,
693                    &PokemonBoostableStat::Attack,
694                    &1,
695                    side_ref,
696                    side_ref,
697                ) {
698                    instructions_vec.push(boost_instruction);
699                }
700                if let Some(boost_instruction) = get_boost_instruction(
701                    &attacking_side,
702                    &PokemonBoostableStat::SpecialAttack,
703                    &1,
704                    side_ref,
705                    side_ref,
706                ) {
707                    instructions_vec.push(boost_instruction);
708                }
709                if let Some(boost_instruction) = get_boost_instruction(
710                    &attacking_side,
711                    &PokemonBoostableStat::Speed,
712                    &1,
713                    side_ref,
714                    side_ref,
715                ) {
716                    instructions_vec.push(boost_instruction);
717                }
718                for i in instructions_vec {
719                    state.apply_one_instruction(&i);
720                    instructions.instruction_list.push(i);
721                }
722            }
723        }
724        Abilities::MAGICIAN | Abilities::PICKPOCKET => {
725            let defending_pkmn = defending_side.get_active();
726            if damage_dealt > 0
727                && defending_pkmn.item_can_be_removed()
728                && active_pkmn.item == Items::NONE
729            {
730                instructions.instruction_list.push(Instruction::ChangeItem(
731                    ChangeItemInstruction {
732                        side_ref: *side_ref,
733                        current_item: active_pkmn.item,
734                        new_item: defending_pkmn.item,
735                    },
736                ));
737                active_pkmn.item = defending_pkmn.item;
738                instructions.instruction_list.push(Instruction::ChangeItem(
739                    ChangeItemInstruction {
740                        side_ref: side_ref.get_other_side(),
741                        current_item: defending_pkmn.item,
742                        new_item: Items::NONE,
743                    },
744                ));
745                defending_pkmn.item = Items::NONE;
746            }
747        }
748        Abilities::MOXIE | Abilities::CHILLINGNEIGH | Abilities::ASONEGLASTRIER => {
749            if damage_dealt > 0 && defending_side.get_active_immutable().hp == 0 {
750                if let Some(boost_instruction) = get_boost_instruction(
751                    &attacking_side,
752                    &PokemonBoostableStat::Attack,
753                    &1,
754                    side_ref,
755                    side_ref,
756                ) {
757                    state.apply_one_instruction(&boost_instruction);
758                    instructions.instruction_list.push(boost_instruction);
759                }
760            }
761        }
762        Abilities::GRIMNEIGH | Abilities::ASONESPECTRIER => {
763            if damage_dealt > 0 && defending_side.get_active_immutable().hp == 0 {
764                if let Some(boost_instruction) = get_boost_instruction(
765                    &attacking_side,
766                    &PokemonBoostableStat::SpecialAttack,
767                    &1,
768                    side_ref,
769                    side_ref,
770                ) {
771                    state.apply_one_instruction(&boost_instruction);
772                    instructions.instruction_list.push(boost_instruction);
773                }
774            }
775        }
776        Abilities::BEASTBOOST => {
777            if damage_dealt > 0 && defending_side.get_active_immutable().hp == 0 {
778                let highest_stat = &attacking_side.calculate_highest_stat();
779                if let Some(boost_instruction) =
780                    get_boost_instruction(&attacking_side, highest_stat, &1, side_ref, side_ref)
781                {
782                    state.apply_one_instruction(&boost_instruction);
783                    instructions.instruction_list.push(boost_instruction);
784                }
785            }
786        }
787        _ => {}
788    }
789    let (attacking_side, defending_side) = state.get_both_sides(side_ref);
790    let attacking_pkmn = attacking_side.get_active();
791    let defending_pkmn = defending_side.get_active();
792    match defending_pkmn.ability {
793        Abilities::MUMMY | Abilities::LINGERINGAROMA | Abilities::WANDERINGSPIRIT => {
794            if choice.flags.contact {
795                instructions
796                    .instruction_list
797                    .push(Instruction::ChangeAbility(ChangeAbilityInstruction {
798                        side_ref: *side_ref,
799                        ability_change: Abilities::MUMMY as i16 - attacking_pkmn.ability as i16,
800                    }));
801                attacking_pkmn.ability = Abilities::MUMMY;
802            }
803        }
804        Abilities::GULPMISSILE => {
805            if damage_dealt > 0
806                && [PokemonName::CRAMORANTGORGING, PokemonName::CRAMORANTGULPING]
807                    .contains(&defending_pkmn.id)
808            {
809                instructions.instruction_list.push(Instruction::FormeChange(
810                    FormeChangeInstruction {
811                        side_ref: side_ref.get_other_side(),
812                        name_change: PokemonName::CRAMORANT as i16 - defending_pkmn.id as i16,
813                    },
814                ));
815
816                let damage_dealt = cmp::min(attacking_pkmn.maxhp / 4, attacking_pkmn.hp);
817                instructions
818                    .instruction_list
819                    .push(Instruction::Damage(DamageInstruction {
820                        side_ref: *side_ref,
821                        damage_amount: damage_dealt,
822                    }));
823                attacking_pkmn.hp -= damage_dealt;
824
825                if defending_pkmn.id == PokemonName::CRAMORANTGULPING {
826                    defending_pkmn.id = PokemonName::CRAMORANT;
827                    if let Some(boost_instruction) = get_boost_instruction(
828                        &attacking_side,
829                        &PokemonBoostableStat::Defense,
830                        &-1,
831                        &side_ref.get_other_side(),
832                        side_ref,
833                    ) {
834                        state.apply_one_instruction(&boost_instruction);
835                        instructions.instruction_list.push(boost_instruction);
836                    }
837                } else if defending_pkmn.id == PokemonName::CRAMORANTGORGING {
838                    defending_pkmn.id = PokemonName::CRAMORANT;
839                    choice.add_or_create_secondaries(Secondary {
840                        chance: 100.0,
841                        target: MoveTarget::User,
842                        effect: Effect::Status(PokemonStatus::PARALYZE),
843                    })
844                }
845            }
846        }
847        Abilities::COLORCHANGE => {
848            if damage_dealt > 0
849                && defending_pkmn.hp != 0
850                && !defending_pkmn.has_type(&choice.move_type)
851            {
852                let change_type_instruction = Instruction::ChangeType(ChangeType {
853                    side_ref: side_ref.get_other_side(),
854                    new_types: (choice.move_type, PokemonType::TYPELESS),
855                    old_types: defending_pkmn.types,
856                });
857                defending_pkmn.types = (choice.move_type, PokemonType::TYPELESS);
858                instructions.instruction_list.push(change_type_instruction);
859            }
860        }
861        Abilities::STAMINA => {
862            if damage_dealt > 0 && defending_pkmn.hp != 0 {
863                if let Some(boost_instruction) = get_boost_instruction(
864                    &defending_side,
865                    &PokemonBoostableStat::Defense,
866                    &1,
867                    side_ref,
868                    &side_ref.get_other_side(),
869                ) {
870                    state.apply_one_instruction(&boost_instruction);
871                    instructions.instruction_list.push(boost_instruction);
872                }
873            }
874        }
875        Abilities::COTTONDOWN => {
876            if damage_dealt > 0 {
877                if let Some(boost_instruction) = get_boost_instruction(
878                    &attacking_side,
879                    &PokemonBoostableStat::Speed,
880                    &-1,
881                    &side_ref.get_other_side(),
882                    side_ref,
883                ) {
884                    state.apply_one_instruction(&boost_instruction);
885                    instructions.instruction_list.push(boost_instruction);
886                }
887            }
888        }
889        Abilities::SANDSPIT => {
890            if damage_dealt > 0 && state.weather.weather_type != Weather::SAND {
891                instructions
892                    .instruction_list
893                    .push(Instruction::ChangeWeather(ChangeWeather {
894                        new_weather: Weather::SAND,
895                        new_weather_turns_remaining: WEATHER_ABILITY_TURNS,
896                        previous_weather: state.weather.weather_type,
897                        previous_weather_turns_remaining: state.weather.turns_remaining,
898                    }));
899                state.weather.weather_type = Weather::SAND;
900                state.weather.turns_remaining = WEATHER_ABILITY_TURNS;
901            }
902        }
903        Abilities::SEEDSOWER => {
904            if damage_dealt > 0 && state.terrain.terrain_type != Terrain::GRASSYTERRAIN {
905                instructions
906                    .instruction_list
907                    .push(Instruction::ChangeTerrain(ChangeTerrain {
908                        new_terrain: Terrain::GRASSYTERRAIN,
909                        new_terrain_turns_remaining: 5,
910                        previous_terrain: state.terrain.terrain_type,
911                        previous_terrain_turns_remaining: state.terrain.turns_remaining,
912                    }));
913                state.terrain.terrain_type = Terrain::GRASSYTERRAIN;
914                state.terrain.turns_remaining = 5;
915            }
916        }
917        Abilities::TOXICDEBRIS => {
918            // Not complete: Toxic Spikes are not applied if a substitute is hit
919            if damage_dealt > 0
920                && choice.category == MoveCategory::Physical
921                && attacking_side.side_conditions.toxic_spikes < 2
922            {
923                instructions
924                    .instruction_list
925                    .push(Instruction::ChangeSideCondition(
926                        ChangeSideConditionInstruction {
927                            side_ref: *side_ref,
928                            side_condition: PokemonSideCondition::ToxicSpikes,
929                            amount: 1,
930                        },
931                    ));
932                attacking_side.side_conditions.toxic_spikes += 1;
933            }
934        }
935        Abilities::BERSERK => {
936            if damage_dealt > 0
937                && defending_pkmn.hp < defending_pkmn.maxhp / 2
938                && defending_pkmn.hp + damage_dealt >= defending_pkmn.maxhp / 2
939            {
940                if let Some(boost_instruction) = get_boost_instruction(
941                    &defending_side,
942                    &PokemonBoostableStat::SpecialAttack,
943                    &1,
944                    &side_ref.get_other_side(),
945                    &side_ref.get_other_side(),
946                ) {
947                    state.apply_one_instruction(&boost_instruction);
948                    instructions.instruction_list.push(boost_instruction);
949                }
950            }
951        }
952        Abilities::ROUGHSKIN | Abilities::IRONBARBS => {
953            if damage_dealt > 0 && choice.flags.contact {
954                #[cfg(feature = "gen3")]
955                let damage_dealt = cmp::min(attacking_pkmn.maxhp / 16, attacking_pkmn.hp);
956
957                #[cfg(not(feature = "gen3"))]
958                let damage_dealt = cmp::min(attacking_pkmn.maxhp / 8, attacking_pkmn.hp);
959
960                instructions
961                    .instruction_list
962                    .push(Instruction::Damage(DamageInstruction {
963                        side_ref: *side_ref,
964                        damage_amount: damage_dealt,
965                    }));
966                attacking_pkmn.hp -= damage_dealt;
967            }
968        }
969        Abilities::AFTERMATH => {
970            if damage_dealt > 0
971                && defending_side.get_active_immutable().hp == 0
972                && choice.flags.contact
973            {
974                let damage_dealt = cmp::min(attacking_pkmn.maxhp / 4, attacking_pkmn.hp);
975                instructions
976                    .instruction_list
977                    .push(Instruction::Damage(DamageInstruction {
978                        side_ref: *side_ref,
979                        damage_amount: damage_dealt,
980                    }));
981                attacking_pkmn.hp -= damage_dealt;
982            }
983        }
984        Abilities::INNARDSOUT => {
985            if damage_dealt > 0 && defending_side.get_active_immutable().hp == 0 {
986                let damage_dealt = cmp::min(damage_dealt, attacking_pkmn.hp);
987                instructions
988                    .instruction_list
989                    .push(Instruction::Damage(DamageInstruction {
990                        side_ref: *side_ref,
991                        damage_amount: damage_dealt,
992                    }));
993                attacking_pkmn.hp -= damage_dealt;
994            }
995        }
996        Abilities::PERISHBODY => {
997            if damage_dealt > 0 && choice.flags.contact {
998                for side_ref in [SideReference::SideOne, SideReference::SideTwo] {
999                    let side = state.get_side(&side_ref);
1000                    let pkmn = side.get_active();
1001                    if pkmn.hp != 0
1002                        && pkmn.ability != Abilities::SOUNDPROOF
1003                        && !(side
1004                            .volatile_statuses
1005                            .contains(&PokemonVolatileStatus::PERISH4)
1006                            || side
1007                                .volatile_statuses
1008                                .contains(&PokemonVolatileStatus::PERISH3)
1009                            || side
1010                                .volatile_statuses
1011                                .contains(&PokemonVolatileStatus::PERISH2)
1012                            || side
1013                                .volatile_statuses
1014                                .contains(&PokemonVolatileStatus::PERISH1))
1015                    {
1016                        instructions
1017                            .instruction_list
1018                            .push(Instruction::ApplyVolatileStatus(
1019                                ApplyVolatileStatusInstruction {
1020                                    side_ref: side_ref,
1021                                    volatile_status: PokemonVolatileStatus::PERISH4,
1022                                },
1023                            ));
1024                        side.volatile_statuses
1025                            .insert(PokemonVolatileStatus::PERISH4);
1026                    }
1027                }
1028            }
1029        }
1030        _ => {}
1031    }
1032}
1033
1034pub fn ability_on_switch_out(
1035    state: &mut State,
1036    side_ref: &SideReference,
1037    instructions: &mut StateInstructions,
1038) {
1039    let (attacking_side, defending_side) = state.get_both_sides(side_ref);
1040    let active_pkmn = attacking_side.get_active();
1041    if defending_side.get_active_immutable().ability == Abilities::NEUTRALIZINGGAS {
1042        return;
1043    }
1044    match active_pkmn.ability {
1045        Abilities::GULPMISSILE if active_pkmn.base_ability == Abilities::GULPMISSILE => {
1046            if active_pkmn.id != PokemonName::CRAMORANT {
1047                instructions.instruction_list.push(Instruction::FormeChange(
1048                    FormeChangeInstruction {
1049                        side_ref: *side_ref,
1050                        name_change: PokemonName::CRAMORANT as i16 - active_pkmn.id as i16,
1051                    },
1052                ));
1053                active_pkmn.id = PokemonName::CRAMORANT;
1054            }
1055        }
1056        Abilities::ZEROTOHERO => {
1057            if active_pkmn.id == PokemonName::PALAFIN {
1058                instructions.instruction_list.push(Instruction::FormeChange(
1059                    FormeChangeInstruction {
1060                        side_ref: *side_ref,
1061                        name_change: PokemonName::PALAFINHERO as i16 - active_pkmn.id as i16,
1062                    },
1063                ));
1064                active_pkmn.id = PokemonName::PALAFINHERO;
1065                active_pkmn.recalculate_stats(side_ref, instructions);
1066            }
1067        }
1068        Abilities::HUNGERSWITCH => {
1069            if active_pkmn.id == PokemonName::MORPEKOHANGRY && !active_pkmn.terastallized {
1070                instructions.instruction_list.push(Instruction::FormeChange(
1071                    FormeChangeInstruction {
1072                        side_ref: *side_ref,
1073                        name_change: PokemonName::MORPEKO as i16 - active_pkmn.id as i16,
1074                    },
1075                ));
1076                active_pkmn.id = PokemonName::MORPEKO;
1077            }
1078        }
1079        Abilities::NATURALCURE => {
1080            if active_pkmn.status != PokemonStatus::NONE {
1081                let status = active_pkmn.status.clone();
1082                active_pkmn.status = PokemonStatus::NONE;
1083                instructions
1084                    .instruction_list
1085                    .push(Instruction::ChangeStatus(ChangeStatusInstruction {
1086                        side_ref: *side_ref,
1087                        pokemon_index: attacking_side.active_index,
1088                        old_status: status,
1089                        new_status: PokemonStatus::NONE,
1090                    }));
1091            }
1092        }
1093        Abilities::REGENERATOR => {
1094            let hp_recovered = cmp::min(active_pkmn.maxhp / 3, active_pkmn.maxhp - active_pkmn.hp);
1095
1096            if hp_recovered > 0 && active_pkmn.hp > 0 {
1097                instructions
1098                    .instruction_list
1099                    .push(Instruction::Heal(HealInstruction {
1100                        side_ref: *side_ref,
1101                        heal_amount: hp_recovered,
1102                    }));
1103                active_pkmn.hp += hp_recovered;
1104            }
1105        }
1106        Abilities::PRIMORDIALSEA => {
1107            if state.weather.weather_type == Weather::HEAVYRAIN {
1108                instructions
1109                    .instruction_list
1110                    .push(Instruction::ChangeWeather(ChangeWeather {
1111                        new_weather: Weather::NONE,
1112                        new_weather_turns_remaining: -1,
1113                        previous_weather: state.weather.weather_type,
1114                        previous_weather_turns_remaining: state.weather.turns_remaining,
1115                    }));
1116                state.weather.weather_type = Weather::NONE;
1117                state.weather.turns_remaining = -1;
1118            }
1119        }
1120        Abilities::DESOLATELAND => {
1121            if state.weather.weather_type == Weather::HARSHSUN {
1122                instructions
1123                    .instruction_list
1124                    .push(Instruction::ChangeWeather(ChangeWeather {
1125                        new_weather: Weather::NONE,
1126                        new_weather_turns_remaining: -1,
1127                        previous_weather: state.weather.weather_type,
1128                        previous_weather_turns_remaining: state.weather.turns_remaining,
1129                    }));
1130                state.weather.weather_type = Weather::NONE;
1131                state.weather.turns_remaining = -1;
1132            }
1133        }
1134        _ => {}
1135    }
1136
1137    // revert ability on switch-out to base_ability if they are not the same
1138    let active_pkmn = state.get_side(side_ref).get_active();
1139    if active_pkmn.ability != active_pkmn.base_ability {
1140        instructions
1141            .instruction_list
1142            .push(Instruction::ChangeAbility(ChangeAbilityInstruction {
1143                side_ref: *side_ref,
1144                ability_change: active_pkmn.base_ability as i16 - active_pkmn.ability as i16,
1145            }));
1146        active_pkmn.ability = active_pkmn.base_ability;
1147    }
1148}
1149
1150pub fn ability_end_of_turn(
1151    state: &mut State,
1152    side_ref: &SideReference,
1153    instructions: &mut StateInstructions,
1154) {
1155    let (attacking_side, defending_side) = state.get_both_sides(side_ref);
1156    let active_pkmn = attacking_side.get_active();
1157    if defending_side.get_active_immutable().ability == Abilities::NEUTRALIZINGGAS {
1158        return;
1159    }
1160    match active_pkmn.ability {
1161        Abilities::HUNGERSWITCH => {
1162            if active_pkmn.id == PokemonName::MORPEKO && !active_pkmn.terastallized {
1163                instructions.instruction_list.push(Instruction::FormeChange(
1164                    FormeChangeInstruction {
1165                        side_ref: *side_ref,
1166                        name_change: PokemonName::MORPEKOHANGRY as i16 - active_pkmn.id as i16,
1167                    },
1168                ));
1169                active_pkmn.id = PokemonName::MORPEKOHANGRY;
1170            } else if active_pkmn.id == PokemonName::MORPEKOHANGRY && !active_pkmn.terastallized {
1171                instructions.instruction_list.push(Instruction::FormeChange(
1172                    FormeChangeInstruction {
1173                        side_ref: *side_ref,
1174                        name_change: PokemonName::MORPEKO as i16 - active_pkmn.id as i16,
1175                    },
1176                ));
1177                active_pkmn.id = PokemonName::MORPEKO;
1178            }
1179        }
1180        Abilities::SHIELDSDOWN => {
1181            if active_pkmn.hp <= active_pkmn.maxhp / 2
1182                && active_pkmn.id == PokemonName::MINIORMETEOR
1183            {
1184                instructions.instruction_list.push(Instruction::FormeChange(
1185                    FormeChangeInstruction {
1186                        side_ref: *side_ref,
1187                        name_change: PokemonName::MINIOR as i16 - active_pkmn.id as i16,
1188                    },
1189                ));
1190                active_pkmn.id = PokemonName::MINIOR;
1191                active_pkmn.recalculate_stats(side_ref, instructions);
1192            }
1193            if active_pkmn.hp > active_pkmn.maxhp / 2 && active_pkmn.id != PokemonName::MINIORMETEOR
1194            {
1195                instructions.instruction_list.push(Instruction::FormeChange(
1196                    FormeChangeInstruction {
1197                        side_ref: *side_ref,
1198                        name_change: PokemonName::MINIORMETEOR as i16 - active_pkmn.id as i16,
1199                    },
1200                ));
1201                active_pkmn.id = PokemonName::MINIORMETEOR;
1202                active_pkmn.recalculate_stats(side_ref, instructions);
1203            }
1204        }
1205        Abilities::SCHOOLING => {
1206            if active_pkmn.hp <= active_pkmn.maxhp / 4
1207                && active_pkmn.id == PokemonName::WISHIWASHISCHOOL
1208            {
1209                instructions.instruction_list.push(Instruction::FormeChange(
1210                    FormeChangeInstruction {
1211                        side_ref: *side_ref,
1212                        name_change: PokemonName::WISHIWASHI as i16 - active_pkmn.id as i16,
1213                    },
1214                ));
1215                active_pkmn.id = PokemonName::WISHIWASHI;
1216                active_pkmn.recalculate_stats(side_ref, instructions);
1217            }
1218            if active_pkmn.hp > active_pkmn.maxhp / 4 && active_pkmn.id == PokemonName::WISHIWASHI {
1219                instructions.instruction_list.push(Instruction::FormeChange(
1220                    FormeChangeInstruction {
1221                        side_ref: *side_ref,
1222                        name_change: PokemonName::WISHIWASHISCHOOL as i16 - active_pkmn.id as i16,
1223                    },
1224                ));
1225                active_pkmn.id = PokemonName::WISHIWASHISCHOOL;
1226                active_pkmn.recalculate_stats(side_ref, instructions);
1227            }
1228        }
1229        Abilities::BADDREAMS => {
1230            let defender = defending_side.get_active();
1231            if defender.status == PokemonStatus::SLEEP {
1232                let damage_dealt = cmp::min(defender.maxhp / 8, defender.hp);
1233                instructions
1234                    .instruction_list
1235                    .push(Instruction::Damage(DamageInstruction {
1236                        side_ref: side_ref.get_other_side(),
1237                        damage_amount: damage_dealt,
1238                    }));
1239                defender.hp -= damage_dealt;
1240            }
1241        }
1242        Abilities::SOLARPOWER => {
1243            if state.weather_is_active(&Weather::HARSHSUN) || state.weather_is_active(&Weather::SUN)
1244            {
1245                let active_pkmn = state.get_side(side_ref).get_active();
1246                let damage_dealt =
1247                    cmp::min(active_pkmn.maxhp / 8, active_pkmn.maxhp - active_pkmn.hp);
1248                if damage_dealt > 0 {
1249                    instructions
1250                        .instruction_list
1251                        .push(Instruction::Damage(DamageInstruction {
1252                            side_ref: *side_ref,
1253                            damage_amount: damage_dealt,
1254                        }));
1255                    active_pkmn.hp -= damage_dealt;
1256                }
1257            }
1258        }
1259        Abilities::ICEBODY => {
1260            if state.weather_is_active(&Weather::HAIL) {
1261                let active_pkmn = state.get_side(side_ref).get_active();
1262                let health_recovered =
1263                    cmp::min(active_pkmn.maxhp / 16, active_pkmn.maxhp - active_pkmn.hp);
1264                if health_recovered > 0 {
1265                    instructions
1266                        .instruction_list
1267                        .push(Instruction::Heal(HealInstruction {
1268                            side_ref: *side_ref,
1269                            heal_amount: health_recovered,
1270                        }));
1271                    active_pkmn.hp += health_recovered;
1272                }
1273            }
1274        }
1275        Abilities::POISONHEAL => {
1276            if active_pkmn.hp < active_pkmn.maxhp
1277                && (active_pkmn.status == PokemonStatus::POISON
1278                    || active_pkmn.status == PokemonStatus::TOXIC)
1279            {
1280                let heal_amount =
1281                    cmp::min(active_pkmn.maxhp / 8, active_pkmn.maxhp - active_pkmn.hp);
1282                let ins = Instruction::Heal(HealInstruction {
1283                    side_ref: side_ref.clone(),
1284                    heal_amount: heal_amount,
1285                });
1286                active_pkmn.hp += heal_amount;
1287                instructions.instruction_list.push(ins);
1288            }
1289        }
1290        Abilities::SPEEDBOOST => {
1291            if attacking_side.speed_boost < 6 {
1292                let ins = Instruction::Boost(BoostInstruction {
1293                    side_ref: side_ref.clone(),
1294                    stat: PokemonBoostableStat::Speed,
1295                    amount: 1,
1296                });
1297                attacking_side.speed_boost += 1;
1298                instructions.instruction_list.push(ins);
1299            }
1300        }
1301        Abilities::RAINDISH => {
1302            if state.weather_is_active(&Weather::RAIN)
1303                || state.weather_is_active(&Weather::HEAVYRAIN)
1304            {
1305                let active_pkmn = state.get_side(side_ref).get_active();
1306                let health_recovered =
1307                    cmp::min(active_pkmn.maxhp / 16, active_pkmn.maxhp - active_pkmn.hp);
1308                if health_recovered > 0 {
1309                    instructions
1310                        .instruction_list
1311                        .push(Instruction::Heal(HealInstruction {
1312                            side_ref: *side_ref,
1313                            heal_amount: health_recovered,
1314                        }));
1315                    active_pkmn.hp += health_recovered;
1316                }
1317            }
1318        }
1319        Abilities::DRYSKIN => {
1320            if state.weather_is_active(&Weather::RAIN) {
1321                let active_pkmn = state.get_side(side_ref).get_active();
1322                if active_pkmn.hp < active_pkmn.maxhp {
1323                    let heal_amount =
1324                        cmp::min(active_pkmn.maxhp / 8, active_pkmn.maxhp - active_pkmn.hp);
1325                    let ins = Instruction::Heal(HealInstruction {
1326                        side_ref: side_ref.clone(),
1327                        heal_amount: heal_amount,
1328                    });
1329                    active_pkmn.hp += heal_amount;
1330                    instructions.instruction_list.push(ins);
1331                }
1332            }
1333        }
1334        Abilities::HYDRATION => {
1335            if active_pkmn.status != PokemonStatus::NONE
1336                && (state.weather_is_active(&Weather::RAIN)
1337                    || state.weather_is_active(&Weather::HEAVYRAIN))
1338            {
1339                let attacking_side = state.get_side(side_ref);
1340                let active_index = attacking_side.active_index;
1341                let active_pkmn = attacking_side.get_active();
1342
1343                add_remove_status_instructions(
1344                    instructions,
1345                    active_index,
1346                    *side_ref,
1347                    attacking_side,
1348                );
1349            }
1350        }
1351        // Shed skin only has a 1/3 chance of activating at the end of the turn
1352        // but I'm not going to branch on that here
1353        Abilities::SHEDSKIN => {
1354            if active_pkmn.status != PokemonStatus::NONE {
1355                let attacking_side = state.get_side(side_ref);
1356                let active_index = attacking_side.active_index;
1357                let active_pkmn = attacking_side.get_active();
1358
1359                add_remove_status_instructions(
1360                    instructions,
1361                    active_index,
1362                    *side_ref,
1363                    attacking_side,
1364                );
1365            }
1366        }
1367        _ => {}
1368    }
1369}
1370
1371pub fn ability_on_switch_in(
1372    state: &mut State,
1373    side_ref: &SideReference,
1374    instructions: &mut StateInstructions,
1375) {
1376    let (attacking_side, defending_side) = state.get_both_sides(side_ref);
1377    let active_pkmn = attacking_side.get_active();
1378    let defending_pkmn = defending_side.get_active_immutable();
1379    if defending_pkmn.ability == Abilities::NEUTRALIZINGGAS {
1380        return;
1381    }
1382
1383    // trace copying an ability needs to happen before the ability check to activate on switch-in
1384    // e.g. tracing intimidate will activate intimidate
1385    if active_pkmn.ability == Abilities::TRACE && active_pkmn.ability != defending_pkmn.ability {
1386        instructions
1387            .instruction_list
1388            .push(Instruction::ChangeAbility(ChangeAbilityInstruction {
1389                side_ref: *side_ref,
1390                ability_change: defending_pkmn.ability as i16 - active_pkmn.ability as i16,
1391            }));
1392        active_pkmn.ability = defending_pkmn.ability;
1393    }
1394
1395    match active_pkmn.ability {
1396        Abilities::ICEFACE => {
1397            if active_pkmn.id == PokemonName::EISCUENOICE && state.weather_is_active(&Weather::HAIL)
1398                || state.weather_is_active(&Weather::SNOW)
1399            {
1400                let active_pkmn = state.get_side(side_ref).get_active();
1401                instructions.instruction_list.push(Instruction::FormeChange(
1402                    FormeChangeInstruction {
1403                        side_ref: *side_ref,
1404                        name_change: PokemonName::EISCUE as i16 - active_pkmn.id as i16,
1405                    },
1406                ));
1407                active_pkmn.id = PokemonName::EISCUE;
1408                active_pkmn.recalculate_stats(side_ref, instructions);
1409            }
1410        }
1411        Abilities::PROTOSYNTHESIS => {
1412            let sun_is_active = state.weather_is_active(&Weather::SUN);
1413            let attacking_side = state.get_side(side_ref);
1414            let volatile = protosynthesis_volatile_from_side(&attacking_side);
1415            protosynthesus_or_quarkdrive_on_switch_in(
1416                sun_is_active,
1417                volatile,
1418                instructions,
1419                attacking_side,
1420                side_ref,
1421            );
1422        }
1423        Abilities::QUARKDRIVE => {
1424            let electric_terrain_is_active = state.terrain_is_active(&Terrain::ELECTRICTERRAIN);
1425            let attacking_side = state.get_side(side_ref);
1426            let volatile = quarkdrive_volatile_from_side(&attacking_side);
1427            protosynthesus_or_quarkdrive_on_switch_in(
1428                electric_terrain_is_active,
1429                volatile,
1430                instructions,
1431                attacking_side,
1432                side_ref,
1433            );
1434        }
1435        Abilities::EMBODYASPECTTEAL => {
1436            if let Some(boost_instruction) = get_boost_instruction(
1437                &attacking_side,
1438                &PokemonBoostableStat::Speed,
1439                &1,
1440                side_ref,
1441                side_ref,
1442            ) {
1443                state.apply_one_instruction(&boost_instruction);
1444                instructions.instruction_list.push(boost_instruction);
1445            }
1446        }
1447        Abilities::EMBODYASPECTWELLSPRING => {
1448            if let Some(boost_instruction) = get_boost_instruction(
1449                &attacking_side,
1450                &PokemonBoostableStat::SpecialDefense,
1451                &1,
1452                side_ref,
1453                side_ref,
1454            ) {
1455                state.apply_one_instruction(&boost_instruction);
1456                instructions.instruction_list.push(boost_instruction);
1457            }
1458        }
1459        Abilities::EMBODYASPECTCORNERSTONE => {
1460            if let Some(boost_instruction) = get_boost_instruction(
1461                &attacking_side,
1462                &PokemonBoostableStat::Defense,
1463                &1,
1464                side_ref,
1465                side_ref,
1466            ) {
1467                state.apply_one_instruction(&boost_instruction);
1468                instructions.instruction_list.push(boost_instruction);
1469            }
1470        }
1471        Abilities::EMBODYASPECTHEARTHFLAME => {
1472            if let Some(boost_instruction) = get_boost_instruction(
1473                &attacking_side,
1474                &PokemonBoostableStat::Attack,
1475                &1,
1476                side_ref,
1477                side_ref,
1478            ) {
1479                state.apply_one_instruction(&boost_instruction);
1480                instructions.instruction_list.push(boost_instruction);
1481            }
1482        }
1483        Abilities::INTREPIDSWORD => {
1484            // no need to check for boost at +6 because we are switching in
1485            attacking_side.attack_boost += 1;
1486            instructions
1487                .instruction_list
1488                .push(Instruction::Boost(BoostInstruction {
1489                    side_ref: *side_ref,
1490                    stat: PokemonBoostableStat::Attack,
1491                    amount: 1,
1492                }));
1493        }
1494        Abilities::SLOWSTART => {
1495            instructions
1496                .instruction_list
1497                .push(Instruction::ApplyVolatileStatus(
1498                    ApplyVolatileStatusInstruction {
1499                        side_ref: *side_ref,
1500                        volatile_status: PokemonVolatileStatus::SLOWSTART,
1501                    },
1502                ));
1503            instructions
1504                .instruction_list
1505                .push(Instruction::ChangeVolatileStatusDuration(
1506                    ChangeVolatileStatusDurationInstruction {
1507                        side_ref: *side_ref,
1508                        volatile_status: PokemonVolatileStatus::SLOWSTART,
1509                        amount: 6 - attacking_side.volatile_status_durations.slowstart,
1510                    },
1511                ));
1512            attacking_side
1513                .volatile_statuses
1514                .insert(PokemonVolatileStatus::SLOWSTART);
1515            attacking_side.volatile_status_durations.slowstart = 6;
1516        }
1517        Abilities::DROUGHT | Abilities::ORICHALCUMPULSE => {
1518            if state.weather.weather_type != Weather::SUN {
1519                instructions
1520                    .instruction_list
1521                    .push(Instruction::ChangeWeather(ChangeWeather {
1522                        new_weather: Weather::SUN,
1523                        new_weather_turns_remaining: WEATHER_ABILITY_TURNS,
1524                        previous_weather: state.weather.weather_type,
1525                        previous_weather_turns_remaining: state.weather.turns_remaining,
1526                    }));
1527                state.weather.weather_type = Weather::SUN;
1528                state.weather.turns_remaining = WEATHER_ABILITY_TURNS;
1529            }
1530        }
1531        Abilities::DESOLATELAND => {
1532            if state.weather.weather_type != Weather::HARSHSUN {
1533                instructions
1534                    .instruction_list
1535                    .push(Instruction::ChangeWeather(ChangeWeather {
1536                        new_weather: Weather::HARSHSUN,
1537                        new_weather_turns_remaining: -1,
1538                        previous_weather: state.weather.weather_type,
1539                        previous_weather_turns_remaining: state.weather.turns_remaining,
1540                    }));
1541                state.weather.weather_type = Weather::HARSHSUN;
1542                state.weather.turns_remaining = -1;
1543            }
1544        }
1545        Abilities::MISTYSURGE => {
1546            if state.terrain.terrain_type != Terrain::MISTYTERRAIN {
1547                instructions
1548                    .instruction_list
1549                    .push(Instruction::ChangeTerrain(ChangeTerrain {
1550                        new_terrain: Terrain::MISTYTERRAIN,
1551                        new_terrain_turns_remaining: 5,
1552                        previous_terrain: state.terrain.terrain_type,
1553                        previous_terrain_turns_remaining: state.terrain.turns_remaining,
1554                    }));
1555                state.terrain.terrain_type = Terrain::MISTYTERRAIN;
1556                state.terrain.turns_remaining = 5;
1557            }
1558        }
1559        Abilities::SANDSTREAM => {
1560            if state.weather.weather_type != Weather::SAND {
1561                instructions
1562                    .instruction_list
1563                    .push(Instruction::ChangeWeather(ChangeWeather {
1564                        new_weather: Weather::SAND,
1565                        new_weather_turns_remaining: WEATHER_ABILITY_TURNS,
1566                        previous_weather: state.weather.weather_type,
1567                        previous_weather_turns_remaining: state.weather.turns_remaining,
1568                    }));
1569                state.weather.weather_type = Weather::SAND;
1570                state.weather.turns_remaining = WEATHER_ABILITY_TURNS;
1571            }
1572        }
1573        Abilities::INTIMIDATE => {
1574            if let Some(boost_instruction) = get_boost_instruction(
1575                &defending_side,
1576                &PokemonBoostableStat::Attack,
1577                &-1,
1578                side_ref,
1579                &side_ref.get_other_side(),
1580            ) {
1581                let defender = defending_side.get_active_immutable();
1582                let mut adrenaline_orb_item_instruction = None;
1583                let mut adrenaline_orb_boost_instruction = None;
1584                if defender.item == Items::ADRENALINEORB {
1585                    if let Some(boost_ins) = get_boost_instruction(
1586                        &defending_side,
1587                        &PokemonBoostableStat::Speed,
1588                        &1,
1589                        &side_ref.get_other_side(),
1590                        &side_ref.get_other_side(),
1591                    ) {
1592                        adrenaline_orb_boost_instruction = Some(boost_ins);
1593                        adrenaline_orb_item_instruction =
1594                            Some(Instruction::ChangeItem(ChangeItemInstruction {
1595                                side_ref: side_ref.get_other_side(),
1596                                current_item: Items::ADRENALINEORB,
1597                                new_item: Items::NONE,
1598                            }));
1599                    }
1600                }
1601                match defender.ability {
1602                    Abilities::OWNTEMPO
1603                    | Abilities::OBLIVIOUS
1604                    | Abilities::INNERFOCUS
1605                    | Abilities::SCRAPPY => {}
1606                    _ => {
1607                        state.apply_one_instruction(&boost_instruction);
1608                        instructions.instruction_list.push(boost_instruction);
1609                    }
1610                }
1611                if let Some(ins) = adrenaline_orb_boost_instruction {
1612                    state.apply_one_instruction(&ins);
1613                    instructions.instruction_list.push(ins);
1614                    if let Some(ins) = adrenaline_orb_item_instruction {
1615                        state.apply_one_instruction(&ins);
1616                        instructions.instruction_list.push(ins);
1617                    }
1618                }
1619            }
1620        }
1621        Abilities::DAUNTLESSSHIELD => {
1622            // no need to check for boost at +6 because we are switching in
1623            attacking_side.defense_boost += 1;
1624            instructions
1625                .instruction_list
1626                .push(Instruction::Boost(BoostInstruction {
1627                    side_ref: *side_ref,
1628                    stat: PokemonBoostableStat::Defense,
1629                    amount: 1,
1630                }));
1631        }
1632        Abilities::GRASSYSURGE => {
1633            if state.terrain.terrain_type != Terrain::GRASSYTERRAIN {
1634                instructions
1635                    .instruction_list
1636                    .push(Instruction::ChangeTerrain(ChangeTerrain {
1637                        new_terrain: Terrain::GRASSYTERRAIN,
1638                        new_terrain_turns_remaining: 5,
1639                        previous_terrain: state.terrain.terrain_type,
1640                        previous_terrain_turns_remaining: state.terrain.turns_remaining,
1641                    }));
1642                state.terrain.terrain_type = Terrain::GRASSYTERRAIN;
1643                state.terrain.turns_remaining = 5;
1644            }
1645        }
1646        Abilities::ELECTRICSURGE | Abilities::HADRONENGINE => {
1647            if state.terrain.terrain_type != Terrain::ELECTRICTERRAIN {
1648                instructions
1649                    .instruction_list
1650                    .push(Instruction::ChangeTerrain(ChangeTerrain {
1651                        new_terrain: Terrain::ELECTRICTERRAIN,
1652                        new_terrain_turns_remaining: 5,
1653                        previous_terrain: state.terrain.terrain_type,
1654                        previous_terrain_turns_remaining: state.terrain.turns_remaining,
1655                    }));
1656                state.terrain.terrain_type = Terrain::ELECTRICTERRAIN;
1657                state.terrain.turns_remaining = 5;
1658            }
1659        }
1660        Abilities::DOWNLOAD => {
1661            if defending_side.calculate_boosted_stat(PokemonBoostableStat::Defense)
1662                < defending_side.calculate_boosted_stat(PokemonBoostableStat::SpecialDefense)
1663            {
1664                if let Some(boost_instruction) = get_boost_instruction(
1665                    &attacking_side,
1666                    &PokemonBoostableStat::Attack,
1667                    &1,
1668                    side_ref,
1669                    side_ref,
1670                ) {
1671                    state.apply_one_instruction(&boost_instruction);
1672                    instructions.instruction_list.push(boost_instruction);
1673                }
1674            } else {
1675                if let Some(boost_instruction) = get_boost_instruction(
1676                    &attacking_side,
1677                    &PokemonBoostableStat::SpecialAttack,
1678                    &1,
1679                    side_ref,
1680                    side_ref,
1681                ) {
1682                    state.apply_one_instruction(&boost_instruction);
1683                    instructions.instruction_list.push(boost_instruction);
1684                }
1685            }
1686        }
1687        Abilities::PRIMORDIALSEA => {
1688            if state.weather.weather_type != Weather::HEAVYRAIN {
1689                instructions
1690                    .instruction_list
1691                    .push(Instruction::ChangeWeather(ChangeWeather {
1692                        new_weather: Weather::HEAVYRAIN,
1693                        new_weather_turns_remaining: -1,
1694                        previous_weather: state.weather.weather_type,
1695                        previous_weather_turns_remaining: state.weather.turns_remaining,
1696                    }));
1697                state.weather.weather_type = Weather::HEAVYRAIN;
1698                state.weather.turns_remaining = -1;
1699            }
1700        }
1701        Abilities::SCREENCLEANER => {
1702            if state.side_one.side_conditions.reflect > 0 {
1703                instructions
1704                    .instruction_list
1705                    .push(Instruction::ChangeSideCondition(
1706                        ChangeSideConditionInstruction {
1707                            side_ref: SideReference::SideOne,
1708                            side_condition: PokemonSideCondition::Reflect,
1709                            amount: -1 * state.side_one.side_conditions.reflect,
1710                        },
1711                    ));
1712                state.side_one.side_conditions.reflect = 0;
1713            }
1714            if state.side_two.side_conditions.reflect > 0 {
1715                instructions
1716                    .instruction_list
1717                    .push(Instruction::ChangeSideCondition(
1718                        ChangeSideConditionInstruction {
1719                            side_ref: SideReference::SideTwo,
1720                            side_condition: PokemonSideCondition::Reflect,
1721                            amount: -1 * state.side_two.side_conditions.reflect,
1722                        },
1723                    ));
1724                state.side_two.side_conditions.reflect = 0;
1725            }
1726            if state.side_one.side_conditions.light_screen > 0 {
1727                instructions
1728                    .instruction_list
1729                    .push(Instruction::ChangeSideCondition(
1730                        ChangeSideConditionInstruction {
1731                            side_ref: SideReference::SideOne,
1732                            side_condition: PokemonSideCondition::LightScreen,
1733                            amount: -1 * state.side_one.side_conditions.light_screen,
1734                        },
1735                    ));
1736                state.side_one.side_conditions.light_screen = 0;
1737            }
1738            if state.side_two.side_conditions.light_screen > 0 {
1739                instructions
1740                    .instruction_list
1741                    .push(Instruction::ChangeSideCondition(
1742                        ChangeSideConditionInstruction {
1743                            side_ref: SideReference::SideTwo,
1744                            side_condition: PokemonSideCondition::LightScreen,
1745                            amount: -1 * state.side_two.side_conditions.light_screen,
1746                        },
1747                    ));
1748                state.side_two.side_conditions.light_screen = 0;
1749            }
1750            if state.side_one.side_conditions.aurora_veil > 0 {
1751                instructions
1752                    .instruction_list
1753                    .push(Instruction::ChangeSideCondition(
1754                        ChangeSideConditionInstruction {
1755                            side_ref: SideReference::SideOne,
1756                            side_condition: PokemonSideCondition::AuroraVeil,
1757                            amount: -1 * state.side_one.side_conditions.aurora_veil,
1758                        },
1759                    ));
1760                state.side_one.side_conditions.aurora_veil = 0;
1761            }
1762            if state.side_two.side_conditions.aurora_veil > 0 {
1763                instructions
1764                    .instruction_list
1765                    .push(Instruction::ChangeSideCondition(
1766                        ChangeSideConditionInstruction {
1767                            side_ref: SideReference::SideTwo,
1768                            side_condition: PokemonSideCondition::AuroraVeil,
1769                            amount: -1 * state.side_two.side_conditions.aurora_veil,
1770                        },
1771                    ));
1772                state.side_two.side_conditions.aurora_veil = 0;
1773            }
1774        }
1775        Abilities::SNOWWARNING => {
1776            #[cfg(feature = "gen9")]
1777            let weather_type = Weather::SNOW;
1778            #[cfg(not(feature = "gen9"))]
1779            let weather_type = Weather::HAIL;
1780
1781            if state.weather.weather_type != weather_type {
1782                instructions
1783                    .instruction_list
1784                    .push(Instruction::ChangeWeather(ChangeWeather {
1785                        new_weather: weather_type,
1786                        new_weather_turns_remaining: WEATHER_ABILITY_TURNS,
1787                        previous_weather: state.weather.weather_type,
1788                        previous_weather_turns_remaining: state.weather.turns_remaining,
1789                    }));
1790                state.weather.weather_type = weather_type;
1791                state.weather.turns_remaining = WEATHER_ABILITY_TURNS;
1792            }
1793        }
1794        Abilities::PSYCHICSURGE => {
1795            if state.terrain.terrain_type != Terrain::PSYCHICTERRAIN {
1796                instructions
1797                    .instruction_list
1798                    .push(Instruction::ChangeTerrain(ChangeTerrain {
1799                        new_terrain: Terrain::PSYCHICTERRAIN,
1800                        new_terrain_turns_remaining: 5,
1801                        previous_terrain: state.terrain.terrain_type,
1802                        previous_terrain_turns_remaining: state.terrain.turns_remaining,
1803                    }));
1804                state.terrain.terrain_type = Terrain::PSYCHICTERRAIN;
1805                state.terrain.turns_remaining = 5;
1806            }
1807        }
1808        Abilities::DRIZZLE => {
1809            if state.weather.weather_type != Weather::RAIN {
1810                instructions
1811                    .instruction_list
1812                    .push(Instruction::ChangeWeather(ChangeWeather {
1813                        new_weather: Weather::RAIN,
1814                        new_weather_turns_remaining: WEATHER_ABILITY_TURNS,
1815                        previous_weather: state.weather.weather_type,
1816                        previous_weather_turns_remaining: state.weather.turns_remaining,
1817                    }));
1818                state.weather.weather_type = Weather::RAIN;
1819                state.weather.turns_remaining = WEATHER_ABILITY_TURNS;
1820            }
1821        }
1822        _ => {}
1823    }
1824}
1825
1826pub fn ability_modify_attack_being_used(
1827    state: &State,
1828    attacker_choice: &mut Choice,
1829    defender_choice: &Choice,
1830    attacking_side_ref: &SideReference,
1831) {
1832    let (attacking_side, defending_side) = state.get_both_sides_immutable(attacking_side_ref);
1833    let attacking_pkmn = attacking_side.get_active_immutable();
1834    if defending_side.get_active_immutable().ability == Abilities::NEUTRALIZINGGAS {
1835        return;
1836    }
1837    match attacking_pkmn.ability {
1838        #[cfg(any(feature = "gen9", feature = "gen8", feature = "gen7"))]
1839        Abilities::PRANKSTER => {
1840            if attacker_choice.category == MoveCategory::Status
1841                && defending_side
1842                    .get_active_immutable()
1843                    .has_type(&PokemonType::DARK)
1844            {
1845                attacker_choice.remove_all_effects();
1846            }
1847        }
1848        Abilities::BEADSOFRUIN => {
1849            if attacker_choice.category == MoveCategory::Special {
1850                attacker_choice.base_power *= 1.33;
1851            }
1852        }
1853        Abilities::SWORDOFRUIN => {
1854            if attacker_choice.category == MoveCategory::Physical {
1855                attacker_choice.base_power *= 1.33;
1856            }
1857        }
1858        Abilities::SHARPNESS => {
1859            if attacker_choice.flags.slicing {
1860                attacker_choice.base_power *= 1.5;
1861            }
1862        }
1863        Abilities::WATERBUBBLE => {
1864            if attacker_choice.move_type == PokemonType::WATER {
1865                attacker_choice.base_power *= 2.0;
1866            }
1867        }
1868        Abilities::DRAGONSMAW => {
1869            if attacker_choice.move_type == PokemonType::DRAGON {
1870                attacker_choice.base_power *= 1.5;
1871            }
1872        }
1873        Abilities::HADRONENGINE => {
1874            if attacker_choice.category == MoveCategory::Special
1875                && state.terrain.terrain_type == Terrain::ELECTRICTERRAIN
1876            {
1877                attacker_choice.base_power *= 1.33;
1878            }
1879        }
1880        Abilities::ORICHALCUMPULSE => {
1881            if attacker_choice.category == MoveCategory::Physical
1882                && state.weather.weather_type == Weather::SUN
1883            {
1884                attacker_choice.base_power *= 1.33;
1885            }
1886        }
1887        Abilities::GALVANIZE => {
1888            if attacker_choice.move_type == PokemonType::NORMAL {
1889                attacker_choice.move_type = PokemonType::ELECTRIC;
1890                attacker_choice.base_power *= 1.2;
1891            }
1892        }
1893        #[cfg(any(feature = "gen9", feature = "gen8", feature = "gen7"))]
1894        Abilities::AERILATE => {
1895            if attacker_choice.move_type == PokemonType::NORMAL {
1896                attacker_choice.move_type = PokemonType::FLYING;
1897                attacker_choice.base_power *= 1.2;
1898            }
1899        }
1900        #[cfg(any(feature = "gen6", feature = "gen5", feature = "gen4"))]
1901        Abilities::AERILATE => {
1902            if attacker_choice.move_type == PokemonType::NORMAL {
1903                attacker_choice.move_type = PokemonType::FLYING;
1904                attacker_choice.base_power *= 1.3;
1905            }
1906        }
1907        Abilities::NEUROFORCE => {
1908            if type_effectiveness_modifier(
1909                &attacker_choice.move_type,
1910                &defending_side.get_active_immutable(),
1911            ) > 1.0
1912            {
1913                attacker_choice.base_power *= 1.25;
1914            }
1915        }
1916        Abilities::STAKEOUT => {
1917            if defender_choice.category == MoveCategory::Switch {
1918                attacker_choice.base_power *= 2.0;
1919            }
1920        }
1921        Abilities::TECHNICIAN => {
1922            if attacker_choice.base_power <= 60.0 {
1923                attacker_choice.base_power *= 1.5;
1924            }
1925        }
1926        #[cfg(any(feature = "gen9", feature = "gen8", feature = "gen7"))]
1927        Abilities::REFRIGERATE => {
1928            if attacker_choice.move_type == PokemonType::NORMAL {
1929                attacker_choice.move_type = PokemonType::ICE;
1930                attacker_choice.base_power *= 1.2;
1931            }
1932        }
1933        #[cfg(any(feature = "gen6", feature = "gen5", feature = "gen4"))]
1934        Abilities::REFRIGERATE => {
1935            if attacker_choice.move_type == PokemonType::NORMAL {
1936                attacker_choice.move_type = PokemonType::ICE;
1937                attacker_choice.base_power *= 1.3;
1938            }
1939        }
1940        Abilities::SUPREMEOVERLORD => {
1941            let mut boost_amount = 1.0;
1942            boost_amount += 0.1 * attacking_side.num_fainted_pkmn() as f32;
1943            attacker_choice.base_power *= boost_amount;
1944        }
1945        Abilities::ADAPTABILITY => {
1946            if attacking_pkmn.has_type(&attacker_choice.move_type) {
1947                if attacking_pkmn.terastallized
1948                    && attacker_choice.move_type == attacking_pkmn.tera_type
1949                    && (attacking_pkmn.types.0 == attacker_choice.move_type
1950                        || attacking_pkmn.types.1 == attacker_choice.move_type)
1951                {
1952                    attacker_choice.base_power *= 2.25 / 2.0;
1953                } else {
1954                    attacker_choice.base_power *= 2.0 / 1.5;
1955                }
1956            }
1957        }
1958        Abilities::LONGREACH => {
1959            attacker_choice.flags.contact = false;
1960        }
1961        Abilities::PUREPOWER => {
1962            if attacker_choice.category == MoveCategory::Physical {
1963                attacker_choice.base_power *= 2.0;
1964            }
1965        }
1966        Abilities::TINTEDLENS => {
1967            if type_effectiveness_modifier(
1968                &attacker_choice.move_type,
1969                &defending_side.get_active_immutable(),
1970            ) < 1.0
1971            {
1972                attacker_choice.base_power *= 2.0;
1973            }
1974        }
1975        Abilities::FLAREBOOST => {
1976            if attacking_pkmn.status == PokemonStatus::BURN {
1977                attacker_choice.base_power *= 1.5;
1978            }
1979        }
1980        Abilities::LIQUIDVOICE => {
1981            if attacker_choice.flags.sound {
1982                attacker_choice.move_type = PokemonType::WATER;
1983            }
1984        }
1985        Abilities::NOGUARD => attacker_choice.accuracy = 100.0,
1986        Abilities::TORRENT => {
1987            if attacker_choice.move_type == PokemonType::WATER
1988                && attacking_pkmn.hp <= attacking_pkmn.maxhp / 3
1989            {
1990                attacker_choice.base_power *= 1.5;
1991            }
1992        }
1993        Abilities::SERENEGRACE => {
1994            if let Some(secondaries) = &mut attacker_choice.secondaries {
1995                for secondary in secondaries.iter_mut() {
1996                    secondary.chance *= 2.0;
1997                }
1998            }
1999        }
2000        Abilities::TOUGHCLAWS => {
2001            if attacker_choice.flags.contact {
2002                attacker_choice.base_power *= 1.3;
2003            }
2004        }
2005        Abilities::RECKLESS => {
2006            if attacker_choice.crash.is_some() || attacker_choice.recoil.is_some() {
2007                attacker_choice.base_power *= 1.2;
2008            }
2009        }
2010        Abilities::HUGEPOWER => {
2011            if attacker_choice.category == MoveCategory::Physical {
2012                attacker_choice.base_power *= 2.0;
2013            }
2014        }
2015        Abilities::SOLARPOWER => {
2016            if state.weather_is_active(&Weather::SUN) {
2017                attacker_choice.base_power *= 1.5;
2018            }
2019        }
2020        Abilities::FAIRYAURA => {
2021            if attacker_choice.move_type == PokemonType::FAIRY
2022                && defending_side.get_active_immutable().ability != Abilities::AURABREAK
2023            {
2024                attacker_choice.base_power *= 1.33;
2025            }
2026        }
2027        Abilities::NORMALIZE => {
2028            attacker_choice.move_type = PokemonType::NORMAL;
2029        }
2030        Abilities::DARKAURA => {
2031            if attacker_choice.move_type == PokemonType::DARK
2032                && defending_side.get_active_immutable().ability != Abilities::AURABREAK
2033            {
2034                attacker_choice.base_power *= 1.33;
2035            }
2036        }
2037        Abilities::VICTORYSTAR => {
2038            attacker_choice.accuracy *= 1.1;
2039        }
2040        Abilities::COMPOUNDEYES => {
2041            attacker_choice.accuracy *= 1.3;
2042        }
2043        Abilities::STEELWORKER | Abilities::STEELYSPIRIT => {
2044            if attacker_choice.move_type == PokemonType::STEEL {
2045                attacker_choice.base_power *= 1.5;
2046            }
2047        }
2048        #[cfg(any(
2049            feature = "gen8",
2050            feature = "gen7",
2051            feature = "gen6",
2052            feature = "gen5",
2053            feature = "gen4"
2054        ))]
2055        Abilities::TRANSISTOR => {
2056            if attacker_choice.move_type == PokemonType::ELECTRIC {
2057                attacker_choice.base_power *= 1.5;
2058            }
2059        }
2060        #[cfg(any(feature = "gen9"))]
2061        Abilities::TRANSISTOR => {
2062            if attacker_choice.move_type == PokemonType::ELECTRIC {
2063                attacker_choice.base_power *= 1.3;
2064            }
2065        }
2066        Abilities::STENCH => {
2067            let mut already_flinches = false;
2068            if let Some(secondaries) = &mut attacker_choice.secondaries {
2069                for secondary in secondaries.iter() {
2070                    if secondary.effect == Effect::VolatileStatus(PokemonVolatileStatus::FLINCH) {
2071                        already_flinches = true;
2072                    }
2073                }
2074            }
2075            if !already_flinches {
2076                attacker_choice.add_or_create_secondaries(Secondary {
2077                    chance: 10.0,
2078                    target: MoveTarget::Opponent,
2079                    effect: Effect::VolatileStatus(PokemonVolatileStatus::FLINCH),
2080                })
2081            }
2082        }
2083        Abilities::SWARM => {
2084            if attacker_choice.move_type == PokemonType::BUG
2085                && attacking_pkmn.hp <= attacking_pkmn.maxhp / 3
2086            {
2087                attacker_choice.base_power *= 1.5;
2088            }
2089        }
2090        Abilities::GORILLATACTICS => {
2091            if attacker_choice.category == MoveCategory::Physical {
2092                attacker_choice.base_power *= 1.5;
2093            }
2094        }
2095        Abilities::BLAZE => {
2096            if attacker_choice.move_type == PokemonType::FIRE
2097                && attacking_pkmn.hp <= attacking_pkmn.maxhp / 3
2098            {
2099                attacker_choice.base_power *= 1.5;
2100            }
2101        }
2102        Abilities::OVERGROW => {
2103            if attacker_choice.move_type == PokemonType::GRASS
2104                && attacking_pkmn.hp <= attacking_pkmn.maxhp / 3
2105            {
2106                attacker_choice.base_power *= 1.5;
2107            }
2108        }
2109        Abilities::ANALYTIC => {
2110            if !attacker_choice.first_move {
2111                attacker_choice.base_power *= 1.3;
2112            }
2113        }
2114        Abilities::MEGALAUNCHER => {
2115            if attacker_choice.flags.pulse {
2116                attacker_choice.base_power *= 1.5;
2117            };
2118        }
2119        #[cfg(any(feature = "gen9", feature = "gen8", feature = "gen7"))]
2120        Abilities::PIXILATE => {
2121            if attacker_choice.move_type == PokemonType::NORMAL {
2122                attacker_choice.move_type = PokemonType::FAIRY;
2123                attacker_choice.base_power *= 1.2;
2124            }
2125        }
2126        #[cfg(any(feature = "gen6", feature = "gen5", feature = "gen4"))]
2127        Abilities::PIXILATE => {
2128            if attacker_choice.move_type == PokemonType::NORMAL {
2129                attacker_choice.move_type = PokemonType::FAIRY;
2130                attacker_choice.base_power *= 1.3;
2131            }
2132        }
2133        Abilities::DEFEATIST => {
2134            if attacking_pkmn.hp <= attacking_pkmn.maxhp / 2 {
2135                attacker_choice.base_power *= 0.5;
2136            }
2137        }
2138        Abilities::ROCKYPAYLOAD => {
2139            if attacker_choice.move_type == PokemonType::ROCK {
2140                attacker_choice.base_power *= 1.5;
2141            }
2142        }
2143        Abilities::PUNKROCK => {
2144            if attacker_choice.flags.sound {
2145                attacker_choice.base_power *= 1.3;
2146            }
2147        }
2148        Abilities::STRONGJAW => {
2149            if attacker_choice.flags.bite {
2150                attacker_choice.base_power *= 1.5;
2151            }
2152        }
2153        Abilities::BATTERY => {
2154            if attacker_choice.category == MoveCategory::Special {
2155                attacker_choice.base_power *= 1.3;
2156            }
2157        }
2158        Abilities::SHEERFORCE => {
2159            let mut sheer_force_volatile_boosted = false;
2160            if let Some(attacker_volatile_status) = &attacker_choice.volatile_status {
2161                if attacker_volatile_status.volatile_status
2162                    != PokemonVolatileStatus::PARTIALLYTRAPPED
2163                    && attacker_volatile_status.volatile_status != PokemonVolatileStatus::LOCKEDMOVE
2164                    && attacker_volatile_status.volatile_status != PokemonVolatileStatus::SMACKDOWN
2165                {
2166                    sheer_force_volatile_boosted = true;
2167                }
2168            }
2169            if attacker_choice.secondaries.is_some() || sheer_force_volatile_boosted {
2170                attacker_choice.base_power *= 1.3;
2171                attacker_choice.secondaries = None;
2172                attacker_choice.volatile_status = None
2173            }
2174        }
2175        Abilities::IRONFIST => {
2176            if attacker_choice.flags.punch {
2177                attacker_choice.base_power *= 1.2;
2178            }
2179        }
2180        Abilities::UNSEENFIST => {
2181            if attacker_choice.flags.contact {
2182                attacker_choice.flags.protect = false
2183            }
2184        }
2185        Abilities::HUSTLE => {
2186            if attacker_choice.category == MoveCategory::Physical {
2187                attacker_choice.base_power *= 1.5;
2188                attacker_choice.accuracy *= 0.80
2189            }
2190        }
2191        Abilities::POISONTOUCH => {
2192            if attacker_choice.flags.contact {
2193                attacker_choice.add_or_create_secondaries(Secondary {
2194                    chance: 30.0,
2195                    target: MoveTarget::Opponent,
2196                    effect: Effect::Status(PokemonStatus::POISON),
2197                })
2198            }
2199        }
2200        Abilities::TOXICCHAIN => {
2201            if attacker_choice.target == MoveTarget::Opponent {
2202                attacker_choice.add_or_create_secondaries(Secondary {
2203                    chance: 30.0,
2204                    target: MoveTarget::Opponent,
2205                    effect: Effect::Status(PokemonStatus::TOXIC),
2206                })
2207            }
2208        }
2209        Abilities::GUTS => {
2210            if attacking_pkmn.status != PokemonStatus::NONE {
2211                attacker_choice.base_power *= 1.5;
2212
2213                // not the right place to put this, but good enough
2214                if attacking_pkmn.status == PokemonStatus::BURN
2215                    && attacker_choice.category == MoveCategory::Physical
2216                {
2217                    attacker_choice.base_power *= 2.0;
2218                }
2219            }
2220        }
2221        Abilities::SANDFORCE => {
2222            if state.weather_is_active(&Weather::SAND)
2223                && (attacker_choice.move_type == PokemonType::ROCK
2224                    || attacker_choice.move_type == PokemonType::GROUND
2225                    || attacker_choice.move_type == PokemonType::STEEL)
2226            {
2227                attacker_choice.base_power *= 1.3;
2228            }
2229        }
2230        Abilities::TOXICBOOST => {
2231            if attacking_pkmn.status == PokemonStatus::POISON
2232                || attacking_pkmn.status == PokemonStatus::TOXIC
2233            {
2234                attacker_choice.base_power *= 1.5;
2235            }
2236        }
2237        _ => {}
2238    }
2239}
2240
2241pub fn ability_modify_attack_against(
2242    state: &State,
2243    attacker_choice: &mut Choice,
2244    defender_choice: &Choice,
2245    attacking_side_ref: &SideReference,
2246) {
2247    let (attacking_side, defending_side) = state.get_both_sides_immutable(attacking_side_ref);
2248    let attacking_pkmn = attacking_side.get_active_immutable();
2249    let target_pkmn = defending_side.get_active_immutable();
2250    if target_pkmn.ability == Abilities::NEUTRALIZINGGAS
2251        || attacker_choice.target == MoveTarget::User
2252    {
2253        return;
2254    }
2255    if (attacking_pkmn.ability == Abilities::MOLDBREAKER
2256        || attacker_choice.move_id == Choices::MOONGEISTBEAM
2257        || attacker_choice.move_id == Choices::PHOTONGEYSER
2258        || attacker_choice.move_id == Choices::SUNSTEELSTRIKE
2259        || (attacking_pkmn.ability == Abilities::MYCELIUMMIGHT
2260            && attacker_choice.category == MoveCategory::Status)
2261        || attacking_pkmn.ability == Abilities::TERAVOLT
2262        || attacking_pkmn.ability == Abilities::TURBOBLAZE)
2263        && mold_breaker_ignores(&target_pkmn.ability)
2264    {
2265        return;
2266    }
2267
2268    match target_pkmn.ability {
2269        Abilities::TABLETSOFRUIN => {
2270            if attacker_choice.category == MoveCategory::Physical {
2271                attacker_choice.base_power *= 0.75;
2272            }
2273        }
2274        Abilities::VESSELOFRUIN => {
2275            if attacker_choice.category == MoveCategory::Special {
2276                attacker_choice.base_power *= 0.75;
2277            }
2278        }
2279        Abilities::ARMORTAIL => {
2280            if attacker_choice.priority > 0 && attacker_choice.category != MoveCategory::Status {
2281                attacker_choice.remove_all_effects();
2282            }
2283        }
2284        Abilities::SOUNDPROOF => {
2285            if attacker_choice.flags.sound {
2286                attacker_choice.remove_all_effects();
2287                attacker_choice.accuracy = 0.0;
2288            }
2289        }
2290        Abilities::POISONPOINT => {
2291            if attacker_choice.flags.contact {
2292                attacker_choice.add_or_create_secondaries(Secondary {
2293                    chance: 33.0,
2294                    target: MoveTarget::User,
2295                    effect: Effect::Status(PokemonStatus::POISON),
2296                })
2297            }
2298        }
2299        Abilities::BULLETPROOF => {
2300            if attacker_choice.flags.bullet {
2301                attacker_choice.remove_all_effects();
2302                attacker_choice.accuracy = 0.0;
2303            }
2304        }
2305        Abilities::MULTISCALE => {
2306            if target_pkmn.hp == target_pkmn.maxhp {
2307                attacker_choice.base_power /= 2.0;
2308            }
2309        }
2310        Abilities::LIGHTNINGROD => {
2311            if attacker_choice.move_type == PokemonType::ELECTRIC {
2312                attacker_choice.remove_all_effects();
2313                attacker_choice.accuracy = 100.0;
2314                attacker_choice.target = MoveTarget::Opponent;
2315                attacker_choice.boost = Some(Boost {
2316                    boosts: StatBoosts {
2317                        attack: 0,
2318                        defense: 0,
2319                        special_attack: 1,
2320                        special_defense: 0,
2321                        speed: 0,
2322                        accuracy: 0,
2323                    },
2324                    target: MoveTarget::Opponent,
2325                });
2326                attacker_choice.category = MoveCategory::Status;
2327            }
2328        }
2329        Abilities::EARTHEATER => {
2330            if attacker_choice.move_type == PokemonType::GROUND {
2331                attacker_choice.remove_all_effects();
2332                attacker_choice.base_power = 0.0;
2333                attacker_choice.heal = Some(Heal {
2334                    target: MoveTarget::Opponent,
2335                    amount: 0.25,
2336                });
2337                attacker_choice.category = MoveCategory::Status;
2338            }
2339        }
2340        Abilities::STEAMENGINE => {
2341            if attacker_choice.move_type == PokemonType::WATER
2342                || attacker_choice.move_type == PokemonType::FIRE
2343            {
2344                attacker_choice.add_or_create_secondaries(Secondary {
2345                    chance: 100.0,
2346                    target: MoveTarget::Opponent,
2347                    effect: Effect::Boost(StatBoosts {
2348                        attack: 0,
2349                        defense: 0,
2350                        special_attack: 0,
2351                        special_defense: 0,
2352                        speed: 6,
2353                        accuracy: 0,
2354                    }),
2355                });
2356            }
2357        }
2358        Abilities::THERMALEXCHANGE => {
2359            if attacker_choice.move_type == PokemonType::FIRE {
2360                attacker_choice.add_or_create_secondaries(Secondary {
2361                    chance: 100.0,
2362                    target: MoveTarget::Opponent,
2363                    effect: Effect::Boost(StatBoosts {
2364                        attack: 1,
2365                        defense: 0,
2366                        special_attack: 0,
2367                        special_defense: 0,
2368                        speed: 0,
2369                        accuracy: 0,
2370                    }),
2371                });
2372            }
2373        }
2374        #[cfg(any(feature = "gen9", feature = "gen8", feature = "gen7"))]
2375        Abilities::WEAKARMOR => {
2376            if attacker_choice.category == MoveCategory::Physical {
2377                attacker_choice.add_or_create_secondaries(Secondary {
2378                    chance: 100.0,
2379                    target: MoveTarget::Opponent,
2380                    effect: Effect::Boost(StatBoosts {
2381                        attack: 0,
2382                        defense: -1,
2383                        special_attack: 0,
2384                        special_defense: 0,
2385                        speed: 2,
2386                        accuracy: 0,
2387                    }),
2388                });
2389            }
2390        }
2391        #[cfg(any(feature = "gen6", feature = "gen5", feature = "gen4"))]
2392        Abilities::WEAKARMOR => {
2393            if attacker_choice.category == MoveCategory::Physical {
2394                attacker_choice.add_or_create_secondaries(Secondary {
2395                    chance: 100.0,
2396                    target: MoveTarget::Opponent,
2397                    effect: Effect::Boost(StatBoosts {
2398                        attack: 0,
2399                        defense: -1,
2400                        special_attack: 0,
2401                        special_defense: 0,
2402                        speed: 1,
2403                        accuracy: 0,
2404                    }),
2405                });
2406            }
2407        }
2408        Abilities::QUEENLYMAJESTY => {
2409            if attacker_choice.priority > 0 {
2410                attacker_choice.remove_all_effects();
2411                attacker_choice.accuracy = 0.0;
2412            }
2413        }
2414        Abilities::SAPSIPPER => {
2415            if attacker_choice.move_type == PokemonType::GRASS {
2416                attacker_choice.remove_all_effects();
2417                attacker_choice.accuracy = 100.0;
2418                attacker_choice.target = MoveTarget::Opponent;
2419                attacker_choice.boost = Some(Boost {
2420                    boosts: StatBoosts {
2421                        attack: 1,
2422                        defense: 0,
2423                        special_attack: 0,
2424                        special_defense: 0,
2425                        speed: 0,
2426                        accuracy: 0,
2427                    },
2428                    target: MoveTarget::Opponent,
2429                });
2430                attacker_choice.category = MoveCategory::Status;
2431            }
2432        }
2433        Abilities::SHADOWSHIELD => {
2434            if target_pkmn.hp == target_pkmn.maxhp {
2435                attacker_choice.base_power /= 2.0;
2436            }
2437        }
2438        Abilities::NOGUARD => {
2439            attacker_choice.accuracy = 100.0;
2440        }
2441        Abilities::MARVELSCALE => {
2442            if target_pkmn.status != PokemonStatus::NONE
2443                && attacker_choice.category == MoveCategory::Physical
2444            {
2445                attacker_choice.base_power /= 1.5;
2446            }
2447        }
2448        #[cfg(feature = "gen3")]
2449        Abilities::EFFECTSPORE => {
2450            if attacker_choice.flags.contact {
2451                attacker_choice.add_or_create_secondaries(Secondary {
2452                    chance: 3.30,
2453                    target: MoveTarget::User,
2454                    effect: Effect::Status(PokemonStatus::POISON),
2455                });
2456                attacker_choice.add_or_create_secondaries(Secondary {
2457                    chance: 3.30,
2458                    target: MoveTarget::User,
2459                    effect: Effect::Status(PokemonStatus::PARALYZE),
2460                });
2461                attacker_choice.add_or_create_secondaries(Secondary {
2462                    chance: 3.30,
2463                    target: MoveTarget::User,
2464                    effect: Effect::Status(PokemonStatus::SLEEP),
2465                });
2466            }
2467        }
2468
2469        #[cfg(not(feature = "gen3"))]
2470        Abilities::EFFECTSPORE => {
2471            if attacker_choice.flags.contact {
2472                attacker_choice.add_or_create_secondaries(Secondary {
2473                    chance: 9.0,
2474                    target: MoveTarget::User,
2475                    effect: Effect::Status(PokemonStatus::POISON),
2476                });
2477                attacker_choice.add_or_create_secondaries(Secondary {
2478                    chance: 10.0,
2479                    target: MoveTarget::User,
2480                    effect: Effect::Status(PokemonStatus::PARALYZE),
2481                });
2482                attacker_choice.add_or_create_secondaries(Secondary {
2483                    chance: 11.0,
2484                    target: MoveTarget::User,
2485                    effect: Effect::Status(PokemonStatus::SLEEP),
2486                });
2487            }
2488        }
2489        Abilities::FLAMEBODY => {
2490            if attacker_choice.flags.contact {
2491                attacker_choice.add_or_create_secondaries(Secondary {
2492                    chance: 30.0,
2493                    target: MoveTarget::User,
2494                    effect: Effect::Status(PokemonStatus::BURN),
2495                });
2496            }
2497        }
2498        Abilities::GOOEY => {
2499            if attacker_choice.flags.contact {
2500                attacker_choice.add_or_create_secondaries(Secondary {
2501                    chance: 100.0,
2502                    target: MoveTarget::User,
2503                    effect: Effect::Boost(StatBoosts {
2504                        attack: 0,
2505                        defense: 0,
2506                        special_attack: 0,
2507                        special_defense: 0,
2508                        speed: -1,
2509                        accuracy: 0,
2510                    }),
2511                })
2512            }
2513        }
2514        Abilities::MOTORDRIVE => {
2515            if attacker_choice.move_type == PokemonType::ELECTRIC {
2516                attacker_choice.remove_all_effects();
2517                attacker_choice.accuracy = 100.0;
2518                attacker_choice.target = MoveTarget::Opponent;
2519                attacker_choice.boost = Some(Boost {
2520                    boosts: StatBoosts {
2521                        attack: 0,
2522                        defense: 0,
2523                        special_attack: 0,
2524                        special_defense: 0,
2525                        speed: 1,
2526                        accuracy: 0,
2527                    },
2528                    target: MoveTarget::Opponent,
2529                });
2530                attacker_choice.category = MoveCategory::Status;
2531            }
2532        }
2533        Abilities::WINDRIDER => {
2534            if attacker_choice.flags.wind {
2535                attacker_choice.remove_all_effects();
2536                attacker_choice.accuracy = 100.0;
2537                attacker_choice.target = MoveTarget::Opponent;
2538                attacker_choice.boost = Some(Boost {
2539                    boosts: StatBoosts {
2540                        attack: 1,
2541                        defense: 0,
2542                        special_attack: 0,
2543                        special_defense: 0,
2544                        speed: 0,
2545                        accuracy: 0,
2546                    },
2547                    target: MoveTarget::Opponent,
2548                });
2549                attacker_choice.category = MoveCategory::Status;
2550            }
2551        }
2552        Abilities::SUCTIONCUPS => {
2553            attacker_choice.flags.drag = false;
2554        }
2555        Abilities::WONDERGUARD => {
2556            if attacker_choice.category != MoveCategory::Status
2557                && type_effectiveness_modifier(&attacker_choice.move_type, &target_pkmn) <= 1.0
2558            {
2559                attacker_choice.remove_all_effects();
2560                attacker_choice.base_power = 0.0;
2561            }
2562        }
2563        Abilities::FAIRYAURA => {
2564            if attacker_choice.move_type == PokemonType::FAIRY {
2565                attacker_choice.base_power *= 1.33;
2566            }
2567        }
2568        Abilities::LEVITATE => {
2569            if attacker_choice.move_type == PokemonType::GROUND
2570                && attacker_choice.target == MoveTarget::Opponent
2571                && attacker_choice.move_id != Choices::THOUSANDARROWS
2572            {
2573                attacker_choice.base_power = 0.0;
2574            }
2575        }
2576        Abilities::STATIC => {
2577            if attacker_choice.flags.contact {
2578                attacker_choice.add_or_create_secondaries(Secondary {
2579                    chance: 30.0,
2580                    target: MoveTarget::User,
2581                    effect: Effect::Status(PokemonStatus::PARALYZE),
2582                })
2583            }
2584        }
2585        Abilities::WONDERSKIN => {
2586            if attacker_choice.category == MoveCategory::Status && attacker_choice.accuracy > 50.0 {
2587                attacker_choice.accuracy = 50.0;
2588            }
2589        }
2590        Abilities::THICKFAT => {
2591            if attacker_choice.move_type == PokemonType::FIRE
2592                || attacker_choice.move_type == PokemonType::ICE
2593            {
2594                attacker_choice.base_power /= 2.0;
2595            }
2596        }
2597        Abilities::FLASHFIRE => {
2598            if attacker_choice.move_type == PokemonType::FIRE {
2599                attacker_choice.remove_all_effects();
2600                attacker_choice.volatile_status = Some(VolatileStatus {
2601                    target: MoveTarget::Opponent,
2602                    volatile_status: PokemonVolatileStatus::FLASHFIRE,
2603                });
2604            }
2605        }
2606        Abilities::WELLBAKEDBODY => {
2607            if attacker_choice.move_type == PokemonType::FIRE {
2608                attacker_choice.remove_all_effects();
2609                attacker_choice.boost = Some(Boost {
2610                    boosts: StatBoosts {
2611                        attack: 0,
2612                        defense: 2,
2613                        special_attack: 0,
2614                        special_defense: 0,
2615                        speed: 0,
2616                        accuracy: 0,
2617                    },
2618                    target: MoveTarget::Opponent,
2619                });
2620            }
2621        }
2622        Abilities::DAZZLING => {
2623            if attacker_choice.priority > 0 {
2624                attacker_choice.accuracy = 0.0;
2625            }
2626        }
2627        Abilities::LIQUIDOOZE => {
2628            if let Some(drain) = attacker_choice.drain {
2629                attacker_choice.drain = Some(-1.0 * drain);
2630            }
2631        }
2632        Abilities::PRISMARMOR => {
2633            if type_effectiveness_modifier(&attacker_choice.move_type, &target_pkmn) > 1.0 {
2634                attacker_choice.base_power *= 0.75;
2635            }
2636        }
2637        Abilities::HEATPROOF => {
2638            if attacker_choice.move_type == PokemonType::FIRE {
2639                attacker_choice.base_power *= 0.5;
2640            }
2641        }
2642        Abilities::SHIELDDUST => {
2643            if let Some(secondaries) = &mut attacker_choice.secondaries {
2644                for secondary in secondaries.iter_mut() {
2645                    if secondary.target == MoveTarget::Opponent {
2646                        secondary.chance = 0.0;
2647                    }
2648                }
2649            }
2650        }
2651        Abilities::GRASSPELT => {
2652            if state.terrain_is_active(&Terrain::GRASSYTERRAIN)
2653                && attacker_choice.category == MoveCategory::Physical
2654            {
2655                attacker_choice.base_power /= 1.5;
2656            }
2657        }
2658        Abilities::FILTER => {
2659            if type_effectiveness_modifier(&attacker_choice.move_type, &target_pkmn) > 1.0 {
2660                attacker_choice.base_power *= 0.75;
2661            }
2662        }
2663        Abilities::FURCOAT => {
2664            if attacker_choice.category == MoveCategory::Physical {
2665                attacker_choice.base_power *= 0.5;
2666            }
2667        }
2668        Abilities::TANGLINGHAIR => {
2669            if attacker_choice.flags.contact {
2670                attacker_choice.add_or_create_secondaries(Secondary {
2671                    chance: 100.0,
2672                    target: MoveTarget::User,
2673                    effect: Effect::Boost(StatBoosts {
2674                        attack: 0,
2675                        defense: 0,
2676                        special_attack: 0,
2677                        special_defense: 0,
2678                        speed: -1,
2679                        accuracy: 0,
2680                    }),
2681                })
2682            }
2683        }
2684        Abilities::MAGICBOUNCE => {
2685            if attacker_choice.flags.reflectable {
2686                attacker_choice.target = MoveTarget::User;
2687                if let Some(side_condition) = &mut attacker_choice.side_condition {
2688                    if side_condition.target == MoveTarget::Opponent {
2689                        side_condition.target = MoveTarget::User;
2690                    }
2691                }
2692                if let Some(status) = &mut attacker_choice.status {
2693                    if status.target == MoveTarget::Opponent {
2694                        status.target = MoveTarget::User;
2695                    }
2696                }
2697                if let Some(volatile_status) = &mut attacker_choice.volatile_status {
2698                    if volatile_status.target == MoveTarget::Opponent {
2699                        volatile_status.target = MoveTarget::User;
2700                    }
2701                }
2702            }
2703        }
2704        Abilities::STORMDRAIN => {
2705            if attacker_choice.move_type == PokemonType::WATER {
2706                attacker_choice.remove_all_effects();
2707                attacker_choice.accuracy = 100.0;
2708                attacker_choice.target = MoveTarget::Opponent;
2709                attacker_choice.boost = Some(Boost {
2710                    boosts: StatBoosts {
2711                        attack: 0,
2712                        defense: 0,
2713                        special_attack: 1,
2714                        special_defense: 0,
2715                        speed: 0,
2716                        accuracy: 0,
2717                    },
2718                    target: MoveTarget::Opponent,
2719                });
2720                attacker_choice.category = MoveCategory::Status;
2721            }
2722        }
2723        Abilities::WATERCOMPACTION => {
2724            if attacker_choice.move_type == PokemonType::WATER {
2725                attacker_choice.add_or_create_secondaries(Secondary {
2726                    chance: 100.0,
2727                    target: MoveTarget::Opponent,
2728                    effect: Effect::Boost(StatBoosts {
2729                        attack: 0,
2730                        defense: 2,
2731                        special_attack: 0,
2732                        special_defense: 0,
2733                        speed: 0,
2734                        accuracy: 0,
2735                    }),
2736                });
2737            }
2738        }
2739        Abilities::JUSTIFIED => {
2740            if attacker_choice.move_type == PokemonType::DARK {
2741                attacker_choice.add_or_create_secondaries(Secondary {
2742                    chance: 100.0,
2743                    target: MoveTarget::Opponent,
2744                    effect: Effect::Boost(StatBoosts {
2745                        attack: 1,
2746                        defense: 0,
2747                        special_attack: 0,
2748                        special_defense: 0,
2749                        speed: 0,
2750                        accuracy: 0,
2751                    }),
2752                })
2753            }
2754        }
2755        Abilities::ICESCALES => {
2756            if attacker_choice.category == MoveCategory::Special {
2757                attacker_choice.base_power *= 0.5;
2758            }
2759        }
2760        Abilities::WATERABSORB => {
2761            if attacker_choice.move_type == PokemonType::WATER {
2762                attacker_choice.remove_all_effects();
2763                attacker_choice.base_power = 0.0;
2764                attacker_choice.heal = Some(Heal {
2765                    target: MoveTarget::Opponent,
2766                    amount: 0.25,
2767                });
2768                attacker_choice.category = MoveCategory::Status;
2769            }
2770        }
2771        Abilities::DRYSKIN => {
2772            if attacker_choice.move_type == PokemonType::WATER {
2773                attacker_choice.remove_all_effects();
2774                attacker_choice.base_power = 0.0;
2775                attacker_choice.heal = Some(Heal {
2776                    target: MoveTarget::Opponent,
2777                    amount: 0.25,
2778                });
2779                attacker_choice.category = MoveCategory::Status;
2780            } else if attacker_choice.move_type == PokemonType::FIRE {
2781                attacker_choice.base_power *= 1.25;
2782            }
2783        }
2784        Abilities::FLUFFY => {
2785            if attacker_choice.flags.contact {
2786                attacker_choice.base_power *= 0.5;
2787            }
2788            if attacker_choice.move_type == PokemonType::FIRE {
2789                attacker_choice.base_power *= 2.0;
2790            }
2791        }
2792        Abilities::PUNKROCK => {
2793            if attacker_choice.flags.sound {
2794                attacker_choice.base_power /= 2.0;
2795            }
2796        }
2797        Abilities::DAMP => {
2798            if [
2799                Choices::SELFDESTRUCT,
2800                Choices::EXPLOSION,
2801                Choices::MINDBLOWN,
2802                Choices::MISTYEXPLOSION,
2803            ]
2804            .contains(&attacker_choice.move_id)
2805            {
2806                attacker_choice.accuracy = 0.0;
2807                attacker_choice.heal = None;
2808            }
2809        }
2810        Abilities::VOLTABSORB => {
2811            #[cfg(feature = "gen3")]
2812            let activate = attacker_choice.move_type == PokemonType::ELECTRIC
2813                && attacker_choice.category != MoveCategory::Status;
2814
2815            #[cfg(not(feature = "gen3"))]
2816            let activate = attacker_choice.move_type == PokemonType::ELECTRIC;
2817
2818            if activate {
2819                attacker_choice.remove_all_effects();
2820                attacker_choice.accuracy = 100.0;
2821                attacker_choice.base_power = 0.0;
2822                attacker_choice.heal = Some(Heal {
2823                    target: MoveTarget::Opponent,
2824                    amount: 0.25,
2825                });
2826                attacker_choice.category = MoveCategory::Status;
2827            }
2828        }
2829        Abilities::SOLIDROCK => {
2830            if type_effectiveness_modifier(&attacker_choice.move_type, &target_pkmn) > 1.0 {
2831                attacker_choice.base_power *= 0.75;
2832            }
2833        }
2834        Abilities::OVERCOAT => {
2835            if attacker_choice.flags.powder {
2836                attacker_choice.remove_all_effects();
2837                attacker_choice.accuracy = 0.0
2838            }
2839        }
2840        Abilities::GOODASGOLD => {
2841            // This engine doesn't distinguish "targetting other pkmn" versus "targetting the side"
2842            // Thankfully it is a short list of moves that target the opponent side
2843            if attacker_choice.category == MoveCategory::Status
2844                && attacker_choice.target == MoveTarget::Opponent
2845                && ![
2846                    Choices::STEALTHROCK,
2847                    Choices::STICKYWEB,
2848                    Choices::TOXICSPIKES,
2849                    Choices::SPIKES,
2850                ]
2851                .contains(&attacker_choice.move_id)
2852            {
2853                attacker_choice.remove_all_effects();
2854            }
2855        }
2856        Abilities::RATTLED => {
2857            if attacker_choice.move_type == PokemonType::BUG
2858                || attacker_choice.move_type == PokemonType::DARK
2859                || attacker_choice.move_type == PokemonType::GHOST
2860            {
2861                attacker_choice.add_or_create_secondaries(Secondary {
2862                    chance: 100.0,
2863                    target: MoveTarget::Opponent,
2864                    effect: Effect::Boost(StatBoosts {
2865                        attack: 0,
2866                        defense: 0,
2867                        special_attack: 0,
2868                        special_defense: 0,
2869                        speed: 1,
2870                        accuracy: 0,
2871                    }),
2872                });
2873            }
2874        }
2875        Abilities::WATERBUBBLE => {
2876            if attacker_choice.move_type == PokemonType::FIRE {
2877                attacker_choice.base_power /= 2.0;
2878            }
2879        }
2880        Abilities::PURIFYINGSALT => {
2881            if attacker_choice.move_type == PokemonType::GHOST {
2882                attacker_choice.base_power /= 2.0;
2883            }
2884        }
2885        _ => {}
2886    }
2887}