poke_engine/
abilities.rs

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