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
352fn 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 Abilities::ICEFACE => {
521 if defending_pkmn.id == PokemonName::EISCUE && choice.category == MoveCategory::Physical
522 {
523 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 #[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 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 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 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 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 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 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 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 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}