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