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