1#![allow(unused_variables)]
2use crate::choices::{
3 Boost, Choice, Choices, Effect, Heal, MoveCategory, MoveTarget, Secondary, StatBoosts,
4 VolatileStatus,
5};
6use crate::damage_calc::type_effectiveness_modifier;
7use crate::define_enum_with_from_str;
8use crate::generate_instructions::{add_remove_status_instructions, get_boost_instruction};
9use crate::instruction::{
10 ApplyVolatileStatusInstruction, BoostInstruction, ChangeAbilityInstruction,
11 ChangeItemInstruction, ChangeSideConditionInstruction, ChangeStatusInstruction, ChangeTerrain,
12 ChangeType, ChangeVolatileStatusDurationInstruction, ChangeWeather, DamageInstruction,
13 FormeChangeInstruction, HealInstruction, Instruction, StateInstructions,
14};
15use crate::items::{get_choice_move_disable_instructions, Items};
16use crate::pokemon::PokemonName;
17use crate::state::{PokemonBoostableStat, PokemonSideCondition, PokemonType, Side, Terrain};
18use crate::state::{PokemonStatus, State};
19use crate::state::{PokemonVolatileStatus, SideReference, Weather};
20use std::cmp;
21
22#[cfg(any(feature = "gen3", feature = "gen4", feature = "gen5"))]
23pub const WEATHER_ABILITY_TURNS: i8 = -1;
24
25#[cfg(any(feature = "gen6", feature = "gen7", feature = "gen8", feature = "gen9"))]
26pub const WEATHER_ABILITY_TURNS: i8 = 5;
27
28define_enum_with_from_str! {
29 #[repr(u16)]
30 #[derive(PartialEq, Debug, Clone)]
31 Abilities {
32 NONE,
33 ARMORTAIL,
34 RIPEN,
35 TANGLEDFEET,
36 DRAGONSMAW,
37 CLEARBODY,
38 GALVANIZE,
39 VITALSPIRIT,
40 AERILATE,
41 DEFIANT,
42 CUTECHARM,
43 NEUROFORCE,
44 SOUNDPROOF,
45 RKSSYSTEM,
46 POISONPOINT,
47 STAKEOUT,
48 UNNERVE,
49 ROCKHEAD,
50 AURABREAK,
51 MIMICRY,
52 BULLETPROOF,
53 POWEROFALCHEMY,
54 TECHNICIAN,
55 MULTISCALE,
56 ARENATRAP,
57 BATTLEBOND,
58 DISGUISE,
59 EARLYBIRD,
60 LIGHTNINGROD,
61 MAGICIAN,
62 REFRIGERATE,
63 FRIENDGUARD,
64 NOABILITY,
65 GULPMISSILE,
66 POWERCONSTRUCT,
67 FORECAST,
68 PRANKSTER,
69 PROTEAN,
70 ASONEGLASTRIER,
71 SHADOWTAG,
72 SHARPNESS,
73 WINDRIDER,
74 SKILLLINK,
75 INTREPIDSWORD,
76 SOULHEART,
77 SWIFTSWIM,
78 EARTHEATER,
79 SUPERLUCK,
80 SUPREMEOVERLORD,
81 INSOMNIA,
82 DANCER,
83 STEAMENGINE,
84 ANGERPOINT,
85 CONTRARY,
86 MAGMAARMOR,
87 HUNGERSWITCH,
88 RECEIVER,
89 ZENMODE,
90 EMERGENCYEXIT,
91 ILLUSION,
92 WEAKARMOR,
93 DROUGHT,
94 INNARDSOUT,
95 SHIELDSDOWN,
96 ADAPTABILITY,
97 CORROSION,
98 LONGREACH,
99 PUREPOWER,
100 TINTEDLENS,
101 QUEENLYMAJESTY,
102 DESOLATELAND,
103 MOXIE,
104 SAPSIPPER,
105 SLUSHRUSH,
106 BIGPECKS,
107 STALL,
108 WHITESMOKE,
109 FLAREBOOST,
110 SHADOWSHIELD,
111 LIQUIDVOICE,
112 MISTYSURGE,
113 MULTITYPE,
114 NOGUARD,
115 TORRENT,
116 DELTASTREAM,
117 KLUTZ,
118 LIBERO,
119 SERENEGRACE,
120 CURSEDBODY,
121 UNAWARE,
122 LIGHTMETAL,
123 MARVELSCALE,
124 TELEPATHY,
125 QUICKDRAW,
126 HYPERCUTTER,
127 SYMBIOSIS,
128 PLUS,
129 MIRRORARMOR,
130 PASTELVEIL,
131 TOUGHCLAWS,
132 EFFECTSPORE,
133 MUMMY,
134 BADDREAMS,
135 MAGICGUARD,
136 SANDSTREAM,
137 POWERSPOT,
138 FLAMEBODY,
139 RECKLESS,
140 PRESSURE,
141 GOOEY,
142 IMMUNITY,
143 LEAFGUARD,
144 HUGEPOWER,
145 SOLARPOWER,
146 SCHOOLING,
147 MOTORDRIVE,
148 ANTICIPATION,
149 MERCILESS,
150 TRACE,
151 NATURALCURE,
152 HARVEST,
153 SUCTIONCUPS,
154 ICEFACE,
155 ROUGHSKIN,
156 WONDERGUARD,
157 WATERVEIL,
158 FAIRYAURA,
159 SANDSPIT,
160 SEEDSOWER,
161 TOXICDEBRIS,
162 INTIMIDATE,
163 DAUNTLESSSHIELD,
164 AROMAVEIL,
165 AIRLOCK,
166 NORMALIZE,
167 DARKAURA,
168 VICTORYSTAR,
169 GRASSYSURGE,
170 STURDY,
171 PICKPOCKET,
172 ELECTRICSURGE,
173 HADRONENGINE,
174 ORICHALCUMPULSE,
175 RUNAWAY,
176 OBLIVIOUS,
177 SURGESURFER,
178 LEVITATE,
179 ASONESPECTRIER,
180 PICKUP,
181 ICEBODY,
182 CURIOUSMEDICINE,
183 FLOWERVEIL,
184 STATIC,
185 WONDERSKIN,
186 OVERGROW,
187 PROPELLERTAIL,
188 THICKFAT,
189 GLUTTONY,
190 KEENEYE,
191 MOUNTAINEER,
192 FLASHFIRE,
193 COMPOUNDEYES,
194 STEELWORKER,
195 COMATOSE,
196 BALLFETCH,
197 DAZZLING,
198 DOWNLOAD,
199 TRANSISTOR,
200 MOLDBREAKER,
201 MYCELIUMMIGHT,
202 LIQUIDOOZE,
203 POISONHEAL,
204 PRISMARMOR,
205 SNIPER,
206 STENCH,
207 COMPETITIVE,
208 SWARM,
209 STALWART,
210 ILLUMINATE,
211 TURBOBLAZE,
212 GORILLATACTICS,
213 SPEEDBOOST,
214 GUARDDOG,
215 HEATPROOF,
216 SNOWCLOAK,
217 TERAVOLT,
218 CHILLINGNEIGH,
219 SHIELDDUST,
220 RIVALRY,
221 PRIMORDIALSEA,
222 SCREENCLEANER,
223 MAGNETPULL,
224 HONEYGATHER,
225 COTTONDOWN,
226 GRASSPELT,
227 BATTLEARMOR,
228 BEASTBOOST,
229 BERSERK,
230 MINUS,
231 RAINDISH,
232 SYNCHRONIZE,
233 FILTER,
234 TRUANT,
235 FURCOAT,
236 FULLMETALBODY,
237 REGENERATOR,
238 FOREWARN,
239 IRONBARBS,
240 STAMINA,
241 SANDRUSH,
242 COLORCHANGE,
243 BLAZE,
244 ANALYTIC,
245 TANGLINGHAIR,
246 CLOUDNINE,
247 STEELYSPIRIT,
248 QUICKFEET,
249 MAGICBOUNCE,
250 MEGALAUNCHER,
251 HEAVYMETAL,
252 STORMDRAIN,
253 PIXILATE,
254 WATERCOMPACTION,
255 JUSTIFIED,
256 SLOWSTART,
257 SNOWWARNING,
258 FLOWERGIFT,
259 SHEDSKIN,
260 WIMPOUT,
261 ICESCALES,
262 INFILTRATOR,
263 LIMBER,
264 PSYCHICSURGE,
265 DEFEATIST,
266 WATERABSORB,
267 IMPOSTER,
268 DRYSKIN,
269 FLUFFY,
270 UNBURDEN,
271 CHEEKPOUCH,
272 STANCECHANGE,
273 MOODY,
274 ROCKYPAYLOAD,
275 PUNKROCK,
276 SANDVEIL,
277 PARENTALBOND,
278 STRONGJAW,
279 BATTERY,
280 HEALER,
281 STEADFAST,
282 DAMP,
283 PERISHBODY,
284 TRIAGE,
285 SHEERFORCE,
286 OWNTEMPO,
287 FRISK,
288 VOLTABSORB,
289 GALEWINGS,
290 AFTERMATH,
291 STICKYHOLD,
292 GRIMNEIGH,
293 IRONFIST,
294 REBOUND,
295 UNSEENFIST,
296 SOLIDROCK,
297 HUSTLE,
298 HYDRATION,
299 SCRAPPY,
300 MINDSEYE,
301 OVERCOAT,
302 NEUTRALIZINGGAS,
303 SWEETVEIL,
304 DRIZZLE,
305 INNERFOCUS,
306 POISONTOUCH,
307 WANDERINGSPIRIT,
308 GUTS,
309 SHELLARMOR,
310 RATTLED,
311 WATERBUBBLE,
312 SANDFORCE,
313 TOXICBOOST,
314 PERSISTENT,
315 CHLOROPHYLL,
316 SIMPLE,
317 PURIFYINGSALT,
318 EMBODYASPECTWELLSPRING,
319 EMBODYASPECTCORNERSTONE,
320 EMBODYASPECTHEARTHFLAME,
321 EMBODYASPECTTEAL,
322 ANGERSHELL,
323 BEADSOFRUIN,
324 COMMANDER,
325 COSTAR,
326 CUDCHEW,
327 ELECTROMORPHOSIS,
328 EMBODYASPECT,
329 GOODASGOLD,
330 HOSPITALITY,
331 LINGERINGAROMA,
332 OPPORTUNIST,
333 POISONPUPPETEER,
334 PROTOSYNTHESIS,
335 QUARKDRIVE,
336 SUPERSWEETSYRUP,
337 SWORDOFRUIN,
338 TABLETSOFRUIN,
339 TERASHELL,
340 TERASHIFT,
341 TERAFORMZERO,
342 THERMALEXCHANGE,
343 TOXICCHAIN,
344 VESSELOFRUIN,
345 WELLBAKEDBODY,
346 WINDPOWER,
347 ZEROTOHERO,
348 },
349 default = NONE
350}
351
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 instructions
1510 .instruction_list
1511 .push(Instruction::ApplyVolatileStatus(
1512 ApplyVolatileStatusInstruction {
1513 side_ref: *side_ref,
1514 volatile_status: PokemonVolatileStatus::SLOWSTART,
1515 },
1516 ));
1517 instructions
1518 .instruction_list
1519 .push(Instruction::ChangeVolatileStatusDuration(
1520 ChangeVolatileStatusDurationInstruction {
1521 side_ref: *side_ref,
1522 volatile_status: PokemonVolatileStatus::SLOWSTART,
1523 amount: 6 - attacking_side.volatile_status_durations.slowstart,
1524 },
1525 ));
1526 attacking_side
1527 .volatile_statuses
1528 .insert(PokemonVolatileStatus::SLOWSTART);
1529 attacking_side.volatile_status_durations.slowstart = 6;
1530 }
1531 Abilities::DROUGHT | Abilities::ORICHALCUMPULSE => {
1532 if state.weather.weather_type != Weather::SUN {
1533 instructions
1534 .instruction_list
1535 .push(Instruction::ChangeWeather(ChangeWeather {
1536 new_weather: Weather::SUN,
1537 new_weather_turns_remaining: WEATHER_ABILITY_TURNS,
1538 previous_weather: state.weather.weather_type,
1539 previous_weather_turns_remaining: state.weather.turns_remaining,
1540 }));
1541 state.weather.weather_type = Weather::SUN;
1542 state.weather.turns_remaining = WEATHER_ABILITY_TURNS;
1543 }
1544 }
1545 Abilities::DESOLATELAND => {
1546 if state.weather.weather_type != Weather::HARSHSUN {
1547 instructions
1548 .instruction_list
1549 .push(Instruction::ChangeWeather(ChangeWeather {
1550 new_weather: Weather::HARSHSUN,
1551 new_weather_turns_remaining: -1,
1552 previous_weather: state.weather.weather_type,
1553 previous_weather_turns_remaining: state.weather.turns_remaining,
1554 }));
1555 state.weather.weather_type = Weather::HARSHSUN;
1556 state.weather.turns_remaining = -1;
1557 }
1558 }
1559 Abilities::MISTYSURGE => {
1560 if state.terrain.terrain_type != Terrain::MISTYTERRAIN {
1561 instructions
1562 .instruction_list
1563 .push(Instruction::ChangeTerrain(ChangeTerrain {
1564 new_terrain: Terrain::MISTYTERRAIN,
1565 new_terrain_turns_remaining: 5,
1566 previous_terrain: state.terrain.terrain_type,
1567 previous_terrain_turns_remaining: state.terrain.turns_remaining,
1568 }));
1569 state.terrain.terrain_type = Terrain::MISTYTERRAIN;
1570 state.terrain.turns_remaining = 5;
1571 }
1572 }
1573 Abilities::SANDSTREAM => {
1574 if state.weather.weather_type != Weather::SAND {
1575 instructions
1576 .instruction_list
1577 .push(Instruction::ChangeWeather(ChangeWeather {
1578 new_weather: Weather::SAND,
1579 new_weather_turns_remaining: WEATHER_ABILITY_TURNS,
1580 previous_weather: state.weather.weather_type,
1581 previous_weather_turns_remaining: state.weather.turns_remaining,
1582 }));
1583 state.weather.weather_type = Weather::SAND;
1584 state.weather.turns_remaining = WEATHER_ABILITY_TURNS;
1585 }
1586 }
1587 Abilities::INTIMIDATE => {
1588 if let Some(boost_instruction) = get_boost_instruction(
1589 &defending_side,
1590 &PokemonBoostableStat::Attack,
1591 &-1,
1592 side_ref,
1593 &side_ref.get_other_side(),
1594 ) {
1595 let defender = defending_side.get_active_immutable();
1596 let mut adrenaline_orb_item_instruction = None;
1597 let mut adrenaline_orb_boost_instruction = None;
1598 if defender.item == Items::ADRENALINEORB {
1599 if let Some(boost_ins) = get_boost_instruction(
1600 &defending_side,
1601 &PokemonBoostableStat::Speed,
1602 &1,
1603 &side_ref.get_other_side(),
1604 &side_ref.get_other_side(),
1605 ) {
1606 adrenaline_orb_boost_instruction = Some(boost_ins);
1607 adrenaline_orb_item_instruction =
1608 Some(Instruction::ChangeItem(ChangeItemInstruction {
1609 side_ref: side_ref.get_other_side(),
1610 current_item: Items::ADRENALINEORB,
1611 new_item: Items::NONE,
1612 }));
1613 }
1614 }
1615 match defender.ability {
1616 Abilities::OWNTEMPO
1617 | Abilities::OBLIVIOUS
1618 | Abilities::INNERFOCUS
1619 | Abilities::SCRAPPY => {}
1620 _ => {
1621 state.apply_one_instruction(&boost_instruction);
1622 instructions.instruction_list.push(boost_instruction);
1623 }
1624 }
1625 if let Some(ins) = adrenaline_orb_boost_instruction {
1626 state.apply_one_instruction(&ins);
1627 instructions.instruction_list.push(ins);
1628 if let Some(ins) = adrenaline_orb_item_instruction {
1629 state.apply_one_instruction(&ins);
1630 instructions.instruction_list.push(ins);
1631 }
1632 }
1633 }
1634 }
1635 Abilities::DAUNTLESSSHIELD => {
1636 attacking_side.defense_boost += 1;
1638 instructions
1639 .instruction_list
1640 .push(Instruction::Boost(BoostInstruction {
1641 side_ref: *side_ref,
1642 stat: PokemonBoostableStat::Defense,
1643 amount: 1,
1644 }));
1645 }
1646 Abilities::GRASSYSURGE => {
1647 if state.terrain.terrain_type != Terrain::GRASSYTERRAIN {
1648 instructions
1649 .instruction_list
1650 .push(Instruction::ChangeTerrain(ChangeTerrain {
1651 new_terrain: Terrain::GRASSYTERRAIN,
1652 new_terrain_turns_remaining: 5,
1653 previous_terrain: state.terrain.terrain_type,
1654 previous_terrain_turns_remaining: state.terrain.turns_remaining,
1655 }));
1656 state.terrain.terrain_type = Terrain::GRASSYTERRAIN;
1657 state.terrain.turns_remaining = 5;
1658 }
1659 }
1660 Abilities::ELECTRICSURGE | Abilities::HADRONENGINE => {
1661 if state.terrain.terrain_type != Terrain::ELECTRICTERRAIN {
1662 instructions
1663 .instruction_list
1664 .push(Instruction::ChangeTerrain(ChangeTerrain {
1665 new_terrain: Terrain::ELECTRICTERRAIN,
1666 new_terrain_turns_remaining: 5,
1667 previous_terrain: state.terrain.terrain_type,
1668 previous_terrain_turns_remaining: state.terrain.turns_remaining,
1669 }));
1670 state.terrain.terrain_type = Terrain::ELECTRICTERRAIN;
1671 state.terrain.turns_remaining = 5;
1672 }
1673 }
1674 Abilities::DOWNLOAD => {
1675 if defending_side.calculate_boosted_stat(PokemonBoostableStat::Defense)
1676 < defending_side.calculate_boosted_stat(PokemonBoostableStat::SpecialDefense)
1677 {
1678 if let Some(boost_instruction) = get_boost_instruction(
1679 &attacking_side,
1680 &PokemonBoostableStat::Attack,
1681 &1,
1682 side_ref,
1683 side_ref,
1684 ) {
1685 state.apply_one_instruction(&boost_instruction);
1686 instructions.instruction_list.push(boost_instruction);
1687 }
1688 } else {
1689 if let Some(boost_instruction) = get_boost_instruction(
1690 &attacking_side,
1691 &PokemonBoostableStat::SpecialAttack,
1692 &1,
1693 side_ref,
1694 side_ref,
1695 ) {
1696 state.apply_one_instruction(&boost_instruction);
1697 instructions.instruction_list.push(boost_instruction);
1698 }
1699 }
1700 }
1701 Abilities::PRIMORDIALSEA => {
1702 if state.weather.weather_type != Weather::HEAVYRAIN {
1703 instructions
1704 .instruction_list
1705 .push(Instruction::ChangeWeather(ChangeWeather {
1706 new_weather: Weather::HEAVYRAIN,
1707 new_weather_turns_remaining: -1,
1708 previous_weather: state.weather.weather_type,
1709 previous_weather_turns_remaining: state.weather.turns_remaining,
1710 }));
1711 state.weather.weather_type = Weather::HEAVYRAIN;
1712 state.weather.turns_remaining = -1;
1713 }
1714 }
1715 Abilities::SCREENCLEANER => {
1716 if state.side_one.side_conditions.reflect > 0 {
1717 instructions
1718 .instruction_list
1719 .push(Instruction::ChangeSideCondition(
1720 ChangeSideConditionInstruction {
1721 side_ref: SideReference::SideOne,
1722 side_condition: PokemonSideCondition::Reflect,
1723 amount: -1 * state.side_one.side_conditions.reflect,
1724 },
1725 ));
1726 state.side_one.side_conditions.reflect = 0;
1727 }
1728 if state.side_two.side_conditions.reflect > 0 {
1729 instructions
1730 .instruction_list
1731 .push(Instruction::ChangeSideCondition(
1732 ChangeSideConditionInstruction {
1733 side_ref: SideReference::SideTwo,
1734 side_condition: PokemonSideCondition::Reflect,
1735 amount: -1 * state.side_two.side_conditions.reflect,
1736 },
1737 ));
1738 state.side_two.side_conditions.reflect = 0;
1739 }
1740 if state.side_one.side_conditions.light_screen > 0 {
1741 instructions
1742 .instruction_list
1743 .push(Instruction::ChangeSideCondition(
1744 ChangeSideConditionInstruction {
1745 side_ref: SideReference::SideOne,
1746 side_condition: PokemonSideCondition::LightScreen,
1747 amount: -1 * state.side_one.side_conditions.light_screen,
1748 },
1749 ));
1750 state.side_one.side_conditions.light_screen = 0;
1751 }
1752 if state.side_two.side_conditions.light_screen > 0 {
1753 instructions
1754 .instruction_list
1755 .push(Instruction::ChangeSideCondition(
1756 ChangeSideConditionInstruction {
1757 side_ref: SideReference::SideTwo,
1758 side_condition: PokemonSideCondition::LightScreen,
1759 amount: -1 * state.side_two.side_conditions.light_screen,
1760 },
1761 ));
1762 state.side_two.side_conditions.light_screen = 0;
1763 }
1764 if state.side_one.side_conditions.aurora_veil > 0 {
1765 instructions
1766 .instruction_list
1767 .push(Instruction::ChangeSideCondition(
1768 ChangeSideConditionInstruction {
1769 side_ref: SideReference::SideOne,
1770 side_condition: PokemonSideCondition::AuroraVeil,
1771 amount: -1 * state.side_one.side_conditions.aurora_veil,
1772 },
1773 ));
1774 state.side_one.side_conditions.aurora_veil = 0;
1775 }
1776 if state.side_two.side_conditions.aurora_veil > 0 {
1777 instructions
1778 .instruction_list
1779 .push(Instruction::ChangeSideCondition(
1780 ChangeSideConditionInstruction {
1781 side_ref: SideReference::SideTwo,
1782 side_condition: PokemonSideCondition::AuroraVeil,
1783 amount: -1 * state.side_two.side_conditions.aurora_veil,
1784 },
1785 ));
1786 state.side_two.side_conditions.aurora_veil = 0;
1787 }
1788 }
1789 Abilities::SNOWWARNING => {
1790 #[cfg(feature = "gen9")]
1791 let weather_type = Weather::SNOW;
1792 #[cfg(not(feature = "gen9"))]
1793 let weather_type = Weather::HAIL;
1794
1795 if state.weather.weather_type != weather_type {
1796 instructions
1797 .instruction_list
1798 .push(Instruction::ChangeWeather(ChangeWeather {
1799 new_weather: weather_type,
1800 new_weather_turns_remaining: WEATHER_ABILITY_TURNS,
1801 previous_weather: state.weather.weather_type,
1802 previous_weather_turns_remaining: state.weather.turns_remaining,
1803 }));
1804 state.weather.weather_type = weather_type;
1805 state.weather.turns_remaining = WEATHER_ABILITY_TURNS;
1806 }
1807 }
1808 Abilities::PSYCHICSURGE => {
1809 if state.terrain.terrain_type != Terrain::PSYCHICTERRAIN {
1810 instructions
1811 .instruction_list
1812 .push(Instruction::ChangeTerrain(ChangeTerrain {
1813 new_terrain: Terrain::PSYCHICTERRAIN,
1814 new_terrain_turns_remaining: 5,
1815 previous_terrain: state.terrain.terrain_type,
1816 previous_terrain_turns_remaining: state.terrain.turns_remaining,
1817 }));
1818 state.terrain.terrain_type = Terrain::PSYCHICTERRAIN;
1819 state.terrain.turns_remaining = 5;
1820 }
1821 }
1822 Abilities::DRIZZLE => {
1823 if state.weather.weather_type != Weather::RAIN {
1824 instructions
1825 .instruction_list
1826 .push(Instruction::ChangeWeather(ChangeWeather {
1827 new_weather: Weather::RAIN,
1828 new_weather_turns_remaining: WEATHER_ABILITY_TURNS,
1829 previous_weather: state.weather.weather_type,
1830 previous_weather_turns_remaining: state.weather.turns_remaining,
1831 }));
1832 state.weather.weather_type = Weather::RAIN;
1833 state.weather.turns_remaining = WEATHER_ABILITY_TURNS;
1834 }
1835 }
1836 _ => {}
1837 }
1838}
1839
1840pub fn ability_modify_attack_being_used(
1841 state: &State,
1842 attacker_choice: &mut Choice,
1843 defender_choice: &Choice,
1844 attacking_side_ref: &SideReference,
1845) {
1846 let (attacking_side, defending_side) = state.get_both_sides_immutable(attacking_side_ref);
1847 let attacking_pkmn = attacking_side.get_active_immutable();
1848 if defending_side.get_active_immutable().ability == Abilities::NEUTRALIZINGGAS {
1849 return;
1850 }
1851 match attacking_pkmn.ability {
1852 #[cfg(any(feature = "gen9", feature = "gen8", feature = "gen7"))]
1853 Abilities::PRANKSTER => {
1854 if attacker_choice.category == MoveCategory::Status
1855 && defending_side
1856 .get_active_immutable()
1857 .has_type(&PokemonType::DARK)
1858 {
1859 attacker_choice.remove_all_effects();
1860 }
1861 }
1862 Abilities::BEADSOFRUIN => {
1863 if attacker_choice.category == MoveCategory::Special {
1864 attacker_choice.base_power *= 1.33;
1865 }
1866 }
1867 Abilities::SWORDOFRUIN => {
1868 if attacker_choice.category == MoveCategory::Physical {
1869 attacker_choice.base_power *= 1.33;
1870 }
1871 }
1872 Abilities::SHARPNESS => {
1873 if attacker_choice.flags.slicing {
1874 attacker_choice.base_power *= 1.5;
1875 }
1876 }
1877 Abilities::WATERBUBBLE => {
1878 if attacker_choice.move_type == PokemonType::WATER {
1879 attacker_choice.base_power *= 2.0;
1880 }
1881 }
1882 Abilities::DRAGONSMAW => {
1883 if attacker_choice.move_type == PokemonType::DRAGON {
1884 attacker_choice.base_power *= 1.5;
1885 }
1886 }
1887 Abilities::HADRONENGINE => {
1888 if attacker_choice.category == MoveCategory::Special
1889 && state.terrain.terrain_type == Terrain::ELECTRICTERRAIN
1890 {
1891 attacker_choice.base_power *= 1.33;
1892 }
1893 }
1894 Abilities::ORICHALCUMPULSE => {
1895 if attacker_choice.category == MoveCategory::Physical
1896 && state.weather.weather_type == Weather::SUN
1897 {
1898 attacker_choice.base_power *= 1.33;
1899 }
1900 }
1901 Abilities::GALVANIZE => {
1902 if attacker_choice.move_type == PokemonType::NORMAL {
1903 attacker_choice.move_type = PokemonType::ELECTRIC;
1904 attacker_choice.base_power *= 1.2;
1905 }
1906 }
1907 #[cfg(any(feature = "gen9", feature = "gen8", feature = "gen7"))]
1908 Abilities::AERILATE => {
1909 if attacker_choice.move_type == PokemonType::NORMAL {
1910 attacker_choice.move_type = PokemonType::FLYING;
1911 attacker_choice.base_power *= 1.2;
1912 }
1913 }
1914 #[cfg(any(feature = "gen6", feature = "gen5", feature = "gen4"))]
1915 Abilities::AERILATE => {
1916 if attacker_choice.move_type == PokemonType::NORMAL {
1917 attacker_choice.move_type = PokemonType::FLYING;
1918 attacker_choice.base_power *= 1.3;
1919 }
1920 }
1921 Abilities::NEUROFORCE => {
1922 if type_effectiveness_modifier(
1923 &attacker_choice.move_type,
1924 &defending_side.get_active_immutable(),
1925 ) > 1.0
1926 {
1927 attacker_choice.base_power *= 1.25;
1928 }
1929 }
1930 Abilities::STAKEOUT => {
1931 if defender_choice.category == MoveCategory::Switch {
1932 attacker_choice.base_power *= 2.0;
1933 }
1934 }
1935 Abilities::TECHNICIAN => {
1936 if attacker_choice.base_power <= 60.0 {
1937 attacker_choice.base_power *= 1.5;
1938 }
1939 }
1940 #[cfg(any(feature = "gen9", feature = "gen8", feature = "gen7"))]
1941 Abilities::REFRIGERATE => {
1942 if attacker_choice.move_type == PokemonType::NORMAL {
1943 attacker_choice.move_type = PokemonType::ICE;
1944 attacker_choice.base_power *= 1.2;
1945 }
1946 }
1947 #[cfg(any(feature = "gen6", feature = "gen5", feature = "gen4"))]
1948 Abilities::REFRIGERATE => {
1949 if attacker_choice.move_type == PokemonType::NORMAL {
1950 attacker_choice.move_type = PokemonType::ICE;
1951 attacker_choice.base_power *= 1.3;
1952 }
1953 }
1954 Abilities::SUPREMEOVERLORD => {
1955 let mut boost_amount = 1.0;
1956 boost_amount += 0.1 * attacking_side.num_fainted_pkmn() as f32;
1957 attacker_choice.base_power *= boost_amount;
1958 }
1959 Abilities::ADAPTABILITY => {
1960 if attacking_pkmn.has_type(&attacker_choice.move_type) {
1961 if attacking_pkmn.terastallized
1962 && attacker_choice.move_type == attacking_pkmn.tera_type
1963 && (attacking_pkmn.types.0 == attacker_choice.move_type
1964 || attacking_pkmn.types.1 == attacker_choice.move_type)
1965 {
1966 attacker_choice.base_power *= 2.25 / 2.0;
1967 } else {
1968 attacker_choice.base_power *= 2.0 / 1.5;
1969 }
1970 }
1971 }
1972 Abilities::LONGREACH => {
1973 attacker_choice.flags.contact = false;
1974 }
1975 Abilities::PUREPOWER => {
1976 if attacker_choice.category == MoveCategory::Physical {
1977 attacker_choice.base_power *= 2.0;
1978 }
1979 }
1980 Abilities::TINTEDLENS => {
1981 if type_effectiveness_modifier(
1982 &attacker_choice.move_type,
1983 &defending_side.get_active_immutable(),
1984 ) < 1.0
1985 {
1986 attacker_choice.base_power *= 2.0;
1987 }
1988 }
1989 Abilities::FLAREBOOST => {
1990 if attacking_pkmn.status == PokemonStatus::BURN {
1991 attacker_choice.base_power *= 1.5;
1992 }
1993 }
1994 Abilities::LIQUIDVOICE => {
1995 if attacker_choice.flags.sound {
1996 attacker_choice.move_type = PokemonType::WATER;
1997 }
1998 }
1999 Abilities::NOGUARD => attacker_choice.accuracy = 100.0,
2000 Abilities::TORRENT => {
2001 if attacker_choice.move_type == PokemonType::WATER
2002 && attacking_pkmn.hp <= attacking_pkmn.maxhp / 3
2003 {
2004 attacker_choice.base_power *= 1.5;
2005 }
2006 }
2007 Abilities::SERENEGRACE => {
2008 if let Some(secondaries) = &mut attacker_choice.secondaries {
2009 for secondary in secondaries.iter_mut() {
2010 secondary.chance *= 2.0;
2011 }
2012 }
2013 }
2014 Abilities::TOUGHCLAWS => {
2015 if attacker_choice.flags.contact {
2016 attacker_choice.base_power *= 1.3;
2017 }
2018 }
2019 Abilities::RECKLESS => {
2020 if attacker_choice.crash.is_some() || attacker_choice.recoil.is_some() {
2021 attacker_choice.base_power *= 1.2;
2022 }
2023 }
2024 Abilities::HUGEPOWER => {
2025 if attacker_choice.category == MoveCategory::Physical {
2026 attacker_choice.base_power *= 2.0;
2027 }
2028 }
2029 Abilities::SOLARPOWER => {
2030 if state.weather_is_active(&Weather::SUN) {
2031 attacker_choice.base_power *= 1.5;
2032 }
2033 }
2034 Abilities::FAIRYAURA => {
2035 if attacker_choice.move_type == PokemonType::FAIRY
2036 && defending_side.get_active_immutable().ability != Abilities::AURABREAK
2037 {
2038 attacker_choice.base_power *= 1.33;
2039 }
2040 }
2041 Abilities::NORMALIZE => {
2042 attacker_choice.move_type = PokemonType::NORMAL;
2043 }
2044 Abilities::DARKAURA => {
2045 if attacker_choice.move_type == PokemonType::DARK
2046 && defending_side.get_active_immutable().ability != Abilities::AURABREAK
2047 {
2048 attacker_choice.base_power *= 1.33;
2049 }
2050 }
2051 Abilities::VICTORYSTAR => {
2052 attacker_choice.accuracy *= 1.1;
2053 }
2054 Abilities::COMPOUNDEYES => {
2055 attacker_choice.accuracy *= 1.3;
2056 }
2057 Abilities::STEELWORKER | Abilities::STEELYSPIRIT => {
2058 if attacker_choice.move_type == PokemonType::STEEL {
2059 attacker_choice.base_power *= 1.5;
2060 }
2061 }
2062 #[cfg(any(
2063 feature = "gen8",
2064 feature = "gen7",
2065 feature = "gen6",
2066 feature = "gen5",
2067 feature = "gen4"
2068 ))]
2069 Abilities::TRANSISTOR => {
2070 if attacker_choice.move_type == PokemonType::ELECTRIC {
2071 attacker_choice.base_power *= 1.5;
2072 }
2073 }
2074 #[cfg(any(feature = "gen9"))]
2075 Abilities::TRANSISTOR => {
2076 if attacker_choice.move_type == PokemonType::ELECTRIC {
2077 attacker_choice.base_power *= 1.3;
2078 }
2079 }
2080 Abilities::STENCH => {
2081 let mut already_flinches = false;
2082 if let Some(secondaries) = &mut attacker_choice.secondaries {
2083 for secondary in secondaries.iter() {
2084 if secondary.effect == Effect::VolatileStatus(PokemonVolatileStatus::FLINCH) {
2085 already_flinches = true;
2086 }
2087 }
2088 }
2089 if !already_flinches {
2090 attacker_choice.add_or_create_secondaries(Secondary {
2091 chance: 10.0,
2092 target: MoveTarget::Opponent,
2093 effect: Effect::VolatileStatus(PokemonVolatileStatus::FLINCH),
2094 })
2095 }
2096 }
2097 Abilities::SWARM => {
2098 if attacker_choice.move_type == PokemonType::BUG
2099 && attacking_pkmn.hp <= attacking_pkmn.maxhp / 3
2100 {
2101 attacker_choice.base_power *= 1.5;
2102 }
2103 }
2104 Abilities::GORILLATACTICS => {
2105 if attacker_choice.category == MoveCategory::Physical {
2106 attacker_choice.base_power *= 1.5;
2107 }
2108 }
2109 Abilities::BLAZE => {
2110 if attacker_choice.move_type == PokemonType::FIRE
2111 && attacking_pkmn.hp <= attacking_pkmn.maxhp / 3
2112 {
2113 attacker_choice.base_power *= 1.5;
2114 }
2115 }
2116 Abilities::OVERGROW => {
2117 if attacker_choice.move_type == PokemonType::GRASS
2118 && attacking_pkmn.hp <= attacking_pkmn.maxhp / 3
2119 {
2120 attacker_choice.base_power *= 1.5;
2121 }
2122 }
2123 Abilities::ANALYTIC => {
2124 if !attacker_choice.first_move {
2125 attacker_choice.base_power *= 1.3;
2126 }
2127 }
2128 Abilities::MEGALAUNCHER => {
2129 if attacker_choice.flags.pulse {
2130 attacker_choice.base_power *= 1.5;
2131 };
2132 }
2133 #[cfg(any(feature = "gen9", feature = "gen8", feature = "gen7"))]
2134 Abilities::PIXILATE => {
2135 if attacker_choice.move_type == PokemonType::NORMAL {
2136 attacker_choice.move_type = PokemonType::FAIRY;
2137 attacker_choice.base_power *= 1.2;
2138 }
2139 }
2140 #[cfg(any(feature = "gen6", feature = "gen5", feature = "gen4"))]
2141 Abilities::PIXILATE => {
2142 if attacker_choice.move_type == PokemonType::NORMAL {
2143 attacker_choice.move_type = PokemonType::FAIRY;
2144 attacker_choice.base_power *= 1.3;
2145 }
2146 }
2147 Abilities::DEFEATIST => {
2148 if attacking_pkmn.hp <= attacking_pkmn.maxhp / 2 {
2149 attacker_choice.base_power *= 0.5;
2150 }
2151 }
2152 Abilities::ROCKYPAYLOAD => {
2153 if attacker_choice.move_type == PokemonType::ROCK {
2154 attacker_choice.base_power *= 1.5;
2155 }
2156 }
2157 Abilities::PUNKROCK => {
2158 if attacker_choice.flags.sound {
2159 attacker_choice.base_power *= 1.3;
2160 }
2161 }
2162 Abilities::STRONGJAW => {
2163 if attacker_choice.flags.bite {
2164 attacker_choice.base_power *= 1.5;
2165 }
2166 }
2167 Abilities::BATTERY => {
2168 if attacker_choice.category == MoveCategory::Special {
2169 attacker_choice.base_power *= 1.3;
2170 }
2171 }
2172 Abilities::SHEERFORCE => {
2173 let mut sheer_force_volatile_boosted = false;
2174 if let Some(attacker_volatile_status) = &attacker_choice.volatile_status {
2175 if attacker_volatile_status.volatile_status
2176 != PokemonVolatileStatus::PARTIALLYTRAPPED
2177 && attacker_volatile_status.volatile_status != PokemonVolatileStatus::LOCKEDMOVE
2178 && attacker_volatile_status.volatile_status != PokemonVolatileStatus::SMACKDOWN
2179 {
2180 sheer_force_volatile_boosted = true;
2181 }
2182 }
2183 if attacker_choice.secondaries.is_some() || sheer_force_volatile_boosted {
2184 attacker_choice.base_power *= 1.3;
2185 attacker_choice.secondaries = None;
2186 attacker_choice.volatile_status = None
2187 }
2188 }
2189 Abilities::IRONFIST => {
2190 if attacker_choice.flags.punch {
2191 attacker_choice.base_power *= 1.2;
2192 }
2193 }
2194 Abilities::UNSEENFIST => {
2195 if attacker_choice.flags.contact {
2196 attacker_choice.flags.protect = false
2197 }
2198 }
2199 Abilities::HUSTLE => {
2200 if attacker_choice.category == MoveCategory::Physical {
2201 attacker_choice.base_power *= 1.5;
2202 attacker_choice.accuracy *= 0.80
2203 }
2204 }
2205 Abilities::POISONTOUCH => {
2206 if attacker_choice.flags.contact {
2207 attacker_choice.add_or_create_secondaries(Secondary {
2208 chance: 30.0,
2209 target: MoveTarget::Opponent,
2210 effect: Effect::Status(PokemonStatus::POISON),
2211 })
2212 }
2213 }
2214 Abilities::TOXICCHAIN => {
2215 if attacker_choice.target == MoveTarget::Opponent {
2216 attacker_choice.add_or_create_secondaries(Secondary {
2217 chance: 30.0,
2218 target: MoveTarget::Opponent,
2219 effect: Effect::Status(PokemonStatus::TOXIC),
2220 })
2221 }
2222 }
2223 Abilities::GUTS => {
2224 if attacking_pkmn.status != PokemonStatus::NONE {
2225 attacker_choice.base_power *= 1.5;
2226
2227 if attacking_pkmn.status == PokemonStatus::BURN
2229 && attacker_choice.category == MoveCategory::Physical
2230 {
2231 attacker_choice.base_power *= 2.0;
2232 }
2233 }
2234 }
2235 Abilities::SANDFORCE => {
2236 if state.weather_is_active(&Weather::SAND)
2237 && (attacker_choice.move_type == PokemonType::ROCK
2238 || attacker_choice.move_type == PokemonType::GROUND
2239 || attacker_choice.move_type == PokemonType::STEEL)
2240 {
2241 attacker_choice.base_power *= 1.3;
2242 }
2243 }
2244 Abilities::TOXICBOOST => {
2245 if attacking_pkmn.status == PokemonStatus::POISON
2246 || attacking_pkmn.status == PokemonStatus::TOXIC
2247 {
2248 attacker_choice.base_power *= 1.5;
2249 }
2250 }
2251 _ => {}
2252 }
2253}
2254
2255pub fn ability_modify_attack_against(
2256 state: &State,
2257 attacker_choice: &mut Choice,
2258 defender_choice: &Choice,
2259 attacking_side_ref: &SideReference,
2260) {
2261 let (attacking_side, defending_side) = state.get_both_sides_immutable(attacking_side_ref);
2262 let attacking_pkmn = attacking_side.get_active_immutable();
2263 let target_pkmn = defending_side.get_active_immutable();
2264 if target_pkmn.ability == Abilities::NEUTRALIZINGGAS
2265 || attacker_choice.target == MoveTarget::User
2266 {
2267 return;
2268 }
2269 if (attacking_pkmn.ability == Abilities::MOLDBREAKER
2270 || attacker_choice.move_id == Choices::MOONGEISTBEAM
2271 || attacker_choice.move_id == Choices::PHOTONGEYSER
2272 || attacker_choice.move_id == Choices::SUNSTEELSTRIKE
2273 || (attacking_pkmn.ability == Abilities::MYCELIUMMIGHT
2274 && attacker_choice.category == MoveCategory::Status)
2275 || attacking_pkmn.ability == Abilities::TERAVOLT
2276 || attacking_pkmn.ability == Abilities::TURBOBLAZE)
2277 && mold_breaker_ignores(&target_pkmn.ability)
2278 {
2279 return;
2280 }
2281
2282 match target_pkmn.ability {
2283 Abilities::TABLETSOFRUIN => {
2284 if attacker_choice.category == MoveCategory::Physical {
2285 attacker_choice.base_power *= 0.75;
2286 }
2287 }
2288 Abilities::VESSELOFRUIN => {
2289 if attacker_choice.category == MoveCategory::Special {
2290 attacker_choice.base_power *= 0.75;
2291 }
2292 }
2293 Abilities::ARMORTAIL => {
2294 if attacker_choice.priority > 0 && attacker_choice.category != MoveCategory::Status {
2295 attacker_choice.remove_all_effects();
2296 }
2297 }
2298 Abilities::SOUNDPROOF => {
2299 if attacker_choice.flags.sound {
2300 attacker_choice.remove_all_effects();
2301 attacker_choice.accuracy = 0.0;
2302 }
2303 }
2304 Abilities::POISONPOINT => {
2305 if attacker_choice.flags.contact {
2306 attacker_choice.add_or_create_secondaries(Secondary {
2307 chance: 33.0,
2308 target: MoveTarget::User,
2309 effect: Effect::Status(PokemonStatus::POISON),
2310 })
2311 }
2312 }
2313 Abilities::BULLETPROOF => {
2314 if attacker_choice.flags.bullet {
2315 attacker_choice.remove_all_effects();
2316 attacker_choice.accuracy = 0.0;
2317 }
2318 }
2319 Abilities::MULTISCALE => {
2320 if target_pkmn.hp == target_pkmn.maxhp {
2321 attacker_choice.base_power /= 2.0;
2322 }
2323 }
2324 Abilities::LIGHTNINGROD => {
2325 if attacker_choice.move_type == PokemonType::ELECTRIC {
2326 attacker_choice.remove_all_effects();
2327 attacker_choice.accuracy = 100.0;
2328 attacker_choice.target = MoveTarget::Opponent;
2329 attacker_choice.boost = Some(Boost {
2330 boosts: StatBoosts {
2331 attack: 0,
2332 defense: 0,
2333 special_attack: 1,
2334 special_defense: 0,
2335 speed: 0,
2336 accuracy: 0,
2337 },
2338 target: MoveTarget::Opponent,
2339 });
2340 attacker_choice.category = MoveCategory::Status;
2341 }
2342 }
2343 Abilities::EARTHEATER => {
2344 if attacker_choice.move_type == PokemonType::GROUND {
2345 attacker_choice.remove_all_effects();
2346 attacker_choice.base_power = 0.0;
2347 attacker_choice.heal = Some(Heal {
2348 target: MoveTarget::Opponent,
2349 amount: 0.25,
2350 });
2351 attacker_choice.category = MoveCategory::Status;
2352 }
2353 }
2354 Abilities::STEAMENGINE => {
2355 if attacker_choice.move_type == PokemonType::WATER
2356 || attacker_choice.move_type == PokemonType::FIRE
2357 {
2358 attacker_choice.add_or_create_secondaries(Secondary {
2359 chance: 100.0,
2360 target: MoveTarget::Opponent,
2361 effect: Effect::Boost(StatBoosts {
2362 attack: 0,
2363 defense: 0,
2364 special_attack: 0,
2365 special_defense: 0,
2366 speed: 6,
2367 accuracy: 0,
2368 }),
2369 });
2370 }
2371 }
2372 Abilities::THERMALEXCHANGE => {
2373 if attacker_choice.move_type == PokemonType::FIRE {
2374 attacker_choice.add_or_create_secondaries(Secondary {
2375 chance: 100.0,
2376 target: MoveTarget::Opponent,
2377 effect: Effect::Boost(StatBoosts {
2378 attack: 1,
2379 defense: 0,
2380 special_attack: 0,
2381 special_defense: 0,
2382 speed: 0,
2383 accuracy: 0,
2384 }),
2385 });
2386 }
2387 }
2388 #[cfg(any(feature = "gen9", feature = "gen8", feature = "gen7"))]
2389 Abilities::WEAKARMOR => {
2390 if attacker_choice.category == MoveCategory::Physical {
2391 attacker_choice.add_or_create_secondaries(Secondary {
2392 chance: 100.0,
2393 target: MoveTarget::Opponent,
2394 effect: Effect::Boost(StatBoosts {
2395 attack: 0,
2396 defense: -1,
2397 special_attack: 0,
2398 special_defense: 0,
2399 speed: 2,
2400 accuracy: 0,
2401 }),
2402 });
2403 }
2404 }
2405 #[cfg(any(feature = "gen6", feature = "gen5", feature = "gen4"))]
2406 Abilities::WEAKARMOR => {
2407 if attacker_choice.category == MoveCategory::Physical {
2408 attacker_choice.add_or_create_secondaries(Secondary {
2409 chance: 100.0,
2410 target: MoveTarget::Opponent,
2411 effect: Effect::Boost(StatBoosts {
2412 attack: 0,
2413 defense: -1,
2414 special_attack: 0,
2415 special_defense: 0,
2416 speed: 1,
2417 accuracy: 0,
2418 }),
2419 });
2420 }
2421 }
2422 Abilities::QUEENLYMAJESTY => {
2423 if attacker_choice.priority > 0 {
2424 attacker_choice.remove_all_effects();
2425 attacker_choice.accuracy = 0.0;
2426 }
2427 }
2428 Abilities::SAPSIPPER => {
2429 if attacker_choice.move_type == PokemonType::GRASS {
2430 attacker_choice.remove_all_effects();
2431 attacker_choice.accuracy = 100.0;
2432 attacker_choice.target = MoveTarget::Opponent;
2433 attacker_choice.boost = Some(Boost {
2434 boosts: StatBoosts {
2435 attack: 1,
2436 defense: 0,
2437 special_attack: 0,
2438 special_defense: 0,
2439 speed: 0,
2440 accuracy: 0,
2441 },
2442 target: MoveTarget::Opponent,
2443 });
2444 attacker_choice.category = MoveCategory::Status;
2445 }
2446 }
2447 Abilities::SHADOWSHIELD => {
2448 if target_pkmn.hp == target_pkmn.maxhp {
2449 attacker_choice.base_power /= 2.0;
2450 }
2451 }
2452 Abilities::NOGUARD => {
2453 attacker_choice.accuracy = 100.0;
2454 }
2455 Abilities::MARVELSCALE => {
2456 if target_pkmn.status != PokemonStatus::NONE
2457 && attacker_choice.category == MoveCategory::Physical
2458 {
2459 attacker_choice.base_power /= 1.5;
2460 }
2461 }
2462 #[cfg(feature = "gen3")]
2463 Abilities::EFFECTSPORE => {
2464 if attacker_choice.flags.contact {
2465 attacker_choice.add_or_create_secondaries(Secondary {
2466 chance: 3.30,
2467 target: MoveTarget::User,
2468 effect: Effect::Status(PokemonStatus::POISON),
2469 });
2470 attacker_choice.add_or_create_secondaries(Secondary {
2471 chance: 3.30,
2472 target: MoveTarget::User,
2473 effect: Effect::Status(PokemonStatus::PARALYZE),
2474 });
2475 attacker_choice.add_or_create_secondaries(Secondary {
2476 chance: 3.30,
2477 target: MoveTarget::User,
2478 effect: Effect::Status(PokemonStatus::SLEEP),
2479 });
2480 }
2481 }
2482
2483 #[cfg(not(feature = "gen3"))]
2484 Abilities::EFFECTSPORE => {
2485 if attacker_choice.flags.contact {
2486 attacker_choice.add_or_create_secondaries(Secondary {
2487 chance: 9.0,
2488 target: MoveTarget::User,
2489 effect: Effect::Status(PokemonStatus::POISON),
2490 });
2491 attacker_choice.add_or_create_secondaries(Secondary {
2492 chance: 10.0,
2493 target: MoveTarget::User,
2494 effect: Effect::Status(PokemonStatus::PARALYZE),
2495 });
2496 attacker_choice.add_or_create_secondaries(Secondary {
2497 chance: 11.0,
2498 target: MoveTarget::User,
2499 effect: Effect::Status(PokemonStatus::SLEEP),
2500 });
2501 }
2502 }
2503 Abilities::FLAMEBODY => {
2504 if attacker_choice.flags.contact {
2505 attacker_choice.add_or_create_secondaries(Secondary {
2506 chance: 30.0,
2507 target: MoveTarget::User,
2508 effect: Effect::Status(PokemonStatus::BURN),
2509 });
2510 }
2511 }
2512 Abilities::GOOEY => {
2513 if attacker_choice.flags.contact {
2514 attacker_choice.add_or_create_secondaries(Secondary {
2515 chance: 100.0,
2516 target: MoveTarget::User,
2517 effect: Effect::Boost(StatBoosts {
2518 attack: 0,
2519 defense: 0,
2520 special_attack: 0,
2521 special_defense: 0,
2522 speed: -1,
2523 accuracy: 0,
2524 }),
2525 })
2526 }
2527 }
2528 Abilities::MOTORDRIVE => {
2529 if attacker_choice.move_type == PokemonType::ELECTRIC {
2530 attacker_choice.remove_all_effects();
2531 attacker_choice.accuracy = 100.0;
2532 attacker_choice.target = MoveTarget::Opponent;
2533 attacker_choice.boost = Some(Boost {
2534 boosts: StatBoosts {
2535 attack: 0,
2536 defense: 0,
2537 special_attack: 0,
2538 special_defense: 0,
2539 speed: 1,
2540 accuracy: 0,
2541 },
2542 target: MoveTarget::Opponent,
2543 });
2544 attacker_choice.category = MoveCategory::Status;
2545 }
2546 }
2547 Abilities::WINDRIDER => {
2548 if attacker_choice.flags.wind {
2549 attacker_choice.remove_all_effects();
2550 attacker_choice.accuracy = 100.0;
2551 attacker_choice.target = MoveTarget::Opponent;
2552 attacker_choice.boost = Some(Boost {
2553 boosts: StatBoosts {
2554 attack: 1,
2555 defense: 0,
2556 special_attack: 0,
2557 special_defense: 0,
2558 speed: 0,
2559 accuracy: 0,
2560 },
2561 target: MoveTarget::Opponent,
2562 });
2563 attacker_choice.category = MoveCategory::Status;
2564 }
2565 }
2566 Abilities::SUCTIONCUPS => {
2567 attacker_choice.flags.drag = false;
2568 }
2569 Abilities::WONDERGUARD => {
2570 if attacker_choice.category != MoveCategory::Status
2571 && type_effectiveness_modifier(&attacker_choice.move_type, &target_pkmn) <= 1.0
2572 {
2573 attacker_choice.remove_all_effects();
2574 attacker_choice.base_power = 0.0;
2575 }
2576 }
2577 Abilities::FAIRYAURA => {
2578 if attacker_choice.move_type == PokemonType::FAIRY {
2579 attacker_choice.base_power *= 1.33;
2580 }
2581 }
2582 Abilities::LEVITATE => {
2583 if attacker_choice.move_type == PokemonType::GROUND
2584 && attacker_choice.target == MoveTarget::Opponent
2585 && attacker_choice.move_id != Choices::THOUSANDARROWS
2586 {
2587 attacker_choice.base_power = 0.0;
2588 }
2589 }
2590 Abilities::STATIC => {
2591 if attacker_choice.flags.contact {
2592 attacker_choice.add_or_create_secondaries(Secondary {
2593 chance: 30.0,
2594 target: MoveTarget::User,
2595 effect: Effect::Status(PokemonStatus::PARALYZE),
2596 })
2597 }
2598 }
2599 Abilities::WONDERSKIN => {
2600 if attacker_choice.category == MoveCategory::Status && attacker_choice.accuracy > 50.0 {
2601 attacker_choice.accuracy = 50.0;
2602 }
2603 }
2604 Abilities::THICKFAT => {
2605 if attacker_choice.move_type == PokemonType::FIRE
2606 || attacker_choice.move_type == PokemonType::ICE
2607 {
2608 attacker_choice.base_power /= 2.0;
2609 }
2610 }
2611 Abilities::FLASHFIRE => {
2612 if attacker_choice.move_type == PokemonType::FIRE {
2613 attacker_choice.remove_all_effects();
2614 attacker_choice.volatile_status = Some(VolatileStatus {
2615 target: MoveTarget::Opponent,
2616 volatile_status: PokemonVolatileStatus::FLASHFIRE,
2617 });
2618 }
2619 }
2620 Abilities::WELLBAKEDBODY => {
2621 if attacker_choice.move_type == PokemonType::FIRE {
2622 attacker_choice.remove_all_effects();
2623 attacker_choice.boost = Some(Boost {
2624 boosts: StatBoosts {
2625 attack: 0,
2626 defense: 2,
2627 special_attack: 0,
2628 special_defense: 0,
2629 speed: 0,
2630 accuracy: 0,
2631 },
2632 target: MoveTarget::Opponent,
2633 });
2634 }
2635 }
2636 Abilities::DAZZLING => {
2637 if attacker_choice.priority > 0 {
2638 attacker_choice.accuracy = 0.0;
2639 }
2640 }
2641 Abilities::LIQUIDOOZE => {
2642 if let Some(drain) = attacker_choice.drain {
2643 attacker_choice.drain = Some(-1.0 * drain);
2644 }
2645 }
2646 Abilities::PRISMARMOR => {
2647 if type_effectiveness_modifier(&attacker_choice.move_type, &target_pkmn) > 1.0 {
2648 attacker_choice.base_power *= 0.75;
2649 }
2650 }
2651 Abilities::HEATPROOF => {
2652 if attacker_choice.move_type == PokemonType::FIRE {
2653 attacker_choice.base_power *= 0.5;
2654 }
2655 }
2656 Abilities::SHIELDDUST => {
2657 if let Some(secondaries) = &mut attacker_choice.secondaries {
2658 for secondary in secondaries.iter_mut() {
2659 if secondary.target == MoveTarget::Opponent {
2660 secondary.chance = 0.0;
2661 }
2662 }
2663 }
2664 }
2665 Abilities::GRASSPELT => {
2666 if state.terrain_is_active(&Terrain::GRASSYTERRAIN)
2667 && attacker_choice.category == MoveCategory::Physical
2668 {
2669 attacker_choice.base_power /= 1.5;
2670 }
2671 }
2672 Abilities::FILTER => {
2673 if type_effectiveness_modifier(&attacker_choice.move_type, &target_pkmn) > 1.0 {
2674 attacker_choice.base_power *= 0.75;
2675 }
2676 }
2677 Abilities::FURCOAT => {
2678 if attacker_choice.category == MoveCategory::Physical {
2679 attacker_choice.base_power *= 0.5;
2680 }
2681 }
2682 Abilities::TANGLINGHAIR => {
2683 if attacker_choice.flags.contact {
2684 attacker_choice.add_or_create_secondaries(Secondary {
2685 chance: 100.0,
2686 target: MoveTarget::User,
2687 effect: Effect::Boost(StatBoosts {
2688 attack: 0,
2689 defense: 0,
2690 special_attack: 0,
2691 special_defense: 0,
2692 speed: -1,
2693 accuracy: 0,
2694 }),
2695 })
2696 }
2697 }
2698 Abilities::MAGICBOUNCE => {
2699 if attacker_choice.flags.reflectable {
2700 attacker_choice.target = MoveTarget::User;
2701 if let Some(side_condition) = &mut attacker_choice.side_condition {
2702 if side_condition.target == MoveTarget::Opponent {
2703 side_condition.target = MoveTarget::User;
2704 }
2705 }
2706 if let Some(status) = &mut attacker_choice.status {
2707 if status.target == MoveTarget::Opponent {
2708 status.target = MoveTarget::User;
2709 }
2710 }
2711 if let Some(volatile_status) = &mut attacker_choice.volatile_status {
2712 if volatile_status.target == MoveTarget::Opponent {
2713 volatile_status.target = MoveTarget::User;
2714 }
2715 }
2716 }
2717 }
2718 Abilities::STORMDRAIN => {
2719 if attacker_choice.move_type == PokemonType::WATER {
2720 attacker_choice.remove_all_effects();
2721 attacker_choice.accuracy = 100.0;
2722 attacker_choice.target = MoveTarget::Opponent;
2723 attacker_choice.boost = Some(Boost {
2724 boosts: StatBoosts {
2725 attack: 0,
2726 defense: 0,
2727 special_attack: 1,
2728 special_defense: 0,
2729 speed: 0,
2730 accuracy: 0,
2731 },
2732 target: MoveTarget::Opponent,
2733 });
2734 attacker_choice.category = MoveCategory::Status;
2735 }
2736 }
2737 Abilities::WATERCOMPACTION => {
2738 if attacker_choice.move_type == PokemonType::WATER {
2739 attacker_choice.add_or_create_secondaries(Secondary {
2740 chance: 100.0,
2741 target: MoveTarget::Opponent,
2742 effect: Effect::Boost(StatBoosts {
2743 attack: 0,
2744 defense: 2,
2745 special_attack: 0,
2746 special_defense: 0,
2747 speed: 0,
2748 accuracy: 0,
2749 }),
2750 });
2751 }
2752 }
2753 Abilities::JUSTIFIED => {
2754 if attacker_choice.move_type == PokemonType::DARK {
2755 attacker_choice.add_or_create_secondaries(Secondary {
2756 chance: 100.0,
2757 target: MoveTarget::Opponent,
2758 effect: Effect::Boost(StatBoosts {
2759 attack: 1,
2760 defense: 0,
2761 special_attack: 0,
2762 special_defense: 0,
2763 speed: 0,
2764 accuracy: 0,
2765 }),
2766 })
2767 }
2768 }
2769 Abilities::ICESCALES => {
2770 if attacker_choice.category == MoveCategory::Special {
2771 attacker_choice.base_power *= 0.5;
2772 }
2773 }
2774 Abilities::WATERABSORB => {
2775 if attacker_choice.move_type == PokemonType::WATER {
2776 attacker_choice.remove_all_effects();
2777 attacker_choice.base_power = 0.0;
2778 attacker_choice.heal = Some(Heal {
2779 target: MoveTarget::Opponent,
2780 amount: 0.25,
2781 });
2782 attacker_choice.category = MoveCategory::Status;
2783 }
2784 }
2785 Abilities::DRYSKIN => {
2786 if attacker_choice.move_type == PokemonType::WATER {
2787 attacker_choice.remove_all_effects();
2788 attacker_choice.base_power = 0.0;
2789 attacker_choice.heal = Some(Heal {
2790 target: MoveTarget::Opponent,
2791 amount: 0.25,
2792 });
2793 attacker_choice.category = MoveCategory::Status;
2794 } else if attacker_choice.move_type == PokemonType::FIRE {
2795 attacker_choice.base_power *= 1.25;
2796 }
2797 }
2798 Abilities::FLUFFY => {
2799 if attacker_choice.flags.contact {
2800 attacker_choice.base_power *= 0.5;
2801 }
2802 if attacker_choice.move_type == PokemonType::FIRE {
2803 attacker_choice.base_power *= 2.0;
2804 }
2805 }
2806 Abilities::PUNKROCK => {
2807 if attacker_choice.flags.sound {
2808 attacker_choice.base_power /= 2.0;
2809 }
2810 }
2811 Abilities::DAMP => {
2812 if [
2813 Choices::SELFDESTRUCT,
2814 Choices::EXPLOSION,
2815 Choices::MINDBLOWN,
2816 Choices::MISTYEXPLOSION,
2817 ]
2818 .contains(&attacker_choice.move_id)
2819 {
2820 attacker_choice.accuracy = 0.0;
2821 attacker_choice.heal = None;
2822 }
2823 }
2824 Abilities::VOLTABSORB => {
2825 #[cfg(feature = "gen3")]
2826 let activate = attacker_choice.move_type == PokemonType::ELECTRIC
2827 && attacker_choice.category != MoveCategory::Status;
2828
2829 #[cfg(not(feature = "gen3"))]
2830 let activate = attacker_choice.move_type == PokemonType::ELECTRIC;
2831
2832 if activate {
2833 attacker_choice.remove_all_effects();
2834 attacker_choice.accuracy = 100.0;
2835 attacker_choice.base_power = 0.0;
2836 attacker_choice.heal = Some(Heal {
2837 target: MoveTarget::Opponent,
2838 amount: 0.25,
2839 });
2840 attacker_choice.category = MoveCategory::Status;
2841 }
2842 }
2843 Abilities::SOLIDROCK => {
2844 if type_effectiveness_modifier(&attacker_choice.move_type, &target_pkmn) > 1.0 {
2845 attacker_choice.base_power *= 0.75;
2846 }
2847 }
2848 Abilities::OVERCOAT => {
2849 if attacker_choice.flags.powder {
2850 attacker_choice.remove_all_effects();
2851 attacker_choice.accuracy = 0.0
2852 }
2853 }
2854 Abilities::GOODASGOLD => {
2855 if attacker_choice.category == MoveCategory::Status
2858 && attacker_choice.target == MoveTarget::Opponent
2859 && ![
2860 Choices::STEALTHROCK,
2861 Choices::STICKYWEB,
2862 Choices::TOXICSPIKES,
2863 Choices::SPIKES,
2864 ]
2865 .contains(&attacker_choice.move_id)
2866 {
2867 attacker_choice.remove_all_effects();
2868 }
2869 }
2870 Abilities::RATTLED => {
2871 if attacker_choice.move_type == PokemonType::BUG
2872 || attacker_choice.move_type == PokemonType::DARK
2873 || attacker_choice.move_type == PokemonType::GHOST
2874 {
2875 attacker_choice.add_or_create_secondaries(Secondary {
2876 chance: 100.0,
2877 target: MoveTarget::Opponent,
2878 effect: Effect::Boost(StatBoosts {
2879 attack: 0,
2880 defense: 0,
2881 special_attack: 0,
2882 special_defense: 0,
2883 speed: 1,
2884 accuracy: 0,
2885 }),
2886 });
2887 }
2888 }
2889 Abilities::WATERBUBBLE => {
2890 if attacker_choice.move_type == PokemonType::FIRE {
2891 attacker_choice.base_power /= 2.0;
2892 }
2893 }
2894 Abilities::PURIFYINGSALT => {
2895 if attacker_choice.move_type == PokemonType::GHOST {
2896 attacker_choice.base_power /= 2.0;
2897 }
2898 }
2899 _ => {}
2900 }
2901}