poke_engine/
abilities.rs

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