1use super::abilities::{
2 ability_after_damage_hit, ability_before_move, ability_end_of_turn,
3 ability_modify_attack_against, ability_modify_attack_being_used, ability_on_switch_in,
4 ability_on_switch_out, Abilities,
5};
6use super::choice_effects::{
7 charge_choice_to_volatile, choice_after_damage_hit, choice_before_move, choice_hazard_clear,
8 choice_special_effect, modify_choice,
9};
10use crate::choices::{
11 Boost, Choices, Effect, Heal, MoveTarget, MultiHitMove, Secondary, SideCondition, StatBoosts,
12 Status, VolatileStatus, MOVES,
13};
14use crate::instruction::{
15 ApplyVolatileStatusInstruction, BoostInstruction, ChangeDamageDealtDamageInstruction,
16 ChangeDamageDealtMoveCategoryInstruction, ChangeItemInstruction,
17 ChangeSideConditionInstruction, ChangeTerrain, ChangeType,
18 ChangeVolatileStatusDurationInstruction, ChangeWeather, DecrementRestTurnsInstruction,
19 DecrementWishInstruction, HealInstruction, RemoveVolatileStatusInstruction,
20 SetSecondMoveSwitchOutMoveInstruction, SetSleepTurnsInstruction, ToggleBatonPassingInstruction,
21 ToggleDamageDealtHitSubstituteInstruction, ToggleShedTailingInstruction,
22 ToggleTrickRoomInstruction,
23};
24use crate::instruction::{ChangeAbilityInstruction, ToggleTerastallizedInstruction};
25use crate::instruction::{DecrementFutureSightInstruction, FormeChangeInstruction};
26use crate::instruction::{DecrementPPInstruction, SetLastUsedMoveInstruction};
27
28use super::damage_calc::calculate_futuresight_damage;
29use super::damage_calc::{calculate_damage, type_effectiveness_modifier, DamageRolls};
30use super::items::{
31 item_before_move, item_end_of_turn, item_modify_attack_against, item_modify_attack_being_used,
32 item_on_switch_in, Items,
33};
34use super::state::{MoveChoice, PokemonVolatileStatus, Terrain, Weather};
35use crate::choices::{Choice, MoveCategory};
36use crate::instruction::{
37 ChangeStatusInstruction, DamageInstruction, Instruction, StateInstructions, SwitchInstruction,
38};
39use crate::state::{
40 LastUsedMove, PokemonBoostableStat, PokemonIndex, PokemonMoveIndex, PokemonSideCondition,
41 PokemonStatus, PokemonType, Side, SideMovesFirst, SideReference, State,
42};
43use std::cmp;
44
45#[cfg(feature = "terastallization")]
46use crate::choices::MultiAccuracyMove;
47
48#[cfg(any(feature = "gen3", feature = "gen4", feature = "gen5", feature = "gen6"))]
49pub const BASE_CRIT_CHANCE: f32 = 1.0 / 16.0;
50
51#[cfg(any(feature = "gen7", feature = "gen8", feature = "gen9"))]
52pub const BASE_CRIT_CHANCE: f32 = 1.0 / 24.0;
53
54#[cfg(any(feature = "gen3", feature = "gen4"))]
55pub const MAX_SLEEP_TURNS: i8 = 4;
56
57#[cfg(any(
58 feature = "gen5",
59 feature = "gen6",
60 feature = "gen7",
61 feature = "gen8",
62 feature = "gen9"
63))]
64pub const MAX_SLEEP_TURNS: i8 = 3;
65
66#[cfg(any(feature = "gen7", feature = "gen8", feature = "gen9"))]
67pub const HIT_SELF_IN_CONFUSION_CHANCE: f32 = 1.0 / 3.0;
68
69#[cfg(any(feature = "gen3", feature = "gen4", feature = "gen5", feature = "gen6"))]
70pub const HIT_SELF_IN_CONFUSION_CHANCE: f32 = 1.0 / 2.0;
71
72#[cfg(any(
73 feature = "gen5",
74 feature = "gen6",
75 feature = "gen7",
76 feature = "gen8",
77 feature = "gen9"
78))]
79pub const CONSECUTIVE_PROTECT_CHANCE: f32 = 1.0 / 3.0;
80
81#[cfg(any(feature = "gen3", feature = "gen4"))]
82pub const CONSECUTIVE_PROTECT_CHANCE: f32 = 1.0 / 2.0;
83
84pub const SIDE_CONDITION_DURATION: i8 = 5;
85pub const TAILWIND_DURATION: i8 = 4;
86
87const PROTECT_VOLATILES: [PokemonVolatileStatus; 6] = [
88 PokemonVolatileStatus::PROTECT,
89 PokemonVolatileStatus::BANEFULBUNKER,
90 PokemonVolatileStatus::BURNINGBULWARK,
91 PokemonVolatileStatus::SPIKYSHIELD,
92 PokemonVolatileStatus::SILKTRAP,
93 PokemonVolatileStatus::ENDURE,
94];
95
96fn chance_to_wake_up(turns_asleep: i8) -> f32 {
97 if turns_asleep == 0 {
98 0.0
99 } else {
100 1.0 / (1 + MAX_SLEEP_TURNS - turns_asleep) as f32
101 }
102}
103
104fn set_last_used_move_as_switch(
105 side: &mut Side,
106 new_pokemon_index: PokemonIndex,
107 switching_side_ref: SideReference,
108 incoming_instructions: &mut StateInstructions,
109) {
110 incoming_instructions
111 .instruction_list
112 .push(Instruction::SetLastUsedMove(SetLastUsedMoveInstruction {
113 side_ref: switching_side_ref,
114 last_used_move: LastUsedMove::Switch(new_pokemon_index),
115 previous_last_used_move: side.last_used_move,
116 }));
117 side.last_used_move = LastUsedMove::Switch(new_pokemon_index);
118}
119
120fn set_last_used_move_as_move(
121 side: &mut Side,
122 used_move: PokemonMoveIndex,
123 switching_side_ref: SideReference,
124 incoming_instructions: &mut StateInstructions,
125) {
126 if side
127 .volatile_statuses
128 .contains(&PokemonVolatileStatus::FLINCH)
129 {
130 if matches!(side.last_used_move, LastUsedMove::Switch(_)) {
133 incoming_instructions
134 .instruction_list
135 .push(Instruction::SetLastUsedMove(SetLastUsedMoveInstruction {
136 side_ref: switching_side_ref,
137 last_used_move: LastUsedMove::None,
138 previous_last_used_move: side.last_used_move,
139 }));
140 side.last_used_move = LastUsedMove::None;
141 }
142 return;
143 }
144 match side.last_used_move {
145 LastUsedMove::Move(last_used_move) => {
146 if last_used_move == used_move {
147 return;
148 }
149 }
150 _ => {}
151 }
152 incoming_instructions
153 .instruction_list
154 .push(Instruction::SetLastUsedMove(SetLastUsedMoveInstruction {
155 side_ref: switching_side_ref,
156 last_used_move: LastUsedMove::Move(used_move),
157 previous_last_used_move: side.last_used_move,
158 }));
159 side.last_used_move = LastUsedMove::Move(used_move);
160}
161
162fn generate_instructions_from_switch(
163 state: &mut State,
164 new_pokemon_index: PokemonIndex,
165 switching_side_ref: SideReference,
166 incoming_instructions: &mut StateInstructions,
167) {
168 let should_last_used_move = state.use_last_used_move;
169 state.apply_instructions(&incoming_instructions.instruction_list);
170
171 let (side, opposite_side) = state.get_both_sides(&switching_side_ref);
172 if side.force_switch {
173 side.force_switch = false;
174 match switching_side_ref {
175 SideReference::SideOne => {
176 incoming_instructions
177 .instruction_list
178 .push(Instruction::ToggleSideOneForceSwitch);
179 }
180 SideReference::SideTwo => {
181 incoming_instructions
182 .instruction_list
183 .push(Instruction::ToggleSideTwoForceSwitch);
184 }
185 }
186 }
187
188 let mut baton_passing = false;
189 if side.baton_passing {
190 baton_passing = true;
191 side.baton_passing = false;
192 match switching_side_ref {
193 SideReference::SideOne => {
194 incoming_instructions
195 .instruction_list
196 .push(Instruction::ToggleBatonPassing(
197 ToggleBatonPassingInstruction {
198 side_ref: SideReference::SideOne,
199 },
200 ));
201 }
202 SideReference::SideTwo => {
203 incoming_instructions
204 .instruction_list
205 .push(Instruction::ToggleBatonPassing(
206 ToggleBatonPassingInstruction {
207 side_ref: SideReference::SideTwo,
208 },
209 ));
210 }
211 }
212 }
213
214 let mut shed_tailing = false;
215 if side.shed_tailing {
216 shed_tailing = true;
217 side.shed_tailing = false;
218 match switching_side_ref {
219 SideReference::SideOne => {
220 incoming_instructions
221 .instruction_list
222 .push(Instruction::ToggleShedTailing(
223 ToggleShedTailingInstruction {
224 side_ref: SideReference::SideOne,
225 },
226 ));
227 }
228 SideReference::SideTwo => {
229 incoming_instructions
230 .instruction_list
231 .push(Instruction::ToggleShedTailing(
232 ToggleShedTailingInstruction {
233 side_ref: SideReference::SideTwo,
234 },
235 ));
236 }
237 }
238 }
239
240 #[cfg(feature = "gen5")]
241 if side.get_active_immutable().status == PokemonStatus::SLEEP {
242 let current_active_index = side.active_index;
243 let active = side.get_active();
244 if active.rest_turns > 0 {
245 let current_rest_turns = active.rest_turns;
246 incoming_instructions
247 .instruction_list
248 .push(Instruction::SetRestTurns(SetSleepTurnsInstruction {
249 side_ref: switching_side_ref,
250 pokemon_index: current_active_index,
251 new_turns: 3,
252 previous_turns: current_rest_turns,
253 }));
254 active.rest_turns = 3
255 } else {
256 let current_sleep_turns = active.sleep_turns;
257 incoming_instructions
258 .instruction_list
259 .push(Instruction::SetSleepTurns(SetSleepTurnsInstruction {
260 side_ref: switching_side_ref,
261 pokemon_index: current_active_index,
262 new_turns: 0,
263 previous_turns: current_sleep_turns,
264 }));
265 active.sleep_turns = 0
266 }
267 }
268
269 if opposite_side
270 .volatile_statuses
271 .contains(&PokemonVolatileStatus::PARTIALLYTRAPPED)
272 {
273 incoming_instructions
274 .instruction_list
275 .push(Instruction::RemoveVolatileStatus(
276 RemoveVolatileStatusInstruction {
277 side_ref: switching_side_ref.get_other_side(),
278 volatile_status: PokemonVolatileStatus::PARTIALLYTRAPPED,
279 },
280 ));
281 opposite_side
282 .volatile_statuses
283 .remove(&PokemonVolatileStatus::PARTIALLYTRAPPED);
284 }
285
286 state.re_enable_disabled_moves(
287 &switching_side_ref,
288 &mut incoming_instructions.instruction_list,
289 );
290 state.remove_volatile_statuses_on_switch(
291 &switching_side_ref,
292 &mut incoming_instructions.instruction_list,
293 baton_passing,
294 shed_tailing,
295 );
296 state.reset_toxic_count(
297 &switching_side_ref,
298 &mut incoming_instructions.instruction_list,
299 );
300 if !baton_passing {
301 state.reset_boosts(
302 &switching_side_ref,
303 &mut incoming_instructions.instruction_list,
304 );
305 }
306
307 ability_on_switch_out(state, &switching_side_ref, incoming_instructions);
308
309 let switch_instruction = Instruction::Switch(SwitchInstruction {
310 side_ref: switching_side_ref,
311 previous_index: state.get_side(&switching_side_ref).active_index,
312 next_index: new_pokemon_index,
313 });
314
315 let side = state.get_side(&switching_side_ref);
316 side.active_index = new_pokemon_index;
317 incoming_instructions
318 .instruction_list
319 .push(switch_instruction);
320
321 if should_last_used_move {
322 set_last_used_move_as_switch(
323 side,
324 new_pokemon_index,
325 switching_side_ref,
326 incoming_instructions,
327 );
328 }
329
330 if side.side_conditions.healing_wish > 0 {
331 #[cfg(any(feature = "gen8", feature = "gen9"))]
332 let mut healing_wish_consumed = false;
333
334 #[cfg(any(
335 feature = "gen3",
336 feature = "gen4",
337 feature = "gen5",
338 feature = "gen6",
339 feature = "gen7"
340 ))]
341 let mut healing_wish_consumed = true;
342
343 let switched_in_pkmn = side.get_active();
344 if switched_in_pkmn.hp < switched_in_pkmn.maxhp {
345 let heal_amount = switched_in_pkmn.maxhp - switched_in_pkmn.hp;
346 let heal_instruction = Instruction::Heal(HealInstruction {
347 side_ref: switching_side_ref,
348 heal_amount,
349 });
350 incoming_instructions
351 .instruction_list
352 .push(heal_instruction);
353 switched_in_pkmn.hp += heal_amount;
354 healing_wish_consumed = true;
355 }
356 if switched_in_pkmn.status != PokemonStatus::NONE {
357 add_remove_status_instructions(
358 incoming_instructions,
359 new_pokemon_index,
360 switching_side_ref,
361 side,
362 );
363 healing_wish_consumed = true;
364 }
365
366 if healing_wish_consumed {
367 incoming_instructions
368 .instruction_list
369 .push(Instruction::ChangeSideCondition(
370 ChangeSideConditionInstruction {
371 side_ref: switching_side_ref,
372 side_condition: PokemonSideCondition::HealingWish,
373 amount: -1 * side.side_conditions.healing_wish,
374 },
375 ));
376 side.side_conditions.healing_wish = 0;
377 }
378 }
379
380 let active = side.get_active_immutable();
381 if active.item != Items::HEAVYDUTYBOOTS {
382 let switched_in_pkmn = side.get_active_immutable();
383 if side.side_conditions.sticky_web == 1 && switched_in_pkmn.is_grounded() {
384 apply_boost_instruction(
387 side,
388 &PokemonBoostableStat::Speed,
389 &-1,
390 &switching_side_ref,
391 &switching_side_ref,
392 incoming_instructions,
393 );
394 }
395
396 let side = state.get_side_immutable(&switching_side_ref);
397 let switched_in_pkmn = side.get_active_immutable();
398 let mut toxic_spike_instruction: Option<Instruction> = None;
399 if side.side_conditions.toxic_spikes > 0 && switched_in_pkmn.is_grounded() {
400 if !immune_to_status(
401 &state,
402 &MoveTarget::User,
403 &switching_side_ref,
404 &PokemonStatus::POISON,
405 ) {
406 if side.side_conditions.toxic_spikes == 1 {
407 toxic_spike_instruction =
408 Some(Instruction::ChangeStatus(ChangeStatusInstruction {
409 side_ref: switching_side_ref,
410 pokemon_index: side.active_index,
411 old_status: switched_in_pkmn.status,
412 new_status: PokemonStatus::POISON,
413 }))
414 } else if side.side_conditions.toxic_spikes == 2 {
415 toxic_spike_instruction =
416 Some(Instruction::ChangeStatus(ChangeStatusInstruction {
417 side_ref: switching_side_ref,
418 pokemon_index: side.active_index,
419 old_status: switched_in_pkmn.status,
420 new_status: PokemonStatus::TOXIC,
421 }))
422 }
423 } else if switched_in_pkmn.has_type(&PokemonType::POISON) {
424 toxic_spike_instruction = Some(Instruction::ChangeSideCondition(
425 ChangeSideConditionInstruction {
426 side_ref: switching_side_ref,
427 side_condition: PokemonSideCondition::ToxicSpikes,
428 amount: -1 * side.side_conditions.toxic_spikes,
429 },
430 ))
431 }
432
433 if let Some(i) = toxic_spike_instruction {
434 state.apply_one_instruction(&i);
435 incoming_instructions.instruction_list.push(i);
436 }
437 }
438
439 let side = state.get_side(&switching_side_ref);
440 let active = side.get_active_immutable();
441 if active.ability != Abilities::MAGICGUARD {
442 if side.side_conditions.stealth_rock == 1 {
443 let switched_in_pkmn = side.get_active();
444 let multiplier = type_effectiveness_modifier(&PokemonType::ROCK, &switched_in_pkmn);
445
446 let dmg_amount = cmp::min(
447 (switched_in_pkmn.maxhp as f32 * multiplier / 8.0) as i16,
448 switched_in_pkmn.hp,
449 );
450 let stealth_rock_dmg_instruction = Instruction::Damage(DamageInstruction {
451 side_ref: switching_side_ref,
452 damage_amount: dmg_amount,
453 });
454 switched_in_pkmn.hp -= dmg_amount;
455 incoming_instructions
456 .instruction_list
457 .push(stealth_rock_dmg_instruction);
458 }
459
460 let switched_in_pkmn = side.get_active_immutable();
461 if side.side_conditions.spikes > 0 && switched_in_pkmn.is_grounded() {
462 let dmg_amount = cmp::min(
463 switched_in_pkmn.maxhp * side.side_conditions.spikes as i16 / 8,
464 switched_in_pkmn.hp,
465 );
466 let spikes_dmg_instruction = Instruction::Damage(DamageInstruction {
467 side_ref: switching_side_ref,
468 damage_amount: dmg_amount,
469 });
470 side.get_active().hp -= dmg_amount;
471 incoming_instructions
472 .instruction_list
473 .push(spikes_dmg_instruction);
474 }
475 }
476 }
477
478 ability_on_switch_in(state, &switching_side_ref, incoming_instructions);
479 item_on_switch_in(state, &switching_side_ref, incoming_instructions);
480
481 state.reverse_instructions(&incoming_instructions.instruction_list);
482}
483
484fn generate_instructions_from_increment_side_condition(
485 state: &mut State,
486 side_condition: &SideCondition,
487 attacking_side_reference: &SideReference,
488 incoming_instructions: &mut StateInstructions,
489) {
490 let affected_side_ref;
491 match side_condition.target {
492 MoveTarget::Opponent => affected_side_ref = attacking_side_reference.get_other_side(),
493 MoveTarget::User => affected_side_ref = *attacking_side_reference,
494 }
495
496 let max_layers = match side_condition.condition {
497 PokemonSideCondition::Spikes => 3,
498 PokemonSideCondition::ToxicSpikes => 2,
499 _ => 1,
500 };
501
502 let affected_side = state.get_side(&affected_side_ref);
503 if affected_side.get_side_condition(side_condition.condition) < max_layers {
504 let ins = Instruction::ChangeSideCondition(ChangeSideConditionInstruction {
505 side_ref: affected_side_ref,
506 side_condition: side_condition.condition,
507 amount: 1,
508 });
509 affected_side.update_side_condition(side_condition.condition, 1);
510 incoming_instructions.instruction_list.push(ins);
511 }
512}
513
514fn generate_instructions_from_duration_side_conditions(
515 state: &mut State,
516 side_condition: &SideCondition,
517 attacking_side_reference: &SideReference,
518 incoming_instructions: &mut StateInstructions,
519 duration: i8,
520) {
521 let affected_side_ref = match side_condition.target {
522 MoveTarget::Opponent => attacking_side_reference.get_other_side(),
523 MoveTarget::User => *attacking_side_reference,
524 };
525 if side_condition.condition == PokemonSideCondition::AuroraVeil
526 && !state.weather_is_active(&Weather::HAIL)
527 && !state.weather_is_active(&Weather::SNOW)
528 {
529 return;
530 }
531 let affected_side = state.get_side(&affected_side_ref);
532 if affected_side.get_side_condition(side_condition.condition) == 0 {
533 let ins = Instruction::ChangeSideCondition(ChangeSideConditionInstruction {
534 side_ref: affected_side_ref,
535 side_condition: side_condition.condition,
536 amount: duration,
537 });
538 affected_side.update_side_condition(side_condition.condition, duration);
539 incoming_instructions.instruction_list.push(ins);
540 }
541}
542
543fn generate_instructions_from_side_conditions(
544 state: &mut State,
545 side_condition: &SideCondition,
546 attacking_side_reference: &SideReference,
547 incoming_instructions: &mut StateInstructions,
548) {
549 match side_condition.condition {
550 PokemonSideCondition::AuroraVeil
551 | PokemonSideCondition::LightScreen
552 | PokemonSideCondition::Reflect
553 | PokemonSideCondition::Safeguard
554 | PokemonSideCondition::Mist => {
555 generate_instructions_from_duration_side_conditions(
556 state,
557 side_condition,
558 attacking_side_reference,
559 incoming_instructions,
560 SIDE_CONDITION_DURATION,
561 );
562 }
563 PokemonSideCondition::Tailwind => {
564 generate_instructions_from_duration_side_conditions(
565 state,
566 side_condition,
567 attacking_side_reference,
568 incoming_instructions,
569 TAILWIND_DURATION,
570 );
571 }
572 _ => generate_instructions_from_increment_side_condition(
573 state,
574 side_condition,
575 attacking_side_reference,
576 incoming_instructions,
577 ),
578 }
579}
580
581fn get_instructions_from_volatile_statuses(
582 state: &mut State,
583 attacker_choice: &Choice,
584 volatile_status: &VolatileStatus,
585 attacking_side_reference: &SideReference,
586 incoming_instructions: &mut StateInstructions,
587) {
588 let target_side: SideReference;
589 match volatile_status.target {
590 MoveTarget::Opponent => target_side = attacking_side_reference.get_other_side(),
591 MoveTarget::User => target_side = *attacking_side_reference,
592 }
593
594 if volatile_status.volatile_status == PokemonVolatileStatus::YAWN
595 && immune_to_status(
596 state,
597 &MoveTarget::Opponent,
598 &target_side,
599 &PokemonStatus::SLEEP,
600 )
601 {
602 return;
603 }
604 let side = state.get_side(&target_side);
605 let affected_pkmn = side.get_active_immutable();
606 if affected_pkmn.volatile_status_can_be_applied(
607 &volatile_status.volatile_status,
608 &side.volatile_statuses,
609 attacker_choice.first_move,
610 ) {
611 let ins = Instruction::ApplyVolatileStatus(ApplyVolatileStatusInstruction {
612 side_ref: target_side,
613 volatile_status: volatile_status.volatile_status,
614 });
615
616 side.volatile_statuses
617 .insert(volatile_status.volatile_status);
618 incoming_instructions.instruction_list.push(ins);
619 }
620}
621
622pub fn add_remove_status_instructions(
623 incoming_instructions: &mut StateInstructions,
624 pokemon_index: PokemonIndex,
625 side_reference: SideReference,
626 side: &mut Side,
627) {
628 let pkmn = &mut side.pokemon[pokemon_index];
635 incoming_instructions
636 .instruction_list
637 .push(Instruction::ChangeStatus(ChangeStatusInstruction {
638 side_ref: side_reference,
639 pokemon_index: pokemon_index,
640 old_status: pkmn.status,
641 new_status: PokemonStatus::NONE,
642 }));
643 match pkmn.status {
644 PokemonStatus::SLEEP => {
645 if pkmn.rest_turns > 0 {
646 incoming_instructions
647 .instruction_list
648 .push(Instruction::SetRestTurns(SetSleepTurnsInstruction {
649 side_ref: side_reference,
650 pokemon_index,
651 new_turns: 0,
652 previous_turns: pkmn.rest_turns,
653 }));
654 pkmn.rest_turns = 0;
655 } else if pkmn.sleep_turns > 0 {
656 incoming_instructions
657 .instruction_list
658 .push(Instruction::SetSleepTurns(SetSleepTurnsInstruction {
659 side_ref: side_reference,
660 pokemon_index,
661 new_turns: 0,
662 previous_turns: pkmn.sleep_turns,
663 }));
664 pkmn.sleep_turns = 0;
665 }
666 }
667 PokemonStatus::TOXIC => {
668 if side.side_conditions.toxic_count != 0 {
669 incoming_instructions
670 .instruction_list
671 .push(Instruction::ChangeSideCondition(
672 ChangeSideConditionInstruction {
673 side_ref: side_reference,
674 side_condition: PokemonSideCondition::ToxicCount,
675 amount: -1 * side.side_conditions.toxic_count,
676 },
677 ));
678 side.side_conditions.toxic_count = 0;
679 }
680 }
681 _ => {}
682 }
683 pkmn.status = PokemonStatus::NONE;
684}
685
686pub fn immune_to_status(
687 state: &State,
688 status_target: &MoveTarget,
689 target_side_ref: &SideReference,
690 status: &PokemonStatus,
691) -> bool {
692 let (target_side, attacking_side) = state.get_both_sides_immutable(target_side_ref);
693 let target_pkmn = target_side.get_active_immutable();
694 let attacking_pkmn = attacking_side.get_active_immutable();
695
696 match target_pkmn.ability {
698 Abilities::SHIELDSDOWN => return target_pkmn.hp > target_pkmn.maxhp / 2,
699 Abilities::PURIFYINGSALT => return true,
700 Abilities::COMATOSE => return true,
701 Abilities::LEAFGUARD => return state.weather_is_active(&Weather::SUN),
702 _ => {}
703 }
704
705 if target_pkmn.status != PokemonStatus::NONE || target_pkmn.hp <= 0 {
706 true
707 } else if state.terrain.terrain_type == Terrain::MISTYTERRAIN && target_pkmn.is_grounded() {
708 true
709 } else if (target_side
710 .volatile_statuses
711 .contains(&PokemonVolatileStatus::SUBSTITUTE)
712 || target_side.side_conditions.safeguard > 0)
713 && status_target == &MoveTarget::Opponent
714 {
716 true
717 } else {
718 match status {
720 PokemonStatus::BURN => {
721 target_pkmn.has_type(&PokemonType::FIRE)
722 || [
723 Abilities::WATERVEIL,
724 Abilities::WATERBUBBLE,
725 Abilities::THERMALEXCHANGE,
726 ]
727 .contains(&target_pkmn.ability)
728 }
729 PokemonStatus::FREEZE => {
730 target_pkmn.has_type(&PokemonType::ICE)
731 || target_pkmn.ability == Abilities::MAGMAARMOR
732 || state.weather_is_active(&Weather::SUN)
733 || state.weather_is_active(&Weather::HARSHSUN)
734 }
735 PokemonStatus::SLEEP => {
736 (state.terrain.terrain_type == Terrain::ELECTRICTERRAIN
737 && target_pkmn.is_grounded())
738 || [
739 Abilities::INSOMNIA,
740 Abilities::SWEETVEIL,
741 Abilities::VITALSPIRIT,
742 ]
743 .contains(&target_pkmn.ability)
744 || (status_target == &MoveTarget::Opponent
745 && target_side.has_alive_non_rested_sleeping_pkmn())
746 }
748
749 #[cfg(any(feature = "gen6", feature = "gen7", feature = "gen8", feature = "gen9"))]
750 PokemonStatus::PARALYZE => {
751 target_pkmn.has_type(&PokemonType::ELECTRIC)
752 || target_pkmn.ability == Abilities::LIMBER
753 }
754
755 #[cfg(any(feature = "gen4", feature = "gen5", feature = "gen3"))]
756 PokemonStatus::PARALYZE => target_pkmn.ability == Abilities::LIMBER,
757
758 PokemonStatus::POISON | PokemonStatus::TOXIC => {
759 ((target_pkmn.has_type(&PokemonType::POISON)
760 || target_pkmn.has_type(&PokemonType::STEEL))
761 && attacking_pkmn.ability != Abilities::CORROSION)
762 || [Abilities::IMMUNITY, Abilities::PASTELVEIL].contains(&target_pkmn.ability)
763 }
764 _ => false,
765 }
766 }
767}
768
769fn get_instructions_from_status_effects(
770 state: &mut State,
771 status: &Status,
772 attacking_side_reference: &SideReference,
773 incoming_instructions: &mut StateInstructions,
774 hit_sub: bool,
775) {
776 let target_side_ref: SideReference;
777 match status.target {
778 MoveTarget::Opponent => target_side_ref = attacking_side_reference.get_other_side(),
779 MoveTarget::User => target_side_ref = *attacking_side_reference,
780 }
781
782 if hit_sub || immune_to_status(state, &status.target, &target_side_ref, &status.status) {
783 return;
784 }
785
786 let target_side = state.get_side(&target_side_ref);
787 let target_side_active = target_side.active_index;
788 let target_pkmn = target_side.get_active();
789
790 let instruction = if target_pkmn.item == Items::LUMBERRY {
791 target_pkmn.item = Items::NONE;
792 Instruction::ChangeItem(ChangeItemInstruction {
793 side_ref: target_side_ref,
794 current_item: Items::LUMBERRY,
795 new_item: Items::NONE,
796 })
797 } else if target_pkmn.item == Items::CHESTOBERRY && status.status == PokemonStatus::SLEEP {
798 target_pkmn.item = Items::NONE;
799 Instruction::ChangeItem(ChangeItemInstruction {
800 side_ref: target_side_ref,
801 current_item: Items::CHESTOBERRY,
802 new_item: Items::NONE,
803 })
804 } else {
805 let old_status = target_pkmn.status;
806 target_pkmn.status = status.status;
807 Instruction::ChangeStatus(ChangeStatusInstruction {
808 side_ref: target_side_ref,
809 pokemon_index: target_side_active,
810 old_status,
811 new_status: status.status,
812 })
813 };
814 incoming_instructions.instruction_list.push(instruction);
815}
816
817pub fn get_boost_amount(side: &Side, boost: &PokemonBoostableStat, amount: i8) -> i8 {
818 let current_boost = side.get_boost_from_boost_enum(boost);
823
824 if amount > 0 {
825 return cmp::min(6 - current_boost, amount);
826 } else if amount < 0 {
827 return cmp::max(-6 - current_boost, amount);
828 }
829 0
830}
831
832pub fn apply_boost_instruction(
833 target_side: &mut Side,
834 stat: &PokemonBoostableStat,
835 boost: &i8,
836 attacking_side_ref: &SideReference,
837 target_side_ref: &SideReference,
838 instructions: &mut StateInstructions,
839) -> bool {
840 let mut boost_was_applied = false;
845 let target_pkmn = target_side.get_active_immutable();
846 let target_pkmn_ability = target_pkmn.ability;
847
848 if boost != &0
849 && !(target_side_ref != attacking_side_ref
850 && target_pkmn
851 .immune_to_stats_lowered_by_opponent(&stat, &target_side.volatile_statuses))
852 && target_pkmn.hp != 0
853 {
854 let mut boost_amount = *boost;
855 if target_pkmn_ability == Abilities::CONTRARY {
856 boost_amount *= -1;
857 }
858 boost_amount = get_boost_amount(target_side, &stat, boost_amount);
859 if boost_amount != 0 {
860 boost_was_applied = true;
861 match stat {
862 PokemonBoostableStat::Attack => target_side.attack_boost += boost_amount,
863 PokemonBoostableStat::Defense => target_side.defense_boost += boost_amount,
864 PokemonBoostableStat::SpecialAttack => {
865 target_side.special_attack_boost += boost_amount
866 }
867 PokemonBoostableStat::SpecialDefense => {
868 target_side.special_defense_boost += boost_amount
869 }
870 PokemonBoostableStat::Speed => target_side.speed_boost += boost_amount,
871 PokemonBoostableStat::Evasion => target_side.accuracy_boost += boost_amount,
872 PokemonBoostableStat::Accuracy => target_side.evasion_boost += boost_amount,
873 }
874 instructions
875 .instruction_list
876 .push(Instruction::Boost(BoostInstruction {
877 side_ref: *target_side_ref,
878 stat: *stat,
879 amount: boost_amount,
880 }));
881
882 if boost_amount < 0 {
883 if target_pkmn_ability == Abilities::DEFIANT
884 && attacking_side_ref != target_side_ref
885 && target_side.attack_boost < 6
886 {
887 let defiant_boost_amount = cmp::min(6 - target_side.attack_boost, 2);
888 target_side.attack_boost += defiant_boost_amount;
889 instructions
890 .instruction_list
891 .push(Instruction::Boost(BoostInstruction {
892 side_ref: *target_side_ref,
893 stat: PokemonBoostableStat::Attack,
894 amount: defiant_boost_amount,
895 }));
896 } else if target_pkmn_ability == Abilities::COMPETITIVE
897 && attacking_side_ref != target_side_ref
898 && target_side.special_attack_boost < 6
899 {
900 let competitive_boost_amount =
901 cmp::min(6 - target_side.special_attack_boost, 2);
902 target_side.special_attack_boost += competitive_boost_amount;
903 instructions
904 .instruction_list
905 .push(Instruction::Boost(BoostInstruction {
906 side_ref: *target_side_ref,
907 stat: PokemonBoostableStat::SpecialAttack,
908 amount: competitive_boost_amount,
909 }));
910 }
911 }
912 }
913 }
914 boost_was_applied
915}
916
917fn get_instructions_from_boosts(
918 state: &mut State,
919 boosts: &Boost,
920 attacking_side_reference: &SideReference,
921 incoming_instructions: &mut StateInstructions,
922) {
923 let target_side_ref: SideReference;
924 match boosts.target {
925 MoveTarget::Opponent => target_side_ref = attacking_side_reference.get_other_side(),
926 MoveTarget::User => target_side_ref = *attacking_side_reference,
927 }
928 let boostable_stats = boosts.boosts.get_as_pokemon_boostable();
929 for (pkmn_boostable_stat, boost) in boostable_stats.iter().filter(|(_, b)| b != &0) {
930 let side = state.get_side(&target_side_ref);
931 apply_boost_instruction(
932 side,
933 pkmn_boostable_stat,
934 boost,
935 attacking_side_reference,
936 &target_side_ref,
937 incoming_instructions,
938 );
939 }
940}
941
942fn compare_health_with_damage_multiples(max_damage: i16, health: i16) -> (i16, i16) {
943 let max_damage_f32 = max_damage as f32;
944 let health_f32 = health as f32;
945
946 let mut total_less_than = 0;
947 let mut num_less_than = 0;
948 let mut num_greater_than = 0;
949 let increment = max_damage as f32 * 0.01;
950 let mut damage = max_damage_f32 * 0.85;
951 for _ in 0..16 {
952 if damage < health_f32 {
953 total_less_than += damage as i16;
954 num_less_than += 1;
955 } else if damage > health_f32 {
956 num_greater_than += 1;
957 }
958 damage += increment;
959 }
960
961 (total_less_than / num_less_than, num_greater_than)
962}
963
964fn get_instructions_from_secondaries(
965 state: &mut State,
966 attacker_choice: &Choice,
967 secondaries: &Vec<Secondary>,
968 side_reference: &SideReference,
969 incoming_instructions: StateInstructions,
970 hit_sub: bool,
971) -> Vec<StateInstructions> {
972 let mut return_instruction_list = Vec::with_capacity(4);
973 return_instruction_list.push(incoming_instructions);
974
975 for secondary in secondaries {
976 if secondary.target == MoveTarget::Opponent && hit_sub {
977 continue;
978 }
979 let secondary_percent_hit = (secondary.chance / 100.0).min(1.0);
980
981 let mut i = 0;
982 while i < return_instruction_list.len() {
983 let mut secondary_hit_instructions = return_instruction_list.remove(i);
984
985 if secondary_percent_hit < 1.0 {
986 let mut secondary_miss_instructions = secondary_hit_instructions.clone();
987 secondary_miss_instructions.update_percentage(1.0 - secondary_percent_hit);
988 return_instruction_list.insert(i, secondary_miss_instructions);
989 i += 1;
990 }
991
992 if secondary_percent_hit > 0.0 {
993 secondary_hit_instructions.update_percentage(secondary_percent_hit);
994
995 state.apply_instructions(&secondary_hit_instructions.instruction_list);
996 match &secondary.effect {
997 Effect::VolatileStatus(volatile_status) => {
998 get_instructions_from_volatile_statuses(
999 state,
1000 attacker_choice,
1001 &VolatileStatus {
1002 target: secondary.target.clone(),
1003 volatile_status: volatile_status.clone(),
1004 },
1005 side_reference,
1006 &mut secondary_hit_instructions,
1007 );
1008 }
1009 Effect::Boost(boost) => {
1010 get_instructions_from_boosts(
1011 state,
1012 &Boost {
1013 target: secondary.target.clone(),
1014 boosts: boost.clone(),
1015 },
1016 side_reference,
1017 &mut secondary_hit_instructions,
1018 );
1019 }
1020 Effect::Status(status) => {
1021 get_instructions_from_status_effects(
1022 state,
1023 &Status {
1024 target: secondary.target.clone(),
1025 status: status.clone(),
1026 },
1027 side_reference,
1028 &mut secondary_hit_instructions,
1029 hit_sub,
1030 );
1031 }
1032 Effect::Heal(heal_amount) => {
1033 get_instructions_from_heal(
1034 state,
1035 &Heal {
1036 target: secondary.target.clone(),
1037 amount: *heal_amount,
1038 },
1039 side_reference,
1040 &mut secondary_hit_instructions,
1041 );
1042 }
1043 Effect::RemoveItem => {
1044 let secondary_target_side_ref: SideReference;
1045 match secondary.target {
1046 MoveTarget::Opponent => {
1047 secondary_target_side_ref = side_reference.get_other_side();
1048 }
1049 MoveTarget::User => {
1050 secondary_target_side_ref = *side_reference;
1051 }
1052 }
1053 let target_pkmn = state.get_side(&secondary_target_side_ref).get_active();
1054 secondary_hit_instructions
1055 .instruction_list
1056 .push(Instruction::ChangeItem(ChangeItemInstruction {
1057 side_ref: secondary_target_side_ref,
1058 current_item: target_pkmn.item.clone(),
1059 new_item: Items::NONE,
1060 }));
1061 target_pkmn.item = Items::NONE;
1062 }
1063 }
1064 state.reverse_instructions(&secondary_hit_instructions.instruction_list);
1065 return_instruction_list.insert(i, secondary_hit_instructions);
1066 i += 1; }
1068 }
1069 }
1070
1071 return_instruction_list
1072}
1073
1074fn get_instructions_from_heal(
1075 state: &mut State,
1076 heal: &Heal,
1077 attacking_side_reference: &SideReference,
1078 incoming_instructions: &mut StateInstructions,
1079) {
1080 let target_side_ref: SideReference;
1081 match heal.target {
1082 MoveTarget::Opponent => target_side_ref = attacking_side_reference.get_other_side(),
1083 MoveTarget::User => target_side_ref = *attacking_side_reference,
1084 }
1085
1086 let target_pkmn = state.get_side(&target_side_ref).get_active();
1087
1088 let mut health_recovered = (heal.amount * target_pkmn.maxhp as f32) as i16;
1089 let final_health = target_pkmn.hp + health_recovered;
1090 if final_health > target_pkmn.maxhp {
1091 health_recovered -= final_health - target_pkmn.maxhp;
1092 } else if final_health < 0 {
1093 health_recovered -= final_health;
1094 }
1095
1096 if health_recovered != 0 {
1097 let ins = Instruction::Heal(HealInstruction {
1098 side_ref: target_side_ref,
1099 heal_amount: health_recovered,
1100 });
1101 target_pkmn.hp += health_recovered;
1102 incoming_instructions.instruction_list.push(ins);
1103 }
1104}
1105
1106fn boosted_accuracy(accuracy_boost: i8) -> f32 {
1107 if accuracy_boost < 0 {
1108 3.0 / (3.0 - accuracy_boost as f32)
1109 } else {
1110 (3.0 + accuracy_boost as f32) / 3.0
1111 }
1112}
1113
1114fn check_move_hit_or_miss(
1115 state: &mut State,
1116 choice: &Choice,
1117 attacking_side_ref: &SideReference,
1118 damage: Option<(i16, i16)>,
1119 incoming_instructions: &mut StateInstructions,
1120 frozen_instructions: &mut Vec<StateInstructions>,
1121) {
1122 let attacking_side = state.get_side(attacking_side_ref);
1131 let attacking_pokemon = attacking_side.get_active_immutable();
1132
1133 let mut percent_hit =
1134 ((choice.accuracy / 100.0) * boosted_accuracy(attacking_side.accuracy_boost)).min(1.0);
1135 if Some((0, 0)) == damage {
1136 percent_hit = 0.0;
1137 }
1138
1139 if percent_hit < 1.0 {
1140 let mut move_missed_instruction = incoming_instructions.clone();
1141 move_missed_instruction.update_percentage(1.0 - percent_hit);
1142 if let Some(crash_fraction) = choice.crash {
1143 let crash_amount = (attacking_pokemon.maxhp as f32 * crash_fraction) as i16;
1144 let crash_instruction = Instruction::Damage(DamageInstruction {
1145 side_ref: *attacking_side_ref,
1146 damage_amount: cmp::min(crash_amount, attacking_pokemon.hp),
1147 });
1148
1149 move_missed_instruction
1150 .instruction_list
1151 .push(crash_instruction);
1152 }
1153
1154 if Items::BLUNDERPOLICY == attacking_pokemon.item {
1155 let boost_amount = get_boost_amount(attacking_side, &PokemonBoostableStat::Speed, 2);
1156 move_missed_instruction
1157 .instruction_list
1158 .push(Instruction::Boost(BoostInstruction {
1159 side_ref: *attacking_side_ref,
1160 stat: PokemonBoostableStat::Speed,
1161 amount: boost_amount,
1162 }));
1163 move_missed_instruction
1164 .instruction_list
1165 .push(Instruction::ChangeItem(ChangeItemInstruction {
1166 side_ref: *attacking_side_ref,
1167 current_item: Items::BLUNDERPOLICY,
1168 new_item: Items::NONE,
1169 }));
1170 }
1171
1172 frozen_instructions.push(move_missed_instruction);
1173 }
1174 incoming_instructions.update_percentage(percent_hit);
1175}
1176
1177fn get_instructions_from_drag(
1178 state: &mut State,
1179 attacking_side_reference: &SideReference,
1180 incoming_instructions: StateInstructions,
1181 frozen_instructions: &mut Vec<StateInstructions>,
1182) {
1183 let defending_side = state.get_side(&attacking_side_reference.get_other_side());
1184 if defending_side.get_active_immutable().hp == 0 {
1185 state.reverse_instructions(&incoming_instructions.instruction_list);
1186 frozen_instructions.push(incoming_instructions);
1187 return;
1188 }
1189
1190 let defending_side_alive_reserve_indices = defending_side.get_alive_pkmn_indices();
1191
1192 state.reverse_instructions(&incoming_instructions.instruction_list);
1193
1194 let num_alive_reserve = defending_side_alive_reserve_indices.len();
1195 if num_alive_reserve == 0 {
1196 frozen_instructions.push(incoming_instructions);
1197 return;
1198 }
1199
1200 for pkmn_id in defending_side_alive_reserve_indices {
1201 let mut cloned_instructions = incoming_instructions.clone();
1202 generate_instructions_from_switch(
1203 state,
1204 pkmn_id,
1205 attacking_side_reference.get_other_side(),
1206 &mut cloned_instructions,
1207 );
1208 cloned_instructions.update_percentage(1.0 / num_alive_reserve as f32);
1209 frozen_instructions.push(cloned_instructions);
1210 }
1211}
1212
1213fn reset_damage_dealt(
1214 side: &Side,
1215 side_reference: &SideReference,
1216 incoming_instructions: &mut StateInstructions,
1217) {
1218 if side.damage_dealt.damage != 0 {
1222 incoming_instructions
1223 .instruction_list
1224 .push(Instruction::ChangeDamageDealtDamage(
1225 ChangeDamageDealtDamageInstruction {
1226 side_ref: *side_reference,
1227 damage_change: 0 - side.damage_dealt.damage,
1228 },
1229 ));
1230 }
1231 if side.damage_dealt.move_category != MoveCategory::Physical {
1232 incoming_instructions
1233 .instruction_list
1234 .push(Instruction::ChangeDamageDealtMoveCatagory(
1235 ChangeDamageDealtMoveCategoryInstruction {
1236 side_ref: *side_reference,
1237 move_category: MoveCategory::Physical,
1238 previous_move_category: side.damage_dealt.move_category,
1239 },
1240 ));
1241 }
1242 if side.damage_dealt.hit_substitute {
1243 incoming_instructions
1244 .instruction_list
1245 .push(Instruction::ToggleDamageDealtHitSubstitute(
1246 ToggleDamageDealtHitSubstituteInstruction {
1247 side_ref: *side_reference,
1248 },
1249 ));
1250 }
1251}
1252
1253fn set_damage_dealt(
1254 attacking_side: &mut Side,
1255 attacking_side_ref: &SideReference,
1256 damage_dealt: i16,
1257 choice: &Choice,
1258 hit_substitute: bool,
1259 incoming_instructions: &mut StateInstructions,
1260) {
1261 if attacking_side.damage_dealt.damage != damage_dealt {
1262 incoming_instructions
1263 .instruction_list
1264 .push(Instruction::ChangeDamageDealtDamage(
1265 ChangeDamageDealtDamageInstruction {
1266 side_ref: *attacking_side_ref,
1267 damage_change: damage_dealt - attacking_side.damage_dealt.damage,
1268 },
1269 ));
1270 attacking_side.damage_dealt.damage = damage_dealt;
1271 }
1272
1273 if attacking_side.damage_dealt.move_category != choice.category {
1274 incoming_instructions
1275 .instruction_list
1276 .push(Instruction::ChangeDamageDealtMoveCatagory(
1277 ChangeDamageDealtMoveCategoryInstruction {
1278 side_ref: *attacking_side_ref,
1279 move_category: choice.category,
1280 previous_move_category: attacking_side.damage_dealt.move_category,
1281 },
1282 ));
1283 attacking_side.damage_dealt.move_category = choice.category;
1284 }
1285
1286 if attacking_side.damage_dealt.hit_substitute != hit_substitute {
1287 incoming_instructions
1288 .instruction_list
1289 .push(Instruction::ToggleDamageDealtHitSubstitute(
1290 ToggleDamageDealtHitSubstituteInstruction {
1291 side_ref: *attacking_side_ref,
1292 },
1293 ));
1294 attacking_side.damage_dealt.hit_substitute = hit_substitute;
1295 }
1296}
1297
1298fn generate_instructions_from_damage(
1299 mut state: &mut State,
1300 choice: &mut Choice,
1301 calculated_damage: i16,
1302 attacking_side_ref: &SideReference,
1303 mut incoming_instructions: &mut StateInstructions,
1304) -> bool {
1305 let mut hit_sub = false;
1311 let attacking_side = state.get_side(attacking_side_ref);
1312 let attacking_pokemon = attacking_side.get_active();
1313
1314 if calculated_damage <= 0 {
1315 if let Some(crash_fraction) = choice.crash {
1316 let crash_amount = (attacking_pokemon.maxhp as f32 * crash_fraction) as i16;
1317 let damage_taken = cmp::min(crash_amount, attacking_pokemon.hp);
1318 let crash_instruction = Instruction::Damage(DamageInstruction {
1319 side_ref: *attacking_side_ref,
1320 damage_amount: damage_taken,
1321 });
1322 attacking_pokemon.hp -= damage_taken;
1323 incoming_instructions
1324 .instruction_list
1325 .push(crash_instruction);
1326 }
1327 return hit_sub;
1328 }
1329
1330 let percent_hit = (choice.accuracy / 100.0).min(1.0);
1331
1332 if percent_hit > 0.0 {
1333 let should_use_damage_dealt = state.use_damage_dealt;
1334 let (attacking_side, defending_side) = state.get_both_sides(attacking_side_ref);
1335 let attacking_pokemon = attacking_side.get_active();
1336 let mut damage_dealt;
1337 if defending_side
1338 .volatile_statuses
1339 .contains(&PokemonVolatileStatus::SUBSTITUTE)
1340 && !choice.flags.sound
1341 && attacking_pokemon.ability != Abilities::INFILTRATOR
1342 {
1343 damage_dealt = cmp::min(calculated_damage, defending_side.substitute_health);
1344 let substitute_damage_dealt = cmp::min(calculated_damage, damage_dealt);
1345 let substitute_instruction = Instruction::DamageSubstitute(DamageInstruction {
1346 side_ref: attacking_side_ref.get_other_side(),
1347 damage_amount: substitute_damage_dealt,
1348 });
1349 defending_side.substitute_health -= substitute_damage_dealt;
1350 incoming_instructions
1351 .instruction_list
1352 .push(substitute_instruction);
1353
1354 if should_use_damage_dealt {
1355 set_damage_dealt(
1356 attacking_side,
1357 attacking_side_ref,
1358 damage_dealt,
1359 choice,
1360 true,
1361 &mut incoming_instructions,
1362 );
1363 }
1364
1365 if defending_side
1366 .volatile_statuses
1367 .contains(&PokemonVolatileStatus::SUBSTITUTE)
1368 && defending_side.substitute_health == 0
1369 {
1370 incoming_instructions
1371 .instruction_list
1372 .push(Instruction::RemoveVolatileStatus(
1373 RemoveVolatileStatusInstruction {
1374 side_ref: attacking_side_ref.get_other_side(),
1375 volatile_status: PokemonVolatileStatus::SUBSTITUTE,
1376 },
1377 ));
1378 defending_side
1379 .volatile_statuses
1380 .remove(&PokemonVolatileStatus::SUBSTITUTE);
1381 }
1382
1383 hit_sub = true;
1384 } else {
1385 let has_endure = defending_side
1386 .volatile_statuses
1387 .contains(&PokemonVolatileStatus::ENDURE);
1388 let attacking_pokemon = attacking_side.get_active();
1389 let defending_pokemon = defending_side.get_active();
1390 let mut knocked_out = false;
1391 damage_dealt = cmp::min(calculated_damage, defending_pokemon.hp);
1392 if damage_dealt != 0 {
1393 if has_endure
1394 || ((defending_pokemon.ability == Abilities::STURDY
1395 || defending_pokemon.item == Items::FOCUSSASH)
1396 && defending_pokemon.maxhp == defending_pokemon.hp)
1397 {
1398 damage_dealt -= 1;
1399 }
1400
1401 if damage_dealt >= defending_pokemon.hp {
1402 knocked_out = true;
1403 }
1404
1405 let damage_instruction = Instruction::Damage(DamageInstruction {
1406 side_ref: attacking_side_ref.get_other_side(),
1407 damage_amount: damage_dealt,
1408 });
1409 defending_pokemon.hp -= damage_dealt;
1410 incoming_instructions
1411 .instruction_list
1412 .push(damage_instruction);
1413
1414 if knocked_out
1415 && defending_side
1416 .volatile_statuses
1417 .contains(&PokemonVolatileStatus::DESTINYBOND)
1418 {
1419 let damage_instruction = Instruction::Damage(DamageInstruction {
1420 side_ref: *attacking_side_ref,
1421 damage_amount: attacking_pokemon.hp,
1422 });
1423 attacking_pokemon.hp = 0;
1424 incoming_instructions
1425 .instruction_list
1426 .push(damage_instruction);
1427 }
1428
1429 if should_use_damage_dealt {
1430 set_damage_dealt(
1431 attacking_side,
1432 attacking_side_ref,
1433 damage_dealt,
1434 choice,
1435 false,
1436 &mut incoming_instructions,
1437 );
1438 }
1439
1440 ability_after_damage_hit(
1441 &mut state,
1442 choice,
1443 attacking_side_ref,
1444 damage_dealt,
1445 &mut incoming_instructions,
1446 );
1447 }
1448 }
1449
1450 let attacking_pokemon = state.get_side(attacking_side_ref).get_active();
1451 if let Some(drain_fraction) = choice.drain {
1452 let drain_amount = (damage_dealt as f32 * drain_fraction) as i16;
1453 let heal_amount =
1454 cmp::min(drain_amount, attacking_pokemon.maxhp - attacking_pokemon.hp);
1455 if heal_amount != 0 {
1456 let drain_instruction = Instruction::Heal(HealInstruction {
1457 side_ref: *attacking_side_ref,
1458 heal_amount: heal_amount,
1459 });
1460 attacking_pokemon.hp += heal_amount;
1461 incoming_instructions
1462 .instruction_list
1463 .push(drain_instruction);
1464 }
1465 }
1466
1467 let attacking_pokemon = state.get_side(attacking_side_ref).get_active();
1468 if let Some(recoil_fraction) = choice.recoil {
1469 let recoil_amount = (damage_dealt as f32 * recoil_fraction) as i16;
1470 let damage_amount = cmp::min(recoil_amount, attacking_pokemon.hp);
1471 let recoil_instruction = Instruction::Damage(DamageInstruction {
1472 side_ref: *attacking_side_ref,
1473 damage_amount: damage_amount,
1474 });
1475 attacking_pokemon.hp -= damage_amount;
1476 incoming_instructions
1477 .instruction_list
1478 .push(recoil_instruction);
1479 }
1480 choice_after_damage_hit(
1481 &mut state,
1482 &choice,
1483 attacking_side_ref,
1484 &mut incoming_instructions,
1485 hit_sub,
1486 );
1487 }
1488 hit_sub
1489}
1490
1491fn move_has_no_effect(state: &State, choice: &Choice, attacking_side_ref: &SideReference) -> bool {
1492 let (_attacking_side, defending_side) = state.get_both_sides_immutable(attacking_side_ref);
1493 let defender = defending_side.get_active_immutable();
1494
1495 #[cfg(any(feature = "gen6", feature = "gen7", feature = "gen8", feature = "gen9"))]
1496 if choice.flags.powder
1497 && choice.target == MoveTarget::Opponent
1498 && defender.has_type(&PokemonType::GRASS)
1499 {
1500 return true;
1501 }
1502
1503 if choice.move_type == PokemonType::ELECTRIC
1504 && choice.target == MoveTarget::Opponent
1505 && defender.has_type(&PokemonType::GROUND)
1506 {
1507 return true;
1508 } else if choice.move_id == Choices::ENCORE {
1509 return match state
1510 .get_side_immutable(&attacking_side_ref.get_other_side())
1511 .last_used_move
1512 {
1513 LastUsedMove::None => true,
1514 LastUsedMove::Move(_) => false,
1515 LastUsedMove::Switch(_) => true,
1516 };
1517 } else if state.terrain_is_active(&Terrain::PSYCHICTERRAIN)
1518 && defender.is_grounded()
1519 && choice.target == MoveTarget::Opponent
1520 && choice.priority > 0
1521 {
1522 return true;
1523 }
1524 false
1525}
1526
1527fn cannot_use_move(state: &State, choice: &Choice, attacking_side_ref: &SideReference) -> bool {
1528 let (attacking_side, defending_side) = state.get_both_sides_immutable(attacking_side_ref);
1529
1530 if defending_side.get_active_immutable().hp == 0 && choice.category != MoveCategory::Status {
1532 return true;
1533 }
1534
1535 if attacking_side
1537 .volatile_statuses
1538 .contains(&PokemonVolatileStatus::TAUNT)
1539 && matches!(choice.category, MoveCategory::Status)
1540 {
1541 return true;
1542 } else if attacking_side
1543 .volatile_statuses
1544 .contains(&PokemonVolatileStatus::FLINCH)
1545 {
1546 return true;
1547 } else if choice.flags.heal
1548 && attacking_side
1549 .volatile_statuses
1550 .contains(&PokemonVolatileStatus::HEALBLOCK)
1551 {
1552 return true;
1553 }
1554 false
1555}
1556
1557#[cfg(feature = "terastallization")]
1558fn terastallized_base_power_floor(
1559 state: &mut State,
1560 choice: &mut Choice,
1561 attacking_side: &SideReference,
1562) {
1563 let attacker = state
1564 .get_side_immutable(attacking_side)
1565 .get_active_immutable();
1566
1567 if attacker.terastallized
1568 && choice.move_type == attacker.tera_type
1569 && choice.base_power < 60.0
1570 && choice.priority <= 0
1571 && choice.multi_hit() == MultiHitMove::None
1572 && choice.multi_accuracy() == MultiAccuracyMove::None
1573 {
1574 choice.base_power = 60.0;
1575 }
1576}
1577
1578fn before_move(
1579 state: &mut State,
1580 choice: &mut Choice,
1581 defender_choice: &Choice,
1582 attacking_side: &SideReference,
1583 incoming_instructions: &mut StateInstructions,
1584) {
1585 #[cfg(feature = "terastallization")]
1586 terastallized_base_power_floor(state, choice, attacking_side);
1587
1588 ability_before_move(state, choice, attacking_side, incoming_instructions);
1589 item_before_move(state, choice, attacking_side, incoming_instructions);
1590 choice_before_move(state, choice, attacking_side, incoming_instructions);
1591
1592 modify_choice(state, choice, defender_choice, attacking_side);
1593
1594 ability_modify_attack_being_used(state, choice, defender_choice, attacking_side);
1595 ability_modify_attack_against(state, choice, defender_choice, attacking_side);
1596
1597 item_modify_attack_being_used(state, choice, attacking_side);
1598 item_modify_attack_against(state, choice, attacking_side);
1599
1600 let (attacking_side, defending_side) = state.get_both_sides_immutable(attacking_side);
1605 if defending_side
1606 .volatile_statuses
1607 .contains(&PokemonVolatileStatus::SUBSTITUTE)
1608 && choice.category != MoveCategory::Status
1609 {
1610 choice.flags.drag = false;
1611 }
1612
1613 if choice.flags.charge {
1615 let charge_volatile_status = charge_choice_to_volatile(&choice.move_id);
1616 if !attacking_side
1617 .volatile_statuses
1618 .contains(&charge_volatile_status)
1619 {
1620 choice.remove_all_effects();
1621 choice.volatile_status = Some(VolatileStatus {
1622 target: MoveTarget::User,
1623 volatile_status: charge_volatile_status,
1624 });
1625 }
1626 }
1627
1628 if (defending_side
1630 .volatile_statuses
1631 .contains(&PokemonVolatileStatus::PROTECT)
1632 || defending_side
1633 .volatile_statuses
1634 .contains(&PokemonVolatileStatus::SPIKYSHIELD)
1635 || defending_side
1636 .volatile_statuses
1637 .contains(&PokemonVolatileStatus::BANEFULBUNKER)
1638 || defending_side
1639 .volatile_statuses
1640 .contains(&PokemonVolatileStatus::BURNINGBULWARK)
1641 || defending_side
1642 .volatile_statuses
1643 .contains(&PokemonVolatileStatus::SILKTRAP))
1644 && choice.flags.protect
1645 {
1646 choice.remove_effects_for_protect();
1647 if choice.crash.is_some() {
1648 choice.accuracy = 0.0;
1649 }
1650
1651 if defending_side
1652 .volatile_statuses
1653 .contains(&PokemonVolatileStatus::SPIKYSHIELD)
1654 && choice.flags.contact
1655 {
1656 choice.heal = Some(Heal {
1657 target: MoveTarget::User,
1658 amount: -0.125,
1659 })
1660 } else if defending_side
1661 .volatile_statuses
1662 .contains(&PokemonVolatileStatus::BANEFULBUNKER)
1663 && choice.flags.contact
1664 {
1665 choice.status = Some(Status {
1666 target: MoveTarget::User,
1667 status: PokemonStatus::POISON,
1668 })
1669 } else if defending_side
1670 .volatile_statuses
1671 .contains(&PokemonVolatileStatus::BURNINGBULWARK)
1672 && choice.flags.contact
1673 {
1674 choice.status = Some(Status {
1675 target: MoveTarget::User,
1676 status: PokemonStatus::BURN,
1677 })
1678 } else if defending_side
1679 .volatile_statuses
1680 .contains(&PokemonVolatileStatus::SILKTRAP)
1681 && choice.flags.contact
1682 {
1683 choice.boost = Some(Boost {
1684 target: MoveTarget::User,
1685 boosts: StatBoosts {
1686 attack: 0,
1687 defense: 0,
1688 special_attack: 0,
1689 special_defense: 0,
1690 speed: -1,
1691 accuracy: 0,
1692 },
1693 })
1694 }
1695 }
1696}
1697
1698fn generate_instructions_from_existing_status_conditions(
1699 state: &mut State,
1700 attacking_side_ref: &SideReference,
1701 attacker_choice: &Choice,
1702 incoming_instructions: &mut StateInstructions,
1703 final_instructions: &mut Vec<StateInstructions>,
1704) {
1705 let (attacking_side, _defending_side) = state.get_both_sides(attacking_side_ref);
1706 let current_active_index = attacking_side.active_index;
1707 let attacker_active = attacking_side.get_active();
1708 match attacker_active.status {
1709 PokemonStatus::PARALYZE => {
1710 let mut fully_paralyzed_instruction = incoming_instructions.clone();
1712 fully_paralyzed_instruction.update_percentage(0.25);
1713 final_instructions.push(fully_paralyzed_instruction);
1714
1715 incoming_instructions.update_percentage(0.75);
1717 }
1718 PokemonStatus::FREEZE => {
1719 let mut still_frozen_instruction = incoming_instructions.clone();
1720 still_frozen_instruction.update_percentage(0.80);
1721 final_instructions.push(still_frozen_instruction);
1722
1723 incoming_instructions.update_percentage(0.20);
1724 attacker_active.status = PokemonStatus::NONE;
1725 incoming_instructions
1726 .instruction_list
1727 .push(Instruction::ChangeStatus(ChangeStatusInstruction {
1728 side_ref: attacking_side_ref.clone(),
1729 pokemon_index: current_active_index,
1730 old_status: PokemonStatus::FREEZE,
1731 new_status: PokemonStatus::NONE,
1732 }));
1733 }
1734 PokemonStatus::SLEEP => {
1735 match attacker_active.rest_turns {
1736 0 => {
1738 let current_sleep_turns = attacker_active.sleep_turns;
1739 let chance_to_wake = chance_to_wake_up(current_sleep_turns);
1740 if chance_to_wake == 1.0 {
1741 attacker_active.status = PokemonStatus::NONE;
1742 attacker_active.sleep_turns = 0;
1743 incoming_instructions
1744 .instruction_list
1745 .push(Instruction::ChangeStatus(ChangeStatusInstruction {
1746 side_ref: *attacking_side_ref,
1747 pokemon_index: current_active_index,
1748 old_status: PokemonStatus::SLEEP,
1749 new_status: PokemonStatus::NONE,
1750 }));
1751 incoming_instructions
1752 .instruction_list
1753 .push(Instruction::SetSleepTurns(SetSleepTurnsInstruction {
1754 side_ref: *attacking_side_ref,
1755 pokemon_index: current_active_index,
1756 new_turns: 0,
1757 previous_turns: current_sleep_turns,
1758 }));
1759 } else if chance_to_wake == 0.0 {
1760 if attacker_choice.move_id == Choices::SLEEPTALK {
1761 incoming_instructions.instruction_list.push(
1763 Instruction::SetSleepTurns(SetSleepTurnsInstruction {
1764 side_ref: *attacking_side_ref,
1765 pokemon_index: current_active_index,
1766 new_turns: current_sleep_turns + 1,
1767 previous_turns: current_sleep_turns,
1768 }),
1769 );
1770 } else {
1771 let mut still_asleep_instruction = incoming_instructions.clone();
1772 still_asleep_instruction.update_percentage(1.0);
1773 still_asleep_instruction.instruction_list.push(
1774 Instruction::SetSleepTurns(SetSleepTurnsInstruction {
1775 side_ref: *attacking_side_ref,
1776 pokemon_index: current_active_index,
1777 new_turns: current_sleep_turns + 1,
1778 previous_turns: current_sleep_turns,
1779 }),
1780 );
1781 final_instructions.push(still_asleep_instruction);
1782 incoming_instructions.update_percentage(0.0);
1783 }
1784 } else {
1785 let do_nothing_percentage;
1792 let mut do_nothing_instructions = incoming_instructions.clone();
1793 if attacker_choice.move_id == Choices::SLEEPTALK {
1794 do_nothing_percentage = chance_to_wake;
1795 do_nothing_instructions.instruction_list.push(
1796 Instruction::ChangeStatus(ChangeStatusInstruction {
1797 side_ref: *attacking_side_ref,
1798 pokemon_index: current_active_index,
1799 old_status: PokemonStatus::SLEEP,
1800 new_status: PokemonStatus::NONE,
1801 }),
1802 );
1803 do_nothing_instructions.instruction_list.push(
1804 Instruction::SetSleepTurns(SetSleepTurnsInstruction {
1805 side_ref: *attacking_side_ref,
1806 pokemon_index: current_active_index,
1807 new_turns: 0,
1808 previous_turns: current_sleep_turns,
1809 }),
1810 );
1811 incoming_instructions.instruction_list.push(
1812 Instruction::SetSleepTurns(SetSleepTurnsInstruction {
1813 side_ref: *attacking_side_ref,
1814 pokemon_index: current_active_index,
1815 new_turns: current_sleep_turns + 1,
1816 previous_turns: current_sleep_turns,
1817 }),
1818 );
1819 attacker_active.sleep_turns += 1;
1820 } else {
1821 do_nothing_percentage = 1.0 - chance_to_wake;
1822 do_nothing_instructions.instruction_list.push(
1823 Instruction::SetSleepTurns(SetSleepTurnsInstruction {
1824 side_ref: *attacking_side_ref,
1825 pokemon_index: current_active_index,
1826 new_turns: current_sleep_turns + 1,
1827 previous_turns: current_sleep_turns,
1828 }),
1829 );
1830 incoming_instructions
1831 .instruction_list
1832 .push(Instruction::ChangeStatus(ChangeStatusInstruction {
1833 side_ref: *attacking_side_ref,
1834 pokemon_index: current_active_index,
1835 old_status: PokemonStatus::SLEEP,
1836 new_status: PokemonStatus::NONE,
1837 }));
1838 incoming_instructions.instruction_list.push(
1839 Instruction::SetSleepTurns(SetSleepTurnsInstruction {
1840 side_ref: *attacking_side_ref,
1841 pokemon_index: current_active_index,
1842 new_turns: 0,
1843 previous_turns: current_sleep_turns,
1844 }),
1845 );
1846 attacker_active.status = PokemonStatus::NONE;
1847 attacker_active.sleep_turns = 0;
1848 }
1849 do_nothing_instructions.update_percentage(do_nothing_percentage);
1850 incoming_instructions.update_percentage(1.0 - do_nothing_percentage);
1851 final_instructions.push(do_nothing_instructions);
1852 }
1853 }
1854 1 => {
1856 attacker_active.status = PokemonStatus::NONE;
1857 attacker_active.rest_turns -= 1;
1858 incoming_instructions
1859 .instruction_list
1860 .push(Instruction::ChangeStatus(ChangeStatusInstruction {
1861 side_ref: *attacking_side_ref,
1862 pokemon_index: current_active_index,
1863 old_status: PokemonStatus::SLEEP,
1864 new_status: PokemonStatus::NONE,
1865 }));
1866 incoming_instructions
1867 .instruction_list
1868 .push(Instruction::DecrementRestTurns(
1869 DecrementRestTurnsInstruction {
1870 side_ref: *attacking_side_ref,
1871 },
1872 ));
1873 }
1874 2 | 3 => {
1876 attacker_active.rest_turns -= 1;
1877 incoming_instructions
1878 .instruction_list
1879 .push(Instruction::DecrementRestTurns(
1880 DecrementRestTurnsInstruction {
1881 side_ref: *attacking_side_ref,
1882 },
1883 ));
1884 }
1885 _ => panic!("Invalid rest_turns value: {}", attacker_active.rest_turns),
1886 }
1887 }
1888 _ => {}
1889 }
1890
1891 if attacking_side
1892 .volatile_statuses
1893 .contains(&PokemonVolatileStatus::CONFUSION)
1894 {
1895 let mut hit_yourself_instruction = incoming_instructions.clone();
1896 hit_yourself_instruction.update_percentage(HIT_SELF_IN_CONFUSION_CHANCE);
1897
1898 let attacking_stat = attacking_side.calculate_boosted_stat(PokemonBoostableStat::Attack);
1899 let defending_stat = attacking_side.calculate_boosted_stat(PokemonBoostableStat::Defense);
1900
1901 let attacker_active = attacking_side.get_active();
1902 let mut damage_dealt = 2.0 * attacker_active.level as f32;
1903 damage_dealt = damage_dealt.floor() / 5.0;
1904 damage_dealt = damage_dealt.floor() + 2.0;
1905 damage_dealt = damage_dealt.floor() * 40.0; damage_dealt = damage_dealt * attacking_stat as f32 / defending_stat as f32;
1907 damage_dealt = damage_dealt.floor() / 50.0;
1908 damage_dealt = damage_dealt.floor() + 2.0;
1909 if attacker_active.status == PokemonStatus::BURN {
1910 damage_dealt /= 2.0;
1911 }
1912
1913 let damage_dealt = cmp::min(damage_dealt as i16, attacker_active.hp);
1914 let damage_instruction = Instruction::Damage(DamageInstruction {
1915 side_ref: *attacking_side_ref,
1916 damage_amount: damage_dealt,
1917 });
1918 hit_yourself_instruction
1919 .instruction_list
1920 .push(damage_instruction);
1921
1922 final_instructions.push(hit_yourself_instruction);
1923
1924 incoming_instructions.update_percentage(1.0 - HIT_SELF_IN_CONFUSION_CHANCE);
1925 }
1926
1927 if attacking_side.side_conditions.protect > 0 {
1928 if let Some(vs) = &attacker_choice.volatile_status {
1929 if PROTECT_VOLATILES.contains(&vs.volatile_status) {
1930 let protect_success_chance =
1931 CONSECUTIVE_PROTECT_CHANCE.powi(attacking_side.side_conditions.protect as i32);
1932 let mut protect_fail_instruction = incoming_instructions.clone();
1933 protect_fail_instruction.update_percentage(1.0 - protect_success_chance);
1934 final_instructions.push(protect_fail_instruction);
1935 incoming_instructions.update_percentage(protect_success_chance);
1936 }
1937 }
1938 }
1939}
1940
1941pub fn generate_instructions_from_move(
1942 state: &mut State,
1943 choice: &mut Choice,
1944 defender_choice: &Choice,
1945 attacking_side: SideReference,
1946 mut incoming_instructions: StateInstructions,
1947 mut final_instructions: &mut Vec<StateInstructions>,
1948 branch_on_damage: bool,
1949) {
1950 if state.use_damage_dealt {
1951 reset_damage_dealt(
1952 state.get_side(&attacking_side),
1953 &attacking_side,
1954 &mut incoming_instructions,
1955 );
1956 }
1957
1958 if choice.category == MoveCategory::Switch {
1959 generate_instructions_from_switch(
1960 state,
1961 choice.switch_id,
1962 attacking_side,
1963 &mut incoming_instructions,
1964 );
1965 final_instructions.push(incoming_instructions);
1966 return;
1967 }
1968
1969 let attacker_side = state.get_side(&attacking_side);
1970
1971 if choice.move_id == Choices::NONE {
1972 if attacker_side
1973 .volatile_statuses
1974 .contains(&PokemonVolatileStatus::MUSTRECHARGE)
1975 {
1976 incoming_instructions
1977 .instruction_list
1978 .push(Instruction::RemoveVolatileStatus(
1979 RemoveVolatileStatusInstruction {
1980 side_ref: attacking_side,
1981 volatile_status: PokemonVolatileStatus::MUSTRECHARGE,
1982 },
1983 ));
1984 }
1985 final_instructions.push(incoming_instructions);
1986 return;
1987 }
1988
1989 if attacker_side
1990 .volatile_statuses
1991 .contains(&PokemonVolatileStatus::TRUANT)
1992 {
1993 incoming_instructions
1994 .instruction_list
1995 .push(Instruction::RemoveVolatileStatus(
1996 RemoveVolatileStatusInstruction {
1997 side_ref: attacking_side,
1998 volatile_status: PokemonVolatileStatus::TRUANT,
1999 },
2000 ));
2001 final_instructions.push(incoming_instructions);
2002 return;
2003 }
2004
2005 if !choice.first_move && defender_choice.flags.drag {
2007 final_instructions.push(incoming_instructions);
2008 return;
2009 }
2010
2011 state.apply_instructions(&incoming_instructions.instruction_list);
2012
2013 let side = state.get_side(&attacking_side);
2014 if side
2015 .volatile_statuses
2016 .contains(&PokemonVolatileStatus::ENCORE)
2017 {
2018 match side.last_used_move {
2019 LastUsedMove::Move(last_used_move) => {
2020 if choice.move_index != last_used_move {
2021 *choice = MOVES
2022 .get(&side.get_active_immutable().moves[&last_used_move].id)
2023 .unwrap()
2024 .clone();
2025 choice.move_index = last_used_move;
2026 }
2027 }
2028 _ => panic!("Encore should not be active when last used move is not a move"),
2029 }
2030
2031 #[cfg(any(
2034 feature = "gen5",
2035 feature = "gen6",
2036 feature = "gen7",
2037 feature = "gen8",
2038 feature = "gen9"
2039 ))]
2040 if side.volatile_status_durations.encore == 2 {
2041 incoming_instructions
2042 .instruction_list
2043 .push(Instruction::RemoveVolatileStatus(
2044 RemoveVolatileStatusInstruction {
2045 side_ref: attacking_side,
2046 volatile_status: PokemonVolatileStatus::ENCORE,
2047 },
2048 ));
2049 incoming_instructions
2050 .instruction_list
2051 .push(Instruction::ChangeVolatileStatusDuration(
2052 ChangeVolatileStatusDurationInstruction {
2053 side_ref: attacking_side,
2054 volatile_status: PokemonVolatileStatus::ENCORE,
2055 amount: -2,
2056 },
2057 ));
2058 side.volatile_status_durations.encore = 0;
2059 side.volatile_statuses
2060 .remove(&PokemonVolatileStatus::ENCORE);
2061 } else {
2062 incoming_instructions
2063 .instruction_list
2064 .push(Instruction::ChangeVolatileStatusDuration(
2065 ChangeVolatileStatusDurationInstruction {
2066 side_ref: attacking_side,
2067 volatile_status: PokemonVolatileStatus::ENCORE,
2068 amount: 1,
2069 },
2070 ));
2071 side.volatile_status_durations.encore += 1;
2072 }
2073 }
2074
2075 #[cfg(any(
2076 feature = "gen5",
2077 feature = "gen6",
2078 feature = "gen7",
2079 feature = "gen8",
2080 feature = "gen9"
2081 ))]
2082 if side
2083 .volatile_statuses
2084 .contains(&PokemonVolatileStatus::TAUNT)
2085 {
2086 match side.volatile_status_durations.taunt {
2087 0 | 1 => {
2088 incoming_instructions.instruction_list.push(
2089 Instruction::ChangeVolatileStatusDuration(
2090 ChangeVolatileStatusDurationInstruction {
2091 side_ref: attacking_side,
2092 volatile_status: PokemonVolatileStatus::TAUNT,
2093 amount: 1,
2094 },
2095 ),
2096 );
2097 side.volatile_status_durations.taunt += 1;
2098 }
2099
2100 2 => {
2105 side.volatile_statuses.remove(&PokemonVolatileStatus::TAUNT);
2106 incoming_instructions
2107 .instruction_list
2108 .push(Instruction::RemoveVolatileStatus(
2109 RemoveVolatileStatusInstruction {
2110 side_ref: attacking_side,
2111 volatile_status: PokemonVolatileStatus::TAUNT,
2112 },
2113 ));
2114 incoming_instructions.instruction_list.push(
2115 Instruction::ChangeVolatileStatusDuration(
2116 ChangeVolatileStatusDurationInstruction {
2117 side_ref: attacking_side,
2118 volatile_status: PokemonVolatileStatus::TAUNT,
2119 amount: -2,
2120 },
2121 ),
2122 );
2123 side.volatile_status_durations.taunt = 0;
2124 state.re_enable_disabled_moves(
2125 &attacking_side,
2126 &mut incoming_instructions.instruction_list,
2127 );
2128 }
2129 _ => panic!(
2130 "Taunt duration cannot be {} when taunt volatile is active",
2131 side.volatile_status_durations.taunt
2132 ),
2133 }
2134 }
2135
2136 if !choice.first_move
2137 && state
2138 .get_side(&attacking_side.get_other_side())
2139 .force_switch
2140 {
2141 state
2142 .get_side(&attacking_side)
2143 .switch_out_move_second_saved_move = choice.move_id;
2144 state.reverse_instructions(&incoming_instructions.instruction_list);
2145 final_instructions.push(incoming_instructions);
2146 return;
2147 }
2148
2149 if state
2150 .get_side_immutable(&attacking_side)
2151 .get_active_immutable()
2152 .hp
2153 == 0
2154 {
2155 state.reverse_instructions(&incoming_instructions.instruction_list);
2156 final_instructions.push(incoming_instructions);
2157 return;
2158 }
2159
2160 if choice.flags.charge {
2162 let side = state.get_side(&attacking_side);
2163 let volatile_status = charge_choice_to_volatile(&choice.move_id);
2164 if side.volatile_statuses.contains(&volatile_status) {
2165 choice.flags.charge = false;
2166 let instruction = Instruction::RemoveVolatileStatus(RemoveVolatileStatusInstruction {
2167 side_ref: attacking_side,
2168 volatile_status: volatile_status,
2169 });
2170 incoming_instructions.instruction_list.push(instruction);
2171 side.volatile_statuses.remove(&volatile_status);
2172 }
2173 }
2174
2175 before_move(
2176 state,
2177 choice,
2178 defender_choice,
2179 &attacking_side,
2180 &mut incoming_instructions,
2181 );
2182 if incoming_instructions.percentage == 0.0 {
2183 state.reverse_instructions(&incoming_instructions.instruction_list);
2184 return;
2185 }
2186
2187 if state.use_last_used_move {
2188 set_last_used_move_as_move(
2189 state.get_side(&attacking_side),
2190 choice.move_index,
2191 attacking_side,
2192 &mut incoming_instructions,
2193 );
2194 }
2195
2196 if cannot_use_move(state, &choice, &attacking_side) {
2197 state.reverse_instructions(&incoming_instructions.instruction_list);
2198 final_instructions.push(incoming_instructions);
2199 return;
2200 }
2201
2202 let (attacker_side, defender_side) = state.get_both_sides(&attacking_side);
2206 let active = attacker_side.get_active();
2207 if active.moves[&choice.move_index].pp < 10 {
2208 let pp_decrement_amount = if choice.target == MoveTarget::Opponent
2209 && defender_side.get_active_immutable().ability == Abilities::PRESSURE
2210 {
2211 2
2212 } else {
2213 1
2214 };
2215 incoming_instructions
2216 .instruction_list
2217 .push(Instruction::DecrementPP(DecrementPPInstruction {
2218 side_ref: attacking_side,
2219 move_index: choice.move_index,
2220 amount: pp_decrement_amount,
2221 }));
2222 active.moves[&choice.move_index].pp -= pp_decrement_amount;
2223 }
2224
2225 if !choice.sleep_talk_move {
2226 generate_instructions_from_existing_status_conditions(
2227 state,
2228 &attacking_side,
2229 &choice,
2230 &mut incoming_instructions,
2231 &mut final_instructions,
2232 );
2233 }
2234 let attacker = state
2235 .get_side_immutable(&attacking_side)
2236 .get_active_immutable();
2237 if choice.move_id == Choices::SLEEPTALK && attacker.status == PokemonStatus::SLEEP {
2238 let new_choices = attacker.get_sleep_talk_choices();
2239 state.reverse_instructions(&incoming_instructions.instruction_list);
2240 let num_choices = new_choices.len() as f32;
2241 for mut new_choice in new_choices {
2242 new_choice.sleep_talk_move = true;
2243 let mut sleep_talk_instructions = incoming_instructions.clone();
2244 sleep_talk_instructions.update_percentage(1.0 / num_choices);
2245 generate_instructions_from_move(
2246 state,
2247 &mut new_choice,
2248 defender_choice,
2249 attacking_side,
2250 sleep_talk_instructions,
2251 &mut final_instructions,
2252 false,
2253 );
2254 }
2255 return;
2256 } else if attacker.status == PokemonStatus::SLEEP && !choice.sleep_talk_move {
2257 state.reverse_instructions(&incoming_instructions.instruction_list);
2258 if incoming_instructions.percentage > 0.0 {
2259 final_instructions.push(incoming_instructions);
2260 }
2261 return;
2262 }
2263
2264 if move_has_no_effect(state, &choice, &attacking_side) {
2265 state.reverse_instructions(&incoming_instructions.instruction_list);
2266 final_instructions.push(incoming_instructions);
2267 return;
2268 }
2269 choice_special_effect(state, choice, &attacking_side, &mut incoming_instructions);
2270 let damage = calculate_damage(state, &attacking_side, &choice, DamageRolls::Max);
2271 check_move_hit_or_miss(
2272 state,
2273 &choice,
2274 &attacking_side,
2275 damage,
2276 &mut incoming_instructions,
2277 &mut final_instructions,
2278 );
2279
2280 if incoming_instructions.percentage == 0.0 {
2281 state.reverse_instructions(&incoming_instructions.instruction_list);
2282 return;
2283 }
2284
2285 let hit_count;
2287 match choice.multi_hit() {
2288 MultiHitMove::None => {
2289 hit_count = 1;
2290 }
2291 MultiHitMove::DoubleHit => {
2292 hit_count = 2;
2293 }
2294 MultiHitMove::TripleHit => {
2295 hit_count = 3;
2296 }
2297 MultiHitMove::TwoToFiveHits => {
2298 hit_count =
2299 if state.get_side(&attacking_side).get_active().ability == Abilities::SKILLLINK {
2300 5
2301 } else if state.get_side(&attacking_side).get_active().item == Items::LOADEDDICE {
2302 4
2303 } else {
2304 3 };
2306 }
2307 MultiHitMove::PopulationBomb => {
2308 hit_count = if state.get_side(&attacking_side).get_active().item == Items::WIDELENS {
2310 9
2311 } else {
2312 6
2313 };
2314 }
2315 MultiHitMove::TripleAxel => {
2316 hit_count = 3
2319 }
2320 }
2321
2322 let (_attacker_side, defender_side) = state.get_both_sides(&attacking_side);
2323 let defender_active = defender_side.get_active();
2324 let mut does_damage = false;
2325 let (mut branch_damage, mut regular_damage) = (0, 0);
2326 let mut branch_instructions: Option<StateInstructions> = None;
2327 if let Some((max_damage_dealt, max_crit_damage)) = damage {
2328 does_damage = true;
2329 let avg_damage_dealt = (max_damage_dealt as f32 * 0.925) as i16;
2330 let min_damage_dealt = (max_damage_dealt as f32 * 0.85) as i16;
2331 if branch_on_damage
2332 && max_damage_dealt >= defender_active.hp
2333 && min_damage_dealt < defender_active.hp
2334 {
2335 let (average_non_kill_damage, num_kill_rolls) =
2336 compare_health_with_damage_multiples(max_damage_dealt, defender_active.hp);
2337
2338 let crit_rate = if defender_active.ability == Abilities::BATTLEARMOR
2339 || defender_active.ability == Abilities::SHELLARMOR
2340 {
2341 0.0
2342 } else if choice.move_id.guaranteed_crit() {
2343 1.0
2344 } else if choice.move_id.increased_crit_ratio() {
2345 1.0 / 8.0
2346 } else {
2347 BASE_CRIT_CHANCE
2348 };
2349
2350 let branch_chance = ((1.0 - crit_rate) * (num_kill_rolls as f32 / 16.0)) + crit_rate;
2352
2353 let mut branch_ins = incoming_instructions.clone();
2354 branch_ins.update_percentage(branch_chance);
2355 branch_instructions = Some(branch_ins);
2356 branch_damage = defender_active.hp;
2357
2358 incoming_instructions.update_percentage(1.0 - branch_chance);
2359 regular_damage = average_non_kill_damage;
2360 } else if branch_on_damage && max_damage_dealt < defender_active.hp {
2361 let crit_rate = if defender_active.ability == Abilities::BATTLEARMOR
2362 || defender_active.ability == Abilities::SHELLARMOR
2363 {
2364 0.0
2365 } else if choice.move_id.guaranteed_crit() {
2366 1.0
2367 } else if choice.move_id.increased_crit_ratio() {
2368 1.0 / 8.0
2369 } else {
2370 BASE_CRIT_CHANCE
2371 };
2372 let mut branch_ins = incoming_instructions.clone();
2373 branch_ins.update_percentage(crit_rate);
2374 branch_instructions = Some(branch_ins);
2375 branch_damage = (max_crit_damage as f32 * 0.925) as i16;
2376 incoming_instructions.update_percentage(1.0 - crit_rate);
2377 regular_damage = (max_damage_dealt as f32 * 0.925) as i16;
2378 } else {
2379 regular_damage = avg_damage_dealt;
2380 }
2381 }
2382
2383 if incoming_instructions.percentage != 0.0 {
2384 run_move(
2385 state,
2386 attacking_side,
2387 incoming_instructions,
2388 hit_count,
2389 does_damage,
2390 regular_damage,
2391 choice,
2392 defender_choice,
2393 &mut final_instructions,
2394 );
2395 } else {
2396 state.reverse_instructions(&incoming_instructions.instruction_list);
2397 }
2398
2399 if let Some(branch_ins) = branch_instructions {
2401 if branch_ins.percentage != 0.0 {
2402 state.apply_instructions(&branch_ins.instruction_list);
2403 run_move(
2404 state,
2405 attacking_side,
2406 branch_ins,
2407 hit_count,
2408 does_damage,
2409 branch_damage,
2410 choice,
2411 defender_choice,
2412 &mut final_instructions,
2413 );
2414 }
2415 }
2416
2417 combine_duplicate_instructions(&mut final_instructions);
2418 return;
2419}
2420
2421fn combine_duplicate_instructions(list_of_instructions: &mut Vec<StateInstructions>) {
2422 for i in 0..list_of_instructions.len() {
2423 let mut j = i + 1;
2424 while j < list_of_instructions.len() {
2425 if list_of_instructions[i].instruction_list == list_of_instructions[j].instruction_list
2426 {
2427 list_of_instructions[i].percentage += list_of_instructions[j].percentage;
2428 list_of_instructions.remove(j);
2429 } else {
2430 j += 1;
2431 }
2432 }
2433 }
2434}
2435
2436fn get_effective_speed(state: &State, side_reference: &SideReference) -> i16 {
2437 let side = state.get_side_immutable(side_reference);
2438 let active_pkmn = side.get_active_immutable();
2439
2440 let mut boosted_speed = side.calculate_boosted_stat(PokemonBoostableStat::Speed) as f32;
2441
2442 match state.weather.weather_type {
2443 Weather::SUN | Weather::HARSHSUN if active_pkmn.ability == Abilities::CHLOROPHYLL => {
2444 boosted_speed *= 2.0
2445 }
2446 Weather::RAIN | Weather::HEAVYRAIN if active_pkmn.ability == Abilities::SWIFTSWIM => {
2447 boosted_speed *= 2.0
2448 }
2449 Weather::SAND if active_pkmn.ability == Abilities::SANDRUSH => boosted_speed *= 2.0,
2450 Weather::HAIL if active_pkmn.ability == Abilities::SLUSHRUSH => boosted_speed *= 2.0,
2451 _ => {}
2452 }
2453
2454 match active_pkmn.ability {
2455 Abilities::SURGESURFER if state.terrain.terrain_type == Terrain::ELECTRICTERRAIN => {
2456 boosted_speed *= 2.0
2457 }
2458 Abilities::UNBURDEN
2459 if side
2460 .volatile_statuses
2461 .contains(&PokemonVolatileStatus::UNBURDEN) =>
2462 {
2463 boosted_speed *= 2.0
2464 }
2465 Abilities::QUICKFEET if active_pkmn.status != PokemonStatus::NONE => boosted_speed *= 1.5,
2466 _ => {}
2467 }
2468
2469 if side
2470 .volatile_statuses
2471 .contains(&PokemonVolatileStatus::SLOWSTART)
2472 {
2473 boosted_speed *= 0.5;
2474 }
2475
2476 if side
2477 .volatile_statuses
2478 .contains(&PokemonVolatileStatus::PROTOSYNTHESISSPE)
2479 || side
2480 .volatile_statuses
2481 .contains(&PokemonVolatileStatus::QUARKDRIVESPE)
2482 {
2483 boosted_speed *= 1.5;
2484 }
2485
2486 if side.side_conditions.tailwind > 0 {
2487 boosted_speed *= 2.0
2488 }
2489
2490 match active_pkmn.item {
2491 Items::IRONBALL => boosted_speed *= 0.5,
2492 Items::CHOICESCARF => boosted_speed *= 1.5,
2493 _ => {}
2494 }
2495
2496 #[cfg(any(feature = "gen3", feature = "gen4", feature = "gen5", feature = "gen6"))]
2497 if active_pkmn.status == PokemonStatus::PARALYZE && active_pkmn.ability != Abilities::QUICKFEET
2498 {
2499 boosted_speed *= 0.25;
2500 }
2501
2502 #[cfg(any(feature = "gen7", feature = "gen8", feature = "gen9"))]
2503 if active_pkmn.status == PokemonStatus::PARALYZE && active_pkmn.ability != Abilities::QUICKFEET
2504 {
2505 boosted_speed *= 0.50;
2506 }
2507
2508 boosted_speed as i16
2509}
2510
2511fn modify_choice_priority(state: &State, side_reference: &SideReference, choice: &mut Choice) {
2512 let side = state.get_side_immutable(side_reference);
2513 let active_pkmn = side.get_active_immutable();
2514
2515 if choice.move_id == Choices::GRASSYGLIDE && state.terrain_is_active(&Terrain::GRASSYTERRAIN) {
2516 choice.priority += 1;
2517 }
2518
2519 match active_pkmn.ability {
2520 Abilities::PRANKSTER if choice.category == MoveCategory::Status => choice.priority += 1,
2521 Abilities::GALEWINGS
2522 if choice.move_type == PokemonType::FLYING && active_pkmn.hp == active_pkmn.maxhp =>
2523 {
2524 choice.priority += 1
2525 }
2526 Abilities::TRIAGE if choice.flags.heal => choice.priority += 3,
2527 _ => {}
2528 }
2529}
2530
2531fn moves_first(
2532 state: &State,
2533 side_one_choice: &Choice,
2534 side_two_choice: &Choice,
2535 incoming_instructions: &mut StateInstructions,
2536) -> SideMovesFirst {
2537 let side_one_effective_speed = get_effective_speed(&state, &SideReference::SideOne);
2538 let side_two_effective_speed = get_effective_speed(&state, &SideReference::SideTwo);
2539
2540 if side_one_choice.category == MoveCategory::Switch
2541 && side_two_choice.category == MoveCategory::Switch
2542 {
2543 return if side_one_effective_speed > side_two_effective_speed {
2544 SideMovesFirst::SideOne
2545 } else if side_one_effective_speed == side_two_effective_speed {
2546 SideMovesFirst::SpeedTie
2547 } else {
2548 SideMovesFirst::SideTwo
2549 };
2550 } else if side_one_choice.category == MoveCategory::Switch {
2551 return if side_two_choice.move_id != Choices::PURSUIT {
2552 SideMovesFirst::SideOne
2553 } else {
2554 SideMovesFirst::SideTwo
2555 };
2556 } else if side_two_choice.category == MoveCategory::Switch {
2557 return if side_one_choice.move_id == Choices::PURSUIT {
2558 SideMovesFirst::SideOne
2559 } else {
2560 SideMovesFirst::SideTwo
2561 };
2562 }
2563
2564 let side_one_active = state.side_one.get_active_immutable();
2565 let side_two_active = state.side_two.get_active_immutable();
2566 if side_one_choice.priority == side_two_choice.priority {
2567 if side_one_active.item == Items::CUSTAPBERRY
2568 && side_one_active.hp < side_one_active.maxhp / 4
2569 {
2570 incoming_instructions
2571 .instruction_list
2572 .push(Instruction::ChangeItem(ChangeItemInstruction {
2573 side_ref: SideReference::SideOne,
2574 new_item: Items::NONE,
2575 current_item: Items::CUSTAPBERRY,
2576 }));
2577 return SideMovesFirst::SideOne;
2578 } else if side_two_active.item == Items::CUSTAPBERRY
2579 && side_two_active.hp < side_two_active.maxhp / 4
2580 {
2581 incoming_instructions
2582 .instruction_list
2583 .push(Instruction::ChangeItem(ChangeItemInstruction {
2584 side_ref: SideReference::SideTwo,
2585 new_item: Items::NONE,
2586 current_item: Items::CUSTAPBERRY,
2587 }));
2588 return SideMovesFirst::SideTwo;
2589 }
2590
2591 if side_one_effective_speed == side_two_effective_speed {
2592 return SideMovesFirst::SpeedTie;
2593 }
2594
2595 match state.trick_room.active {
2596 true => {
2597 if side_one_effective_speed < side_two_effective_speed {
2598 SideMovesFirst::SideOne
2599 } else {
2600 SideMovesFirst::SideTwo
2601 }
2602 }
2603 false => {
2604 if side_one_effective_speed > side_two_effective_speed {
2605 SideMovesFirst::SideOne
2606 } else {
2607 SideMovesFirst::SideTwo
2608 }
2609 }
2610 }
2611 } else {
2612 if side_one_choice.priority > side_two_choice.priority {
2613 SideMovesFirst::SideOne
2614 } else {
2615 SideMovesFirst::SideTwo
2616 }
2617 }
2618}
2619
2620fn get_active_protosynthesis(side: &Side) -> Option<PokemonVolatileStatus> {
2621 if side
2622 .volatile_statuses
2623 .contains(&PokemonVolatileStatus::PROTOSYNTHESISATK)
2624 {
2625 Some(PokemonVolatileStatus::PROTOSYNTHESISATK)
2626 } else if side
2627 .volatile_statuses
2628 .contains(&PokemonVolatileStatus::PROTOSYNTHESISDEF)
2629 {
2630 Some(PokemonVolatileStatus::PROTOSYNTHESISDEF)
2631 } else if side
2632 .volatile_statuses
2633 .contains(&PokemonVolatileStatus::PROTOSYNTHESISSPA)
2634 {
2635 Some(PokemonVolatileStatus::PROTOSYNTHESISSPA)
2636 } else if side
2637 .volatile_statuses
2638 .contains(&PokemonVolatileStatus::PROTOSYNTHESISSPD)
2639 {
2640 Some(PokemonVolatileStatus::PROTOSYNTHESISSPD)
2641 } else if side
2642 .volatile_statuses
2643 .contains(&PokemonVolatileStatus::PROTOSYNTHESISSPE)
2644 {
2645 Some(PokemonVolatileStatus::PROTOSYNTHESISSPE)
2646 } else {
2647 None
2648 }
2649}
2650
2651fn get_active_quarkdrive(side: &Side) -> Option<PokemonVolatileStatus> {
2652 if side
2653 .volatile_statuses
2654 .contains(&PokemonVolatileStatus::QUARKDRIVEATK)
2655 {
2656 Some(PokemonVolatileStatus::QUARKDRIVEATK)
2657 } else if side
2658 .volatile_statuses
2659 .contains(&PokemonVolatileStatus::QUARKDRIVEDEF)
2660 {
2661 Some(PokemonVolatileStatus::QUARKDRIVEDEF)
2662 } else if side
2663 .volatile_statuses
2664 .contains(&PokemonVolatileStatus::QUARKDRIVESPA)
2665 {
2666 Some(PokemonVolatileStatus::QUARKDRIVESPA)
2667 } else if side
2668 .volatile_statuses
2669 .contains(&PokemonVolatileStatus::QUARKDRIVESPD)
2670 {
2671 Some(PokemonVolatileStatus::QUARKDRIVESPD)
2672 } else if side
2673 .volatile_statuses
2674 .contains(&PokemonVolatileStatus::QUARKDRIVESPE)
2675 {
2676 Some(PokemonVolatileStatus::QUARKDRIVESPE)
2677 } else {
2678 None
2679 }
2680}
2681
2682fn on_weather_end(
2683 state: &mut State,
2684 sides: [&SideReference; 2],
2685 incoming_instructions: &mut StateInstructions,
2686) {
2687 match state.weather.weather_type {
2688 Weather::SUN => {
2689 for side_ref in sides {
2690 let side = state.get_side(side_ref);
2691 if side.get_active_immutable().ability == Abilities::PROTOSYNTHESIS {
2692 if let Some(volatile_status) = get_active_protosynthesis(side) {
2693 let active = side.get_active();
2694 if active.item == Items::BOOSTERENERGY {
2695 incoming_instructions
2696 .instruction_list
2697 .push(Instruction::ChangeItem(ChangeItemInstruction {
2698 side_ref: *side_ref,
2699 current_item: Items::BOOSTERENERGY,
2700 new_item: Items::NONE,
2701 }));
2702 active.item = Items::NONE;
2703 } else {
2704 incoming_instructions.instruction_list.push(
2705 Instruction::RemoveVolatileStatus(
2706 RemoveVolatileStatusInstruction {
2707 side_ref: *side_ref,
2708 volatile_status,
2709 },
2710 ),
2711 );
2712 side.volatile_statuses.remove(&volatile_status);
2713 }
2714 }
2715 }
2716 }
2717 }
2718 _ => {}
2719 }
2720}
2721
2722fn on_terrain_end(
2723 state: &mut State,
2724 sides: [&SideReference; 2],
2725 incoming_instructions: &mut StateInstructions,
2726) {
2727 match state.terrain.terrain_type {
2728 Terrain::ELECTRICTERRAIN => {
2729 for side_ref in sides {
2730 let side = state.get_side(side_ref);
2731 if side.get_active_immutable().ability == Abilities::QUARKDRIVE {
2732 if let Some(volatile_status) = get_active_quarkdrive(side) {
2733 let active = side.get_active();
2734 if active.item == Items::BOOSTERENERGY {
2735 incoming_instructions
2736 .instruction_list
2737 .push(Instruction::ChangeItem(ChangeItemInstruction {
2738 side_ref: *side_ref,
2739 current_item: Items::BOOSTERENERGY,
2740 new_item: Items::NONE,
2741 }));
2742 active.item = Items::NONE;
2743 } else {
2744 incoming_instructions.instruction_list.push(
2745 Instruction::RemoveVolatileStatus(
2746 RemoveVolatileStatusInstruction {
2747 side_ref: *side_ref,
2748 volatile_status,
2749 },
2750 ),
2751 );
2752 side.volatile_statuses.remove(&volatile_status);
2753 }
2754 }
2755 }
2756 }
2757 }
2758 _ => {}
2759 }
2760}
2761
2762fn add_end_of_turn_instructions(
2763 state: &mut State,
2764 mut incoming_instructions: &mut StateInstructions,
2765 first_move_side: &SideReference,
2766) {
2767 if state.side_one.force_switch || state.side_two.force_switch {
2768 return;
2769 }
2770
2771 let sides = [first_move_side, &first_move_side.get_other_side()];
2772
2773 if state.weather.turns_remaining > 0 && state.weather.weather_type != Weather::NONE {
2775 let weather_dissipate_instruction = Instruction::DecrementWeatherTurnsRemaining;
2776 incoming_instructions
2777 .instruction_list
2778 .push(weather_dissipate_instruction);
2779 state.weather.turns_remaining -= 1;
2780 if state.weather.turns_remaining == 0 {
2781 on_weather_end(state, sides, &mut incoming_instructions);
2782 let weather_end_instruction = Instruction::ChangeWeather(ChangeWeather {
2783 new_weather: Weather::NONE,
2784 new_weather_turns_remaining: 0,
2785 previous_weather: state.weather.weather_type,
2786 previous_weather_turns_remaining: 0,
2787 });
2788 incoming_instructions
2789 .instruction_list
2790 .push(weather_end_instruction);
2791 state.weather.weather_type = Weather::NONE;
2792 }
2793 }
2794
2795 if state.trick_room.turns_remaining > 0 && state.trick_room.active {
2797 incoming_instructions
2798 .instruction_list
2799 .push(Instruction::DecrementTrickRoomTurnsRemaining);
2800 state.trick_room.turns_remaining -= 1;
2801 if state.trick_room.turns_remaining == 0 {
2802 incoming_instructions
2803 .instruction_list
2804 .push(Instruction::ToggleTrickRoom(ToggleTrickRoomInstruction {
2805 currently_active: true,
2806 new_trickroom_turns_remaining: 0,
2807 previous_trickroom_turns_remaining: 0,
2808 }));
2809 state.trick_room.active = false;
2810 }
2811 }
2812
2813 if state.terrain.turns_remaining > 0 && state.terrain.terrain_type != Terrain::NONE {
2815 if state.terrain.terrain_type == Terrain::GRASSYTERRAIN {
2816 for side_ref in sides {
2817 let side = state.get_side(side_ref);
2818 let active_pkmn = side.get_active();
2819 if active_pkmn.hp == 0 || !active_pkmn.is_grounded() {
2820 continue;
2821 }
2822 let heal_amount = cmp::min(
2823 (active_pkmn.maxhp as f32 * 0.0625) as i16,
2824 active_pkmn.maxhp - active_pkmn.hp,
2825 );
2826 if heal_amount > 0 {
2827 let heal_instruction = Instruction::Heal(HealInstruction {
2828 side_ref: *side_ref,
2829 heal_amount,
2830 });
2831 active_pkmn.hp += heal_amount;
2832 incoming_instructions
2833 .instruction_list
2834 .push(heal_instruction);
2835 }
2836 }
2837 }
2838 let terrain_dissipate_instruction = Instruction::DecrementTerrainTurnsRemaining;
2839 incoming_instructions
2840 .instruction_list
2841 .push(terrain_dissipate_instruction);
2842 state.terrain.turns_remaining -= 1;
2843 if state.terrain.turns_remaining == 0 {
2844 on_terrain_end(state, sides, &mut incoming_instructions);
2845 let terrain_end_instruction = Instruction::ChangeTerrain(ChangeTerrain {
2846 new_terrain: Terrain::NONE,
2847 new_terrain_turns_remaining: 0,
2848 previous_terrain: state.terrain.terrain_type,
2849 previous_terrain_turns_remaining: 0,
2850 });
2851 incoming_instructions
2852 .instruction_list
2853 .push(terrain_end_instruction);
2854 state.terrain.terrain_type = Terrain::NONE;
2855 }
2856 }
2857
2858 for side_ref in sides {
2860 let side = state.get_side(side_ref);
2861 if side.side_conditions.reflect > 0 {
2862 incoming_instructions
2863 .instruction_list
2864 .push(Instruction::ChangeSideCondition(
2865 ChangeSideConditionInstruction {
2866 side_ref: *side_ref,
2867 side_condition: PokemonSideCondition::Reflect,
2868 amount: -1,
2869 },
2870 ));
2871 side.side_conditions.reflect -= 1;
2872 }
2873 if side.side_conditions.light_screen > 0 {
2874 incoming_instructions
2875 .instruction_list
2876 .push(Instruction::ChangeSideCondition(
2877 ChangeSideConditionInstruction {
2878 side_ref: *side_ref,
2879 side_condition: PokemonSideCondition::LightScreen,
2880 amount: -1,
2881 },
2882 ));
2883 side.side_conditions.light_screen -= 1;
2884 }
2885 if side.side_conditions.aurora_veil > 0 {
2886 incoming_instructions
2887 .instruction_list
2888 .push(Instruction::ChangeSideCondition(
2889 ChangeSideConditionInstruction {
2890 side_ref: *side_ref,
2891 side_condition: PokemonSideCondition::AuroraVeil,
2892 amount: -1,
2893 },
2894 ));
2895 side.side_conditions.aurora_veil -= 1;
2896 }
2897 if side.side_conditions.tailwind > 0 {
2898 incoming_instructions
2899 .instruction_list
2900 .push(Instruction::ChangeSideCondition(
2901 ChangeSideConditionInstruction {
2902 side_ref: *side_ref,
2903 side_condition: PokemonSideCondition::Tailwind,
2904 amount: -1,
2905 },
2906 ));
2907 side.side_conditions.tailwind -= 1;
2908 }
2909 }
2910
2911 for side_ref in sides {
2913 if state.weather_is_active(&Weather::HAIL) {
2914 let active_pkmn = state.get_side(side_ref).get_active();
2915 if active_pkmn.hp == 0
2916 || active_pkmn.ability == Abilities::MAGICGUARD
2917 || active_pkmn.ability == Abilities::OVERCOAT
2918 || active_pkmn.ability == Abilities::ICEBODY
2919 || active_pkmn.has_type(&PokemonType::ICE)
2920 {
2921 continue;
2922 }
2923
2924 let damage_amount =
2925 cmp::min((active_pkmn.maxhp as f32 * 0.0625) as i16, active_pkmn.hp);
2926 let hail_damage_instruction = Instruction::Damage(DamageInstruction {
2927 side_ref: *side_ref,
2928 damage_amount: damage_amount,
2929 });
2930
2931 active_pkmn.hp -= damage_amount;
2932 incoming_instructions
2933 .instruction_list
2934 .push(hail_damage_instruction);
2935 } else if state.weather_is_active(&Weather::SAND) {
2936 let active_pkmn = state.get_side(side_ref).get_active();
2937 if active_pkmn.hp == 0
2938 || active_pkmn.ability == Abilities::MAGICGUARD
2939 || active_pkmn.ability == Abilities::OVERCOAT
2940 || active_pkmn.has_type(&PokemonType::GROUND)
2941 || active_pkmn.has_type(&PokemonType::STEEL)
2942 || active_pkmn.has_type(&PokemonType::ROCK)
2943 {
2944 continue;
2945 }
2946 let damage_amount =
2947 cmp::min((active_pkmn.maxhp as f32 * 0.0625) as i16, active_pkmn.hp);
2948 let sand_damage_instruction = Instruction::Damage(DamageInstruction {
2949 side_ref: *side_ref,
2950 damage_amount: damage_amount,
2951 });
2952 active_pkmn.hp -= damage_amount;
2953 incoming_instructions
2954 .instruction_list
2955 .push(sand_damage_instruction);
2956 }
2957 }
2958
2959 for side_ref in sides {
2961 let (attacking_side, defending_side) = state.get_both_sides(side_ref);
2962 if attacking_side.future_sight.0 > 0 {
2963 let decrement_future_sight_instruction =
2964 Instruction::DecrementFutureSight(DecrementFutureSightInstruction {
2965 side_ref: *side_ref,
2966 });
2967 if attacking_side.future_sight.0 == 1 {
2968 let mut damage = calculate_futuresight_damage(
2969 &attacking_side,
2970 &defending_side,
2971 &attacking_side.future_sight.1,
2972 );
2973 let defender = defending_side.get_active();
2974 damage = cmp::min(damage, defender.hp);
2975 let future_sight_damage_instruction = Instruction::Damage(DamageInstruction {
2976 side_ref: side_ref.get_other_side(),
2977 damage_amount: damage,
2978 });
2979 incoming_instructions
2980 .instruction_list
2981 .push(future_sight_damage_instruction);
2982 defender.hp -= damage;
2983 }
2984 attacking_side.future_sight.0 -= 1;
2985 incoming_instructions
2986 .instruction_list
2987 .push(decrement_future_sight_instruction);
2988 }
2989 }
2990
2991 for side_ref in sides {
2993 let side = state.get_side(side_ref);
2994 let side_wish = side.wish;
2995 let active_pkmn = side.get_active();
2996
2997 if side_wish.0 > 0 {
2998 let decrement_wish_instruction = Instruction::DecrementWish(DecrementWishInstruction {
2999 side_ref: *side_ref,
3000 });
3001 if side_wish.0 == 1 && 0 < active_pkmn.hp && active_pkmn.hp < active_pkmn.maxhp {
3002 #[cfg(not(feature = "gen4"))]
3003 let heal_amount = cmp::min(active_pkmn.maxhp - active_pkmn.hp, side_wish.1);
3004
3005 #[cfg(feature = "gen4")]
3006 let heal_amount =
3007 cmp::min(active_pkmn.maxhp - active_pkmn.hp, active_pkmn.maxhp / 2);
3008
3009 let wish_heal_instruction = Instruction::Heal(HealInstruction {
3010 side_ref: *side_ref,
3011 heal_amount: heal_amount,
3012 });
3013 incoming_instructions
3014 .instruction_list
3015 .push(wish_heal_instruction);
3016 active_pkmn.hp += heal_amount;
3017 }
3018 side.wish.0 -= 1;
3019 incoming_instructions
3020 .instruction_list
3021 .push(decrement_wish_instruction);
3022 }
3023 }
3024
3025 for side_ref in sides {
3027 let (side, other_side) = state.get_both_sides(side_ref);
3028 let toxic_count = side.side_conditions.toxic_count as f32;
3029 let active_pkmn = side.get_active();
3030 let other_side_active = other_side.get_active();
3031 if active_pkmn.hp == 0 || active_pkmn.ability == Abilities::MAGICGUARD {
3032 continue;
3033 }
3034
3035 match active_pkmn.status {
3036 PokemonStatus::BURN => {
3037 #[cfg(any(feature = "gen3", feature = "gen4", feature = "gen5", feature = "gen6"))]
3038 let mut damage_factor = 0.125;
3039
3040 #[cfg(any(feature = "gen7", feature = "gen8", feature = "gen9",))]
3041 let mut damage_factor = 0.0625;
3042
3043 if active_pkmn.ability == Abilities::HEATPROOF {
3044 damage_factor /= 2.0;
3045 }
3046 let damage_amount = cmp::max(
3047 cmp::min(
3048 (active_pkmn.maxhp as f32 * damage_factor) as i16,
3049 active_pkmn.hp,
3050 ),
3051 1,
3052 );
3053 let burn_damage_instruction = Instruction::Damage(DamageInstruction {
3054 side_ref: *side_ref,
3055 damage_amount: damage_amount,
3056 });
3057 active_pkmn.hp -= damage_amount;
3058 incoming_instructions
3059 .instruction_list
3060 .push(burn_damage_instruction);
3061 }
3062 PokemonStatus::POISON if active_pkmn.ability != Abilities::POISONHEAL => {
3063 let damage_amount = cmp::max(
3064 1,
3065 cmp::min((active_pkmn.maxhp as f32 * 0.125) as i16, active_pkmn.hp),
3066 );
3067
3068 let poison_damage_instruction = Instruction::Damage(DamageInstruction {
3069 side_ref: *side_ref,
3070 damage_amount: damage_amount,
3071 });
3072 active_pkmn.hp -= damage_amount;
3073 incoming_instructions
3074 .instruction_list
3075 .push(poison_damage_instruction);
3076 }
3077 PokemonStatus::TOXIC => {
3078 if active_pkmn.ability != Abilities::POISONHEAL
3079 || other_side_active.ability == Abilities::NEUTRALIZINGGAS
3080 {
3081 let toxic_multiplier = (1.0 / 16.0) * toxic_count + (1.0 / 16.0);
3082 let damage_amount = cmp::max(
3083 cmp::min(
3084 (active_pkmn.maxhp as f32 * toxic_multiplier) as i16,
3085 active_pkmn.hp,
3086 ),
3087 1,
3088 );
3089 let toxic_damage_instruction = Instruction::Damage(DamageInstruction {
3090 side_ref: *side_ref,
3091 damage_amount,
3092 });
3093
3094 active_pkmn.hp -= damage_amount;
3095 incoming_instructions
3096 .instruction_list
3097 .push(toxic_damage_instruction);
3098 }
3099
3100 let toxic_counter_increment_instruction =
3102 Instruction::ChangeSideCondition(ChangeSideConditionInstruction {
3103 side_ref: *side_ref,
3104 side_condition: PokemonSideCondition::ToxicCount,
3105 amount: 1,
3106 });
3107 side.side_conditions.toxic_count += 1;
3108 incoming_instructions
3109 .instruction_list
3110 .push(toxic_counter_increment_instruction);
3111 }
3112 _ => {}
3113 }
3114 }
3115
3116 for side_ref in sides {
3118 let side = state.get_side(side_ref);
3119 let active_pkmn = side.get_active();
3120 if active_pkmn.hp == 0 {
3121 continue;
3122 }
3123
3124 item_end_of_turn(state, side_ref, &mut incoming_instructions);
3125 ability_end_of_turn(state, side_ref, &mut incoming_instructions);
3126 }
3127
3128 for side_ref in sides {
3130 let (leechseed_side, other_side) = state.get_both_sides(side_ref);
3131 if leechseed_side
3132 .volatile_statuses
3133 .contains(&PokemonVolatileStatus::LEECHSEED)
3134 {
3135 let active_pkmn = leechseed_side.get_active();
3136 let other_active_pkmn = other_side.get_active();
3137 if active_pkmn.hp == 0
3138 || other_active_pkmn.hp == 0
3139 || active_pkmn.ability == Abilities::MAGICGUARD
3140 {
3141 continue;
3142 }
3143
3144 let health_sapped = cmp::min((active_pkmn.maxhp as f32 * 0.125) as i16, active_pkmn.hp);
3145 let damage_ins = Instruction::Damage(DamageInstruction {
3146 side_ref: *side_ref,
3147 damage_amount: health_sapped,
3148 });
3149 active_pkmn.hp -= health_sapped;
3150 incoming_instructions.instruction_list.push(damage_ins);
3151
3152 let health_recovered = cmp::min(
3153 health_sapped,
3154 other_active_pkmn.maxhp - other_active_pkmn.hp,
3155 );
3156 if health_recovered > 0 {
3157 let heal_ins = Instruction::Heal(HealInstruction {
3158 side_ref: side_ref.get_other_side(),
3159 heal_amount: health_recovered,
3160 });
3161 other_active_pkmn.hp += health_recovered;
3162 incoming_instructions.instruction_list.push(heal_ins);
3163 }
3164 }
3165 }
3166
3167 for side_ref in sides {
3169 let side = state.get_side(side_ref);
3170 if side.get_active().hp == 0 {
3171 continue;
3172 }
3173
3174 if side
3175 .volatile_statuses
3176 .contains(&PokemonVolatileStatus::SLOWSTART)
3177 {
3178 incoming_instructions
3179 .instruction_list
3180 .push(Instruction::ChangeVolatileStatusDuration(
3181 ChangeVolatileStatusDurationInstruction {
3182 side_ref: *side_ref,
3183 volatile_status: PokemonVolatileStatus::SLOWSTART,
3184 amount: -1,
3185 },
3186 ));
3187 side.volatile_status_durations.slowstart -= 1;
3188 if side.volatile_status_durations.slowstart == 0 {
3189 incoming_instructions
3190 .instruction_list
3191 .push(Instruction::RemoveVolatileStatus(
3192 RemoveVolatileStatusInstruction {
3193 side_ref: *side_ref,
3194 volatile_status: PokemonVolatileStatus::SLOWSTART,
3195 },
3196 ));
3197 side.volatile_statuses
3198 .remove(&PokemonVolatileStatus::SLOWSTART);
3199 }
3200 }
3201
3202 if side
3203 .volatile_statuses
3204 .contains(&PokemonVolatileStatus::LOCKEDMOVE)
3205 {
3206 if side.volatile_status_durations.lockedmove == 2 {
3210 side.volatile_status_durations.lockedmove = 0;
3211 side.volatile_statuses
3212 .remove(&PokemonVolatileStatus::LOCKEDMOVE);
3213 incoming_instructions.instruction_list.push(
3214 Instruction::ChangeVolatileStatusDuration(
3215 ChangeVolatileStatusDurationInstruction {
3216 side_ref: *side_ref,
3217 volatile_status: PokemonVolatileStatus::LOCKEDMOVE,
3218 amount: -2,
3219 },
3220 ),
3221 );
3222 incoming_instructions
3223 .instruction_list
3224 .push(Instruction::RemoveVolatileStatus(
3225 RemoveVolatileStatusInstruction {
3226 side_ref: *side_ref,
3227 volatile_status: PokemonVolatileStatus::LOCKEDMOVE,
3228 },
3229 ));
3230 if !side
3231 .volatile_statuses
3232 .contains(&PokemonVolatileStatus::CONFUSION)
3233 {
3234 incoming_instructions
3235 .instruction_list
3236 .push(Instruction::ApplyVolatileStatus(
3237 ApplyVolatileStatusInstruction {
3238 side_ref: *side_ref,
3239 volatile_status: PokemonVolatileStatus::CONFUSION,
3240 },
3241 ));
3242 side.volatile_statuses
3243 .insert(PokemonVolatileStatus::CONFUSION);
3244 }
3245 } else {
3246 side.volatile_status_durations.lockedmove += 1;
3247 incoming_instructions.instruction_list.push(
3248 Instruction::ChangeVolatileStatusDuration(
3249 ChangeVolatileStatusDurationInstruction {
3250 side_ref: *side_ref,
3251 volatile_status: PokemonVolatileStatus::LOCKEDMOVE,
3252 amount: 1,
3253 },
3254 ),
3255 );
3256 }
3257 }
3258
3259 if side
3260 .volatile_statuses
3261 .contains(&PokemonVolatileStatus::YAWN)
3262 {
3263 match side.volatile_status_durations.yawn {
3264 0 => {
3265 incoming_instructions.instruction_list.push(
3266 Instruction::ChangeVolatileStatusDuration(
3267 ChangeVolatileStatusDurationInstruction {
3268 side_ref: *side_ref,
3269 volatile_status: PokemonVolatileStatus::YAWN,
3270 amount: 1,
3271 },
3272 ),
3273 );
3274 side.volatile_status_durations.yawn += 1;
3275 }
3276 1 => {
3277 side.volatile_statuses.remove(&PokemonVolatileStatus::YAWN);
3278 incoming_instructions
3279 .instruction_list
3280 .push(Instruction::RemoveVolatileStatus(
3281 RemoveVolatileStatusInstruction {
3282 side_ref: *side_ref,
3283 volatile_status: PokemonVolatileStatus::YAWN,
3284 },
3285 ));
3286 incoming_instructions.instruction_list.push(
3287 Instruction::ChangeVolatileStatusDuration(
3288 ChangeVolatileStatusDurationInstruction {
3289 side_ref: *side_ref,
3290 volatile_status: PokemonVolatileStatus::YAWN,
3291 amount: -1,
3292 },
3293 ),
3294 );
3295 side.volatile_status_durations.yawn -= 1;
3296
3297 let active = side.get_active();
3298 if active.status == PokemonStatus::NONE {
3299 active.status = PokemonStatus::SLEEP;
3300 incoming_instructions
3301 .instruction_list
3302 .push(Instruction::ChangeStatus(ChangeStatusInstruction {
3303 side_ref: *side_ref,
3304 pokemon_index: side.active_index,
3305 old_status: PokemonStatus::NONE,
3306 new_status: PokemonStatus::SLEEP,
3307 }));
3308 }
3309 }
3310 _ => panic!(
3311 "Yawn duration cannot be {} when yawn volatile is active",
3312 side.volatile_status_durations.yawn
3313 ),
3314 }
3315 }
3316
3317 if side
3318 .volatile_statuses
3319 .contains(&PokemonVolatileStatus::PERISH1)
3320 {
3321 let active_pkmn = side.get_active();
3322 incoming_instructions
3323 .instruction_list
3324 .push(Instruction::Damage(DamageInstruction {
3325 side_ref: *side_ref,
3326 damage_amount: active_pkmn.hp,
3327 }));
3328 active_pkmn.hp = 0;
3329 }
3330
3331 if side
3332 .volatile_statuses
3333 .remove(&PokemonVolatileStatus::PERISH2)
3334 {
3335 side.volatile_statuses
3336 .insert(PokemonVolatileStatus::PERISH1);
3337 incoming_instructions
3338 .instruction_list
3339 .push(Instruction::RemoveVolatileStatus(
3340 RemoveVolatileStatusInstruction {
3341 side_ref: *side_ref,
3342 volatile_status: PokemonVolatileStatus::PERISH2,
3343 },
3344 ));
3345 incoming_instructions
3346 .instruction_list
3347 .push(Instruction::ApplyVolatileStatus(
3348 ApplyVolatileStatusInstruction {
3349 side_ref: *side_ref,
3350 volatile_status: PokemonVolatileStatus::PERISH1,
3351 },
3352 ));
3353 }
3354 if side
3355 .volatile_statuses
3356 .remove(&PokemonVolatileStatus::PERISH3)
3357 {
3358 side.volatile_statuses
3359 .insert(PokemonVolatileStatus::PERISH2);
3360 incoming_instructions
3361 .instruction_list
3362 .push(Instruction::RemoveVolatileStatus(
3363 RemoveVolatileStatusInstruction {
3364 side_ref: *side_ref,
3365 volatile_status: PokemonVolatileStatus::PERISH3,
3366 },
3367 ));
3368 incoming_instructions
3369 .instruction_list
3370 .push(Instruction::ApplyVolatileStatus(
3371 ApplyVolatileStatusInstruction {
3372 side_ref: *side_ref,
3373 volatile_status: PokemonVolatileStatus::PERISH2,
3374 },
3375 ));
3376 }
3377 if side
3378 .volatile_statuses
3379 .remove(&PokemonVolatileStatus::PERISH4)
3380 {
3381 side.volatile_statuses
3382 .insert(PokemonVolatileStatus::PERISH3);
3383 incoming_instructions
3384 .instruction_list
3385 .push(Instruction::RemoveVolatileStatus(
3386 RemoveVolatileStatusInstruction {
3387 side_ref: *side_ref,
3388 volatile_status: PokemonVolatileStatus::PERISH4,
3389 },
3390 ));
3391 incoming_instructions
3392 .instruction_list
3393 .push(Instruction::ApplyVolatileStatus(
3394 ApplyVolatileStatusInstruction {
3395 side_ref: *side_ref,
3396 volatile_status: PokemonVolatileStatus::PERISH3,
3397 },
3398 ));
3399 }
3400
3401 if side
3402 .volatile_statuses
3403 .remove(&PokemonVolatileStatus::FLINCH)
3404 {
3405 incoming_instructions
3406 .instruction_list
3407 .push(Instruction::RemoveVolatileStatus(
3408 RemoveVolatileStatusInstruction {
3409 side_ref: *side_ref,
3410 volatile_status: PokemonVolatileStatus::FLINCH,
3411 },
3412 ));
3413 }
3414 if side.volatile_statuses.remove(&PokemonVolatileStatus::ROOST) {
3415 incoming_instructions
3416 .instruction_list
3417 .push(Instruction::RemoveVolatileStatus(
3418 RemoveVolatileStatusInstruction {
3419 side_ref: *side_ref,
3420 volatile_status: PokemonVolatileStatus::ROOST,
3421 },
3422 ));
3423 }
3424
3425 if side
3426 .volatile_statuses
3427 .contains(&PokemonVolatileStatus::PARTIALLYTRAPPED)
3428 {
3429 let active_pkmn = side.get_active();
3430
3431 #[cfg(any(feature = "gen3", feature = "gen4", feature = "gen5"))]
3432 let damage_amount = cmp::min((active_pkmn.maxhp as f32 / 16.0) as i16, active_pkmn.hp);
3433
3434 #[cfg(any(feature = "gen6", feature = "gen7", feature = "gen8", feature = "gen9"))]
3435 let damage_amount = cmp::min((active_pkmn.maxhp as f32 / 8.0) as i16, active_pkmn.hp);
3436
3437 incoming_instructions
3438 .instruction_list
3439 .push(Instruction::Damage(DamageInstruction {
3440 side_ref: *side_ref,
3441 damage_amount,
3442 }));
3443 active_pkmn.hp -= damage_amount;
3444 }
3445 if side
3446 .volatile_statuses
3447 .contains(&PokemonVolatileStatus::SALTCURE)
3448 {
3449 let active_pkmn = side.get_active();
3450 let mut divisor = 8.0;
3451 if active_pkmn.has_type(&PokemonType::WATER)
3452 || active_pkmn.has_type(&PokemonType::STEEL)
3453 {
3454 divisor = 4.0;
3455 }
3456 let damage_amount =
3457 cmp::min((active_pkmn.maxhp as f32 / divisor) as i16, active_pkmn.hp);
3458 incoming_instructions
3459 .instruction_list
3460 .push(Instruction::Damage(DamageInstruction {
3461 side_ref: *side_ref,
3462 damage_amount: damage_amount,
3463 }));
3464 active_pkmn.hp -= damage_amount;
3465 }
3466
3467 let possible_statuses = [
3468 PokemonVolatileStatus::PROTECT,
3469 PokemonVolatileStatus::BANEFULBUNKER,
3470 PokemonVolatileStatus::BURNINGBULWARK,
3471 PokemonVolatileStatus::SPIKYSHIELD,
3472 PokemonVolatileStatus::SILKTRAP,
3473 PokemonVolatileStatus::ENDURE,
3474 ];
3475
3476 let mut protect_vs = None;
3477 for status in &possible_statuses {
3478 if side.volatile_statuses.contains(status) {
3479 protect_vs = Some(*status);
3480 break;
3481 }
3482 }
3483
3484 if let Some(protect_vs) = protect_vs {
3485 incoming_instructions
3486 .instruction_list
3487 .push(Instruction::RemoveVolatileStatus(
3488 RemoveVolatileStatusInstruction {
3489 side_ref: *side_ref,
3490 volatile_status: protect_vs,
3491 },
3492 ));
3493 side.volatile_statuses.remove(&protect_vs);
3494 incoming_instructions
3495 .instruction_list
3496 .push(Instruction::ChangeSideCondition(
3497 ChangeSideConditionInstruction {
3498 side_ref: *side_ref,
3499 side_condition: PokemonSideCondition::Protect,
3500 amount: 1,
3501 },
3502 ));
3503 side.side_conditions.protect += 1;
3504 } else if side.side_conditions.protect > 0 {
3505 incoming_instructions
3506 .instruction_list
3507 .push(Instruction::ChangeSideCondition(
3508 ChangeSideConditionInstruction {
3509 side_ref: *side_ref,
3510 side_condition: PokemonSideCondition::Protect,
3511 amount: -1 * side.side_conditions.protect,
3512 },
3513 ));
3514 side.side_conditions.protect -= side.side_conditions.protect;
3515 }
3516 } }
3518
3519fn run_move(
3520 state: &mut State,
3521 attacking_side: SideReference,
3522 mut instructions: StateInstructions,
3523 hit_count: i8,
3524 does_damage: bool,
3525 damage_amount: i16,
3526 choice: &mut Choice,
3527 defender_choice: &Choice,
3528 final_instructions: &mut Vec<StateInstructions>,
3529) {
3530 let mut hit_sub = false;
3531 for _ in 0..hit_count {
3532 if does_damage {
3533 hit_sub = generate_instructions_from_damage(
3534 state,
3535 choice,
3536 damage_amount,
3537 &attacking_side,
3538 &mut instructions,
3539 );
3540 }
3541 if let Some(side_condition) = &choice.side_condition {
3542 generate_instructions_from_side_conditions(
3543 state,
3544 side_condition,
3545 &attacking_side,
3546 &mut instructions,
3547 );
3548 }
3549 choice_hazard_clear(state, &choice, &attacking_side, &mut instructions);
3550 if let Some(volatile_status) = &choice.volatile_status {
3551 get_instructions_from_volatile_statuses(
3552 state,
3553 &choice,
3554 volatile_status,
3555 &attacking_side,
3556 &mut instructions,
3557 );
3558 }
3559 if let Some(status) = &choice.status {
3560 get_instructions_from_status_effects(
3561 state,
3562 status,
3563 &attacking_side,
3564 &mut instructions,
3565 hit_sub,
3566 );
3567 }
3568 if let Some(heal) = &choice.heal {
3569 get_instructions_from_heal(state, heal, &attacking_side, &mut instructions);
3570 }
3571 } if let Some(boost) = &choice.boost {
3578 get_instructions_from_boosts(state, boost, &attacking_side, &mut instructions);
3579 }
3580
3581 if choice.flags.drag
3582 && state
3583 .get_side_immutable(&attacking_side.get_other_side())
3584 .get_active_immutable()
3585 .ability
3586 != Abilities::GUARDDOG
3587 {
3588 get_instructions_from_drag(state, &attacking_side, instructions, final_instructions);
3589 return;
3590 }
3591
3592 if choice.flags.pivot {
3596 match attacking_side {
3597 SideReference::SideOne => {
3598 if state.side_one.visible_alive_pkmn() > 1 {
3599 if choice.move_id == Choices::BATONPASS {
3600 state.side_one.baton_passing = !state.side_one.baton_passing;
3601 instructions
3602 .instruction_list
3603 .push(Instruction::ToggleBatonPassing(
3604 ToggleBatonPassingInstruction {
3605 side_ref: SideReference::SideOne,
3606 },
3607 ));
3608 } else if choice.move_id == Choices::SHEDTAIL {
3609 state.side_one.shed_tailing = !state.side_one.shed_tailing;
3610 instructions
3611 .instruction_list
3612 .push(Instruction::ToggleShedTailing(
3613 ToggleShedTailingInstruction {
3614 side_ref: SideReference::SideOne,
3615 },
3616 ));
3617 }
3618 state.side_one.force_switch = !state.side_one.force_switch;
3619 instructions
3620 .instruction_list
3621 .push(Instruction::ToggleSideOneForceSwitch);
3622
3623 if choice.first_move {
3624 instructions.instruction_list.push(
3625 Instruction::SetSideTwoMoveSecondSwitchOutMove(
3626 SetSecondMoveSwitchOutMoveInstruction {
3627 new_choice: defender_choice.move_id,
3628 previous_choice: state
3629 .side_two
3630 .switch_out_move_second_saved_move,
3631 },
3632 ),
3633 );
3634 state.side_two.switch_out_move_second_saved_move = defender_choice.move_id;
3635 } else {
3636 instructions.instruction_list.push(
3637 Instruction::SetSideTwoMoveSecondSwitchOutMove(
3638 SetSecondMoveSwitchOutMoveInstruction {
3639 new_choice: Choices::NONE,
3640 previous_choice: state
3641 .side_two
3642 .switch_out_move_second_saved_move,
3643 },
3644 ),
3645 );
3646 state.side_two.switch_out_move_second_saved_move = defender_choice.move_id;
3647 }
3648 }
3649 }
3650 SideReference::SideTwo => {
3651 if state.side_two.visible_alive_pkmn() > 1 {
3652 if choice.move_id == Choices::BATONPASS {
3653 state.side_two.baton_passing = !state.side_two.baton_passing;
3654 instructions
3655 .instruction_list
3656 .push(Instruction::ToggleBatonPassing(
3657 ToggleBatonPassingInstruction {
3658 side_ref: SideReference::SideTwo,
3659 },
3660 ));
3661 } else if choice.move_id == Choices::SHEDTAIL {
3662 state.side_two.shed_tailing = !state.side_two.shed_tailing;
3663 instructions
3664 .instruction_list
3665 .push(Instruction::ToggleShedTailing(
3666 ToggleShedTailingInstruction {
3667 side_ref: SideReference::SideTwo,
3668 },
3669 ));
3670 }
3671 state.side_two.force_switch = !state.side_two.force_switch;
3672 instructions
3673 .instruction_list
3674 .push(Instruction::ToggleSideTwoForceSwitch);
3675
3676 if choice.first_move {
3677 instructions.instruction_list.push(
3678 Instruction::SetSideOneMoveSecondSwitchOutMove(
3679 SetSecondMoveSwitchOutMoveInstruction {
3680 new_choice: defender_choice.move_id,
3681 previous_choice: state
3682 .side_one
3683 .switch_out_move_second_saved_move,
3684 },
3685 ),
3686 );
3687 state.side_one.switch_out_move_second_saved_move = defender_choice.move_id;
3688 } else {
3689 instructions.instruction_list.push(
3690 Instruction::SetSideOneMoveSecondSwitchOutMove(
3691 SetSecondMoveSwitchOutMoveInstruction {
3692 new_choice: Choices::NONE,
3693 previous_choice: state
3694 .side_one
3695 .switch_out_move_second_saved_move,
3696 },
3697 ),
3698 );
3699 state.side_one.switch_out_move_second_saved_move = defender_choice.move_id;
3700 }
3701 }
3702 }
3703 }
3704 }
3705
3706 if state
3707 .get_side_immutable(&attacking_side.get_other_side())
3708 .get_active_immutable()
3709 .item
3710 == Items::COVERTCLOAK
3711 {
3712 state.reverse_instructions(&instructions.instruction_list);
3713 final_instructions.push(instructions);
3714 } else if let Some(secondaries_vec) = &choice.secondaries {
3715 state.reverse_instructions(&instructions.instruction_list);
3716 let instructions_vec_after_secondaries = get_instructions_from_secondaries(
3717 state,
3718 &choice,
3719 secondaries_vec,
3720 &attacking_side,
3721 instructions,
3722 hit_sub,
3723 );
3724 final_instructions.extend(instructions_vec_after_secondaries);
3725 } else {
3726 state.reverse_instructions(&instructions.instruction_list);
3727 final_instructions.push(instructions);
3728 }
3729}
3730
3731fn after_move_finish(state: &mut State, final_instructions: &mut Vec<StateInstructions>) {
3732 for state_instructions in final_instructions.iter_mut() {
3733 state.apply_instructions(&state_instructions.instruction_list);
3734
3735 for side_ref in [SideReference::SideOne, SideReference::SideTwo] {
3738 let side = state.get_side(&side_ref);
3739 let active_has_whiteherb = side.get_active_immutable().item == Items::WHITEHERB;
3740 if active_has_whiteherb {
3741 if side.reset_negative_boosts(side_ref, state_instructions) {
3742 let active = side.get_active();
3743 active.item = Items::NONE;
3744 state_instructions
3745 .instruction_list
3746 .push(Instruction::ChangeItem(ChangeItemInstruction {
3747 side_ref,
3748 current_item: Items::WHITEHERB,
3749 new_item: Items::NONE,
3750 }));
3751 }
3752 }
3753 }
3754 state.reverse_instructions(&state_instructions.instruction_list);
3755 }
3756}
3757
3758fn handle_both_moves(
3759 state: &mut State,
3760 first_move_side_choice: &mut Choice,
3761 second_move_side_choice: &mut Choice,
3762 first_move_side_ref: SideReference,
3763 incoming_instructions: StateInstructions,
3764 state_instructions_vec: &mut Vec<StateInstructions>,
3765 branch_on_damage: bool,
3766) {
3767 generate_instructions_from_move(
3768 state,
3769 first_move_side_choice,
3770 second_move_side_choice,
3771 first_move_side_ref,
3772 incoming_instructions,
3773 state_instructions_vec,
3774 branch_on_damage,
3775 );
3776 after_move_finish(state, state_instructions_vec);
3777
3778 let mut i = 0;
3779 let vec_len = state_instructions_vec.len();
3780 second_move_side_choice.first_move = false;
3781 while i < vec_len {
3782 let state_instruction = state_instructions_vec.remove(0);
3783 generate_instructions_from_move(
3784 state,
3785 &mut second_move_side_choice.clone(), first_move_side_choice,
3787 first_move_side_ref.get_other_side(),
3788 state_instruction,
3789 state_instructions_vec,
3790 branch_on_damage,
3791 );
3792 after_move_finish(state, state_instructions_vec);
3793 i += 1;
3794 }
3795}
3796
3797fn mega_evolve(state: &mut State, side_ref: SideReference, instructions: &mut StateInstructions) {
3798 let side = state.get_side(&side_ref);
3799 let active_pkmn = side.get_active();
3800
3801 let mega_evolve_data = active_pkmn
3803 .id
3804 .mega_evolve_target(active_pkmn.item)
3805 .unwrap_or_else(|| {
3806 panic!(
3807 "cannot mega evolve {:?} with {:?}",
3808 active_pkmn.id, active_pkmn.item
3809 )
3810 });
3811
3812 instructions
3814 .instruction_list
3815 .push(Instruction::FormeChange(FormeChangeInstruction {
3816 side_ref,
3817 name_change: mega_evolve_data.id as i16 - active_pkmn.id as i16,
3818 }));
3819 active_pkmn.id = mega_evolve_data.id;
3820
3821 active_pkmn.recalculate_stats(&side_ref, instructions);
3823
3824 if mega_evolve_data.ability != active_pkmn.ability {
3826 instructions
3827 .instruction_list
3828 .push(Instruction::ChangeAbility(ChangeAbilityInstruction {
3829 side_ref,
3830 ability_change: mega_evolve_data.ability as i16 - active_pkmn.ability as i16,
3831 }));
3832 active_pkmn.ability = mega_evolve_data.ability;
3833 }
3834 if mega_evolve_data.types != active_pkmn.types {
3836 instructions
3837 .instruction_list
3838 .push(Instruction::ChangeType(ChangeType {
3839 side_ref,
3840 new_types: mega_evolve_data.types,
3841 old_types: active_pkmn.types,
3842 }));
3843 active_pkmn.types = mega_evolve_data.types;
3844 }
3845
3846 ability_on_switch_in(state, &side_ref, instructions);
3848}
3849
3850pub fn generate_instructions_from_move_pair(
3851 state: &mut State,
3852 side_one_move: &MoveChoice,
3853 side_two_move: &MoveChoice,
3854 branch_on_damage: bool,
3855) -> Vec<StateInstructions> {
3856 let mut side_one_choice;
3857 let mut s1_tera = false;
3858 let mut s1_mega = false;
3859 let mut s1_replacing_fainted_pkmn = false;
3860 match side_one_move {
3861 MoveChoice::Switch(switch_id) => {
3862 if state.side_one.get_active().hp == 0 {
3863 s1_replacing_fainted_pkmn = true;
3864 }
3865 side_one_choice = Choice::default();
3866 side_one_choice.switch_id = *switch_id;
3867 side_one_choice.category = MoveCategory::Switch;
3868 }
3869 MoveChoice::Move(move_index) => {
3870 side_one_choice = state.side_one.get_active().moves[move_index].choice.clone();
3871 side_one_choice.move_index = *move_index;
3872 }
3873 MoveChoice::MoveTera(move_index) => {
3874 side_one_choice = state.side_one.get_active().moves[move_index].choice.clone();
3875 side_one_choice.move_index = *move_index;
3876 s1_tera = true;
3877 }
3878 MoveChoice::MoveMega(move_index) => {
3879 side_one_choice = state.side_one.get_active().moves[move_index].choice.clone();
3880 side_one_choice.move_index = *move_index;
3881 s1_mega = true;
3882 }
3883 MoveChoice::None => {
3884 side_one_choice = Choice::default();
3885 }
3886 }
3887
3888 let mut side_two_choice;
3889 let mut s2_replacing_fainted_pkmn = false;
3890 let mut s2_tera = false;
3891 let mut s2_mega = false;
3892 match side_two_move {
3893 MoveChoice::Switch(switch_id) => {
3894 if state.side_two.get_active().hp == 0 {
3895 s2_replacing_fainted_pkmn = true;
3896 }
3897 side_two_choice = Choice::default();
3898 side_two_choice.switch_id = *switch_id;
3899 side_two_choice.category = MoveCategory::Switch;
3900 }
3901 MoveChoice::Move(move_index) => {
3902 side_two_choice = state.side_two.get_active().moves[move_index].choice.clone();
3903 side_two_choice.move_index = *move_index;
3904 }
3905 MoveChoice::MoveTera(move_index) => {
3906 side_two_choice = state.side_two.get_active().moves[move_index].choice.clone();
3907 side_two_choice.move_index = *move_index;
3908 s2_tera = true;
3909 }
3910 MoveChoice::MoveMega(move_index) => {
3911 side_two_choice = state.side_two.get_active().moves[move_index].choice.clone();
3912 side_two_choice.move_index = *move_index;
3913 s2_mega = true;
3914 }
3915 MoveChoice::None => {
3916 side_two_choice = Choice::default();
3917 }
3918 }
3919
3920 let mut state_instructions_vec: Vec<StateInstructions> = Vec::with_capacity(4);
3921 let mut incoming_instructions: StateInstructions = StateInstructions::default();
3922
3923 if s1_tera {
3928 state.side_one.get_active().terastallized = true;
3929 incoming_instructions
3930 .instruction_list
3931 .push(Instruction::ToggleTerastallized(
3932 ToggleTerastallizedInstruction {
3933 side_ref: SideReference::SideOne,
3934 },
3935 ));
3936 }
3937 if s2_tera {
3938 state.side_two.get_active().terastallized = true;
3939 incoming_instructions
3940 .instruction_list
3941 .push(Instruction::ToggleTerastallized(
3942 ToggleTerastallizedInstruction {
3943 side_ref: SideReference::SideTwo,
3944 },
3945 ));
3946 }
3947 if s1_mega {
3948 mega_evolve(state, SideReference::SideOne, &mut incoming_instructions);
3949 }
3950 if s2_mega {
3951 mega_evolve(state, SideReference::SideTwo, &mut incoming_instructions);
3952 }
3953
3954 modify_choice_priority(&state, &SideReference::SideOne, &mut side_one_choice);
3955 modify_choice_priority(&state, &SideReference::SideTwo, &mut side_two_choice);
3956
3957 state.reverse_instructions(&incoming_instructions.instruction_list);
3959
3960 match moves_first(
3961 &state,
3962 &side_one_choice,
3963 &side_two_choice,
3964 &mut incoming_instructions,
3965 ) {
3966 SideMovesFirst::SideOne => {
3967 handle_both_moves(
3968 state,
3969 &mut side_one_choice,
3970 &mut side_two_choice,
3971 SideReference::SideOne,
3972 incoming_instructions,
3973 &mut state_instructions_vec,
3974 branch_on_damage,
3975 );
3976
3977 for state_instruction in state_instructions_vec.iter_mut() {
3978 state.apply_instructions(&state_instruction.instruction_list);
3979 if !(s1_replacing_fainted_pkmn
3980 || s2_replacing_fainted_pkmn
3981 || state.side_one.force_switch
3982 || state.side_two.force_switch)
3983 {
3984 add_end_of_turn_instructions(state, state_instruction, &SideReference::SideOne);
3985 }
3986 state.reverse_instructions(&state_instruction.instruction_list);
3987 }
3988 }
3989 SideMovesFirst::SideTwo => {
3990 handle_both_moves(
3991 state,
3992 &mut side_two_choice,
3993 &mut side_one_choice,
3994 SideReference::SideTwo,
3995 incoming_instructions,
3996 &mut state_instructions_vec,
3997 branch_on_damage,
3998 );
3999 for state_instruction in state_instructions_vec.iter_mut() {
4000 state.apply_instructions(&state_instruction.instruction_list);
4001 if !(s1_replacing_fainted_pkmn
4002 || s2_replacing_fainted_pkmn
4003 || state.side_one.force_switch
4004 || state.side_two.force_switch)
4005 {
4006 add_end_of_turn_instructions(state, state_instruction, &SideReference::SideTwo);
4007 }
4008 state.reverse_instructions(&state_instruction.instruction_list);
4009 }
4010 }
4011 SideMovesFirst::SpeedTie => {
4012 let mut side_one_moves_first_instruction = incoming_instructions.clone();
4013 incoming_instructions.update_percentage(0.5);
4014 side_one_moves_first_instruction.update_percentage(0.5);
4015
4016 handle_both_moves(
4018 state,
4019 &mut side_one_choice,
4020 &mut side_two_choice,
4021 SideReference::SideOne,
4022 side_one_moves_first_instruction,
4023 &mut state_instructions_vec,
4024 branch_on_damage,
4025 );
4026 for state_instruction in state_instructions_vec.iter_mut() {
4027 state.apply_instructions(&state_instruction.instruction_list);
4028 if !(s1_replacing_fainted_pkmn
4029 || s2_replacing_fainted_pkmn
4030 || state.side_one.force_switch
4031 || state.side_two.force_switch)
4032 {
4033 add_end_of_turn_instructions(state, state_instruction, &SideReference::SideOne);
4034 }
4035 state.reverse_instructions(&state_instruction.instruction_list);
4036 }
4037
4038 let mut side_two_moves_first_si = Vec::with_capacity(4);
4040 handle_both_moves(
4041 state,
4042 &mut side_two_choice,
4043 &mut side_one_choice,
4044 SideReference::SideTwo,
4045 incoming_instructions,
4046 &mut side_two_moves_first_si,
4047 branch_on_damage,
4048 );
4049 for state_instruction in side_two_moves_first_si.iter_mut() {
4050 state.apply_instructions(&state_instruction.instruction_list);
4051 if !(s1_replacing_fainted_pkmn
4052 || s2_replacing_fainted_pkmn
4053 || state.side_one.force_switch
4054 || state.side_two.force_switch)
4055 {
4056 add_end_of_turn_instructions(state, state_instruction, &SideReference::SideTwo);
4057 }
4058 state.reverse_instructions(&state_instruction.instruction_list);
4059 }
4060
4061 state_instructions_vec.extend(side_two_moves_first_si);
4063 }
4064 }
4065 state_instructions_vec
4066}
4067
4068pub fn calculate_damage_rolls(
4069 mut state: State,
4070 attacking_side_ref: &SideReference,
4071 mut choice: Choice,
4072 mut defending_choice: &Choice,
4073) -> Option<Vec<i16>> {
4074 let mut incoming_instructions = StateInstructions::default();
4075
4076 if choice.flags.charge {
4077 choice.flags.charge = false;
4078 }
4079 if choice.move_id == Choices::FAKEOUT || choice.move_id == Choices::FIRSTIMPRESSION {
4080 state.get_side(attacking_side_ref).last_used_move = LastUsedMove::Switch(PokemonIndex::P0);
4081 }
4082
4083 let attacker_active = state
4084 .get_side_immutable(attacking_side_ref)
4085 .get_active_immutable();
4086 let defender_active = state
4087 .get_side_immutable(&attacking_side_ref.get_other_side())
4088 .get_active_immutable();
4089 match choice.move_id {
4090 Choices::SEISMICTOSS => {
4091 if type_effectiveness_modifier(&PokemonType::NORMAL, &defender_active) == 0.0 {
4092 return None;
4093 }
4094 return Some(vec![attacker_active.level as i16]);
4095 }
4096 Choices::NIGHTSHADE => {
4097 if type_effectiveness_modifier(&PokemonType::GHOST, &defender_active) == 0.0 {
4098 return None;
4099 }
4100 return Some(vec![attacker_active.level as i16]);
4101 }
4102 Choices::FINALGAMBIT => {
4103 if type_effectiveness_modifier(&PokemonType::GHOST, &defender_active) == 0.0 {
4104 return None;
4105 }
4106 return Some(vec![attacker_active.hp]);
4107 }
4108 Choices::ENDEAVOR => {
4109 if type_effectiveness_modifier(&PokemonType::GHOST, &defender_active) == 0.0
4110 || defender_active.hp <= attacker_active.hp
4111 {
4112 return None;
4113 }
4114 return Some(vec![defender_active.hp - attacker_active.hp]);
4115 }
4116 Choices::PAINSPLIT => {
4117 if type_effectiveness_modifier(&PokemonType::GHOST, &defender_active) == 0.0
4118 || defender_active.hp <= attacker_active.hp
4119 {
4120 return None;
4121 }
4122 return Some(vec![
4123 defender_active.hp - (attacker_active.hp + defender_active.hp) / 2,
4124 ]);
4125 }
4126 Choices::SUPERFANG
4127 if type_effectiveness_modifier(&PokemonType::NORMAL, &defender_active) == 0.0 =>
4128 {
4129 return None;
4130 }
4131 Choices::SUPERFANG | Choices::NATURESMADNESS | Choices::RUINATION => {
4132 return Some(vec![defender_active.hp / 2]);
4133 }
4134 Choices::SUCKERPUNCH | Choices::THUNDERCLAP => {
4135 defending_choice = MOVES.get(&Choices::TACKLE).unwrap();
4136 }
4137
4138 _ => {}
4139 }
4140
4141 before_move(
4142 &mut state,
4143 &mut choice,
4144 defending_choice,
4145 attacking_side_ref,
4146 &mut incoming_instructions,
4147 );
4148
4149 if choice.move_id == Choices::FUTURESIGHT {
4150 choice = MOVES.get(&Choices::FUTURESIGHT)?.clone();
4151 }
4152
4153 let mut return_vec = Vec::with_capacity(4);
4154 if let Some((damage, crit_damage)) =
4155 calculate_damage(&state, attacking_side_ref, &choice, DamageRolls::Max)
4156 {
4157 return_vec.push(damage);
4158 return_vec.push(crit_damage);
4159 Some(return_vec)
4160 } else {
4161 None
4162 }
4163}
4164
4165pub fn calculate_both_damage_rolls(
4166 state: &State,
4167 mut s1_choice: Choice,
4168 mut s2_choice: Choice,
4169 side_one_moves_first: bool,
4170) -> (Option<Vec<i16>>, Option<Vec<i16>>) {
4171 if side_one_moves_first {
4172 s1_choice.first_move = true;
4173 s2_choice.first_move = false;
4174 } else {
4175 s1_choice.first_move = false;
4176 s2_choice.first_move = true;
4177 }
4178
4179 let damages_dealt_s1 = calculate_damage_rolls(
4180 state.clone(),
4181 &SideReference::SideOne,
4182 s1_choice.clone(),
4183 &s2_choice,
4184 );
4185 let damages_dealt_s2 = calculate_damage_rolls(
4186 state.clone(),
4187 &SideReference::SideTwo,
4188 s2_choice,
4189 &s1_choice,
4190 );
4191
4192 (damages_dealt_s1, damages_dealt_s2)
4193}
4194
4195#[cfg(test)]
4196mod tests {
4197 use super::super::abilities::Abilities;
4198 use super::super::state::{PokemonVolatileStatus, Terrain, Weather};
4199 use super::*;
4200 use crate::choices::{Choices, MOVES};
4201 use crate::instruction::{
4202 ApplyVolatileStatusInstruction, BoostInstruction, ChangeItemInstruction,
4203 ChangeStatusInstruction, ChangeSubsituteHealthInstruction, ChangeTerrain,
4204 DamageInstruction, EnableMoveInstruction, SwitchInstruction,
4205 };
4206 use crate::state::{
4207 Move, PokemonBoostableStat, PokemonIndex, PokemonMoveIndex, PokemonSideCondition,
4208 PokemonStatus, SideReference, State,
4209 };
4210
4211 #[test]
4212 fn test_drag_move_as_second_move_exits_early_if_opponent_used_drag_move() {
4213 let mut state: State = State::default();
4214 let mut choice = MOVES.get(&Choices::DRAGONTAIL).unwrap().to_owned();
4215 choice.first_move = false;
4216
4217 let mut instructions = vec![];
4218 generate_instructions_from_move(
4219 &mut state,
4220 &mut choice,
4221 &MOVES.get(&Choices::DRAGONTAIL).unwrap(),
4222 SideReference::SideOne,
4223 StateInstructions::default(),
4224 &mut instructions,
4225 false,
4226 );
4227 assert_eq!(instructions, vec![StateInstructions::default()])
4228 }
4229
4230 #[test]
4231 fn test_electric_move_does_nothing_versus_ground_type() {
4232 let mut state: State = State::default();
4233 let mut choice = MOVES.get(&Choices::THUNDERBOLT).unwrap().to_owned();
4234 state.side_two.get_active().types = (PokemonType::GROUND, PokemonType::TYPELESS);
4235 choice.first_move = false;
4236
4237 let mut instructions = vec![];
4238 generate_instructions_from_move(
4239 &mut state,
4240 &mut choice,
4241 &MOVES.get(&Choices::TACKLE).unwrap(),
4242 SideReference::SideOne,
4243 StateInstructions::default(),
4244 &mut instructions,
4245 false,
4246 );
4247 assert_eq!(instructions, vec![StateInstructions::default()])
4248 }
4249
4250 #[test]
4251 fn test_grass_type_cannot_have_powder_move_used_against_it() {
4252 let mut state: State = State::default();
4253 let mut choice = MOVES.get(&Choices::SPORE).unwrap().to_owned(); state.side_two.get_active().types = (PokemonType::GRASS, PokemonType::TYPELESS);
4255 choice.first_move = false;
4256
4257 let mut instructions = vec![];
4258 generate_instructions_from_move(
4259 &mut state,
4260 &mut choice,
4261 &MOVES.get(&Choices::TACKLE).unwrap(),
4262 SideReference::SideOne,
4263 StateInstructions::default(),
4264 &mut instructions,
4265 false,
4266 );
4267
4268 #[cfg(any(feature = "gen6", feature = "gen7", feature = "gen8", feature = "gen9"))]
4269 let expected_instructions = vec![StateInstructions::default()];
4270
4271 #[cfg(any(feature = "gen3", feature = "gen4", feature = "gen5"))]
4272 let expected_instructions = vec![StateInstructions {
4273 percentage: 100.0,
4274 instruction_list: vec![Instruction::ChangeStatus(ChangeStatusInstruction {
4275 side_ref: SideReference::SideTwo,
4276 pokemon_index: PokemonIndex::P0,
4277 old_status: PokemonStatus::NONE,
4278 new_status: PokemonStatus::SLEEP,
4279 })],
4280 }];
4281
4282 assert_eq!(instructions, expected_instructions)
4283 }
4284
4285 #[test]
4286 fn test_spikes_sets_first_layer() {
4287 let mut state: State = State::default();
4288 let mut choice = MOVES.get(&Choices::SPIKES).unwrap().to_owned();
4289
4290 let mut instructions = vec![];
4291 generate_instructions_from_move(
4292 &mut state,
4293 &mut choice,
4294 &MOVES.get(&Choices::TACKLE).unwrap(),
4295 SideReference::SideOne,
4296 StateInstructions::default(),
4297 &mut instructions,
4298 false,
4299 );
4300
4301 let expected_instructions: StateInstructions = StateInstructions {
4302 percentage: 100.0,
4303 instruction_list: vec![Instruction::ChangeSideCondition(
4304 ChangeSideConditionInstruction {
4305 side_ref: SideReference::SideTwo,
4306 side_condition: PokemonSideCondition::Spikes,
4307 amount: 1,
4308 },
4309 )],
4310 };
4311
4312 assert_eq!(instructions, vec![expected_instructions])
4313 }
4314
4315 #[test]
4316 fn test_spikes_layers_cannot_exceed_3() {
4317 let mut state: State = State::default();
4318 state.side_two.side_conditions.spikes = 3;
4319 let mut choice = MOVES.get(&Choices::SPIKES).unwrap().to_owned();
4320
4321 let mut instructions = vec![];
4322 generate_instructions_from_move(
4323 &mut state,
4324 &mut choice,
4325 &MOVES.get(&Choices::TACKLE).unwrap(),
4326 SideReference::SideOne,
4327 StateInstructions::default(),
4328 &mut instructions,
4329 false,
4330 );
4331
4332 let expected_instructions: StateInstructions = StateInstructions {
4333 percentage: 100.0,
4334 instruction_list: vec![],
4335 };
4336
4337 assert_eq!(instructions, vec![expected_instructions])
4338 }
4339
4340 #[test]
4341 fn test_aurora_veil_works_in_hail() {
4342 let mut state: State = State::default();
4343 state.weather.weather_type = Weather::HAIL;
4344 let mut choice = MOVES.get(&Choices::AURORAVEIL).unwrap().to_owned();
4345
4346 let mut instructions = vec![];
4347 generate_instructions_from_move(
4348 &mut state,
4349 &mut choice,
4350 &MOVES.get(&Choices::TACKLE).unwrap(),
4351 SideReference::SideOne,
4352 StateInstructions::default(),
4353 &mut instructions,
4354 false,
4355 );
4356
4357 let expected_instructions: StateInstructions = StateInstructions {
4358 percentage: 100.0,
4359 instruction_list: vec![Instruction::ChangeSideCondition(
4360 ChangeSideConditionInstruction {
4361 side_ref: SideReference::SideOne,
4362 side_condition: PokemonSideCondition::AuroraVeil,
4363 amount: 5,
4364 },
4365 )],
4366 };
4367
4368 assert_eq!(instructions, vec![expected_instructions])
4369 }
4370
4371 #[test]
4372 fn test_auroa_veil_fails_outside_hail() {
4373 let mut state: State = State::default();
4374 let mut choice = MOVES.get(&Choices::AURORAVEIL).unwrap().to_owned();
4375
4376 let mut instructions = vec![];
4377 generate_instructions_from_move(
4378 &mut state,
4379 &mut choice,
4380 &MOVES.get(&Choices::TACKLE).unwrap(),
4381 SideReference::SideOne,
4382 StateInstructions::default(),
4383 &mut instructions,
4384 false,
4385 );
4386
4387 let expected_instructions: StateInstructions = StateInstructions {
4388 percentage: 100.0,
4389 instruction_list: vec![],
4390 };
4391
4392 assert_eq!(instructions, vec![expected_instructions])
4393 }
4394
4395 #[test]
4396 fn test_auroa_veil_fails_outside_of_hail() {
4397 let mut state: State = State::default();
4398 state.weather.weather_type = Weather::NONE;
4399 let mut choice = MOVES.get(&Choices::AURORAVEIL).unwrap().to_owned();
4400
4401 let mut instructions = vec![];
4402 generate_instructions_from_move(
4403 &mut state,
4404 &mut choice,
4405 &MOVES.get(&Choices::TACKLE).unwrap(),
4406 SideReference::SideOne,
4407 StateInstructions::default(),
4408 &mut instructions,
4409 false,
4410 );
4411
4412 let expected_instructions: StateInstructions = StateInstructions {
4413 percentage: 100.0,
4414 instruction_list: vec![],
4415 };
4416
4417 assert_eq!(instructions, vec![expected_instructions])
4418 }
4419
4420 #[test]
4421 fn test_stealthrock_cannot_exceed_1_layer() {
4422 let mut state: State = State::default();
4423 state.side_two.side_conditions.stealth_rock = 1;
4424 let mut choice = MOVES.get(&Choices::STEALTHROCK).unwrap().to_owned();
4425
4426 let mut instructions = vec![];
4427 generate_instructions_from_move(
4428 &mut state,
4429 &mut choice,
4430 &MOVES.get(&Choices::TACKLE).unwrap(),
4431 SideReference::SideOne,
4432 StateInstructions::default(),
4433 &mut instructions,
4434 false,
4435 );
4436
4437 let expected_instructions: StateInstructions = StateInstructions {
4438 percentage: 100.0,
4439 instruction_list: vec![],
4440 };
4441
4442 assert_eq!(instructions, vec![expected_instructions])
4443 }
4444
4445 #[test]
4446 fn test_stoneaxe_damage_and_stealthrock_setting() {
4447 let mut state: State = State::default();
4448 let mut choice = MOVES.get(&Choices::STONEAXE).unwrap().to_owned();
4449
4450 let mut instructions = vec![];
4451 generate_instructions_from_move(
4452 &mut state,
4453 &mut choice,
4454 &MOVES.get(&Choices::TACKLE).unwrap(),
4455 SideReference::SideOne,
4456 StateInstructions::default(),
4457 &mut instructions,
4458 false,
4459 );
4460
4461 let expected_instructions = vec![
4462 StateInstructions {
4463 percentage: 10.000002,
4464 instruction_list: vec![],
4465 },
4466 StateInstructions {
4467 percentage: 90.0,
4468 instruction_list: vec![
4469 Instruction::Damage(DamageInstruction {
4470 side_ref: SideReference::SideTwo,
4471 damage_amount: 51,
4472 }),
4473 Instruction::ChangeSideCondition(ChangeSideConditionInstruction {
4474 side_ref: SideReference::SideTwo,
4475 side_condition: PokemonSideCondition::Stealthrock,
4476 amount: 1,
4477 }),
4478 ],
4479 },
4480 ];
4481
4482 assert_eq!(instructions, expected_instructions)
4483 }
4484
4485 #[test]
4486 fn test_ceaselessedge_damage_and_stealthrock_setting() {
4487 let mut state: State = State::default();
4488 let mut choice = MOVES.get(&Choices::CEASELESSEDGE).unwrap().to_owned();
4489
4490 let mut instructions = vec![];
4491 generate_instructions_from_move(
4492 &mut state,
4493 &mut choice,
4494 &MOVES.get(&Choices::TACKLE).unwrap(),
4495 SideReference::SideOne,
4496 StateInstructions::default(),
4497 &mut instructions,
4498 false,
4499 );
4500
4501 let expected_instructions = vec![
4502 StateInstructions {
4503 percentage: 10.000002,
4504 instruction_list: vec![],
4505 },
4506 StateInstructions {
4507 percentage: 90.0,
4508 instruction_list: vec![
4509 Instruction::Damage(DamageInstruction {
4510 side_ref: SideReference::SideTwo,
4511 damage_amount: 51,
4512 }),
4513 Instruction::ChangeSideCondition(ChangeSideConditionInstruction {
4514 side_ref: SideReference::SideTwo,
4515 side_condition: PokemonSideCondition::Spikes,
4516 amount: 1,
4517 }),
4518 ],
4519 },
4520 ];
4521
4522 assert_eq!(instructions, expected_instructions)
4523 }
4524
4525 #[test]
4526 fn test_100_percent_secondary_volatilestatus() {
4527 let mut state: State = State::default();
4528 let mut choice = MOVES.get(&Choices::CHATTER).unwrap().to_owned();
4529
4530 let mut instructions = vec![];
4531 generate_instructions_from_move(
4532 &mut state,
4533 &mut choice,
4534 &MOVES.get(&Choices::TACKLE).unwrap(),
4535 SideReference::SideOne,
4536 StateInstructions::default(),
4537 &mut instructions,
4538 false,
4539 );
4540
4541 let expected_instructions = vec![StateInstructions {
4542 percentage: 100.0,
4543 instruction_list: vec![
4544 Instruction::Damage(DamageInstruction {
4545 side_ref: SideReference::SideTwo,
4546 damage_amount: 51,
4547 }),
4548 Instruction::ApplyVolatileStatus(ApplyVolatileStatusInstruction {
4549 side_ref: SideReference::SideTwo,
4550 volatile_status: PokemonVolatileStatus::CONFUSION,
4551 }),
4552 ],
4553 }];
4554
4555 assert_eq!(instructions, expected_instructions)
4556 }
4557
4558 #[test]
4559 fn test_possible_secondary_volatilestatus() {
4560 let mut state: State = State::default();
4561 let mut choice = MOVES.get(&Choices::CONFUSION).unwrap().to_owned();
4562
4563 let mut instructions = vec![];
4564 generate_instructions_from_move(
4565 &mut state,
4566 &mut choice,
4567 &MOVES.get(&Choices::TACKLE).unwrap(),
4568 SideReference::SideOne,
4569 StateInstructions::default(),
4570 &mut instructions,
4571 false,
4572 );
4573
4574 let expected_instructions = vec![
4575 StateInstructions {
4576 percentage: 90.0,
4577 instruction_list: vec![Instruction::Damage(DamageInstruction {
4578 side_ref: SideReference::SideTwo,
4579 damage_amount: 40,
4580 })],
4581 },
4582 StateInstructions {
4583 percentage: 10.0,
4584 instruction_list: vec![
4585 Instruction::Damage(DamageInstruction {
4586 side_ref: SideReference::SideTwo,
4587 damage_amount: 40,
4588 }),
4589 Instruction::ApplyVolatileStatus(ApplyVolatileStatusInstruction {
4590 side_ref: SideReference::SideTwo,
4591 volatile_status: PokemonVolatileStatus::CONFUSION,
4592 }),
4593 ],
4594 },
4595 ];
4596
4597 assert_eq!(instructions, expected_instructions)
4598 }
4599
4600 #[test]
4601 fn test_possible_secondary_volatilestatus_with_possible_accuracy() {
4602 let mut state: State = State::default();
4603 state.side_two.get_active().hp = 400;
4604 state.side_two.get_active().maxhp = 400;
4605 let mut choice = MOVES.get(&Choices::AXEKICK).unwrap().to_owned();
4606
4607 let mut instructions = vec![];
4608 generate_instructions_from_move(
4609 &mut state,
4610 &mut choice,
4611 &MOVES.get(&Choices::TACKLE).unwrap(),
4612 SideReference::SideOne,
4613 StateInstructions::default(),
4614 &mut instructions,
4615 false,
4616 );
4617
4618 let expected_instructions = vec![
4619 StateInstructions {
4620 percentage: 10.000002,
4621 instruction_list: vec![Instruction::Damage(DamageInstruction {
4622 side_ref: SideReference::SideOne,
4623 damage_amount: 50, })],
4625 },
4626 StateInstructions {
4627 percentage: 63.0,
4628 instruction_list: vec![Instruction::Damage(DamageInstruction {
4629 side_ref: SideReference::SideTwo,
4630 damage_amount: 188,
4631 })],
4632 },
4633 StateInstructions {
4634 percentage: 27.0000019,
4635 instruction_list: vec![
4636 Instruction::Damage(DamageInstruction {
4637 side_ref: SideReference::SideTwo,
4638 damage_amount: 188,
4639 }),
4640 Instruction::ApplyVolatileStatus(ApplyVolatileStatusInstruction {
4641 side_ref: SideReference::SideTwo,
4642 volatile_status: PokemonVolatileStatus::CONFUSION,
4643 }),
4644 ],
4645 },
4646 ];
4647
4648 assert_eq!(instructions, expected_instructions)
4649 }
4650
4651 #[test]
4652 fn test_basic_volatile_status_applied_to_self() {
4653 let mut state: State = State::default();
4654 let mut choice = MOVES.get(&Choices::AQUARING).unwrap().to_owned();
4655
4656 let mut instructions = vec![];
4657 generate_instructions_from_move(
4658 &mut state,
4659 &mut choice,
4660 &MOVES.get(&Choices::TACKLE).unwrap(),
4661 SideReference::SideOne,
4662 StateInstructions::default(),
4663 &mut instructions,
4664 false,
4665 );
4666
4667 let expected_instructions = vec![StateInstructions {
4668 percentage: 100.0,
4669 instruction_list: vec![Instruction::ApplyVolatileStatus(
4670 ApplyVolatileStatusInstruction {
4671 side_ref: SideReference::SideOne,
4672 volatile_status: PokemonVolatileStatus::AQUARING,
4673 },
4674 )],
4675 }];
4676
4677 assert_eq!(instructions, expected_instructions)
4678 }
4679
4680 #[test]
4681 fn test_basic_volatile_status_applied_to_opponent() {
4682 let mut state: State = State::default();
4683 let mut choice = MOVES.get(&Choices::ATTRACT).unwrap().to_owned();
4684
4685 let mut instructions = vec![];
4686 generate_instructions_from_move(
4687 &mut state,
4688 &mut choice,
4689 &MOVES.get(&Choices::TACKLE).unwrap(),
4690 SideReference::SideOne,
4691 StateInstructions::default(),
4692 &mut instructions,
4693 false,
4694 );
4695
4696 let expected_instructions = vec![StateInstructions {
4697 percentage: 100.0,
4698 instruction_list: vec![Instruction::ApplyVolatileStatus(
4699 ApplyVolatileStatusInstruction {
4700 side_ref: SideReference::SideTwo,
4701 volatile_status: PokemonVolatileStatus::ATTRACT,
4702 },
4703 )],
4704 }];
4705
4706 assert_eq!(instructions, expected_instructions)
4707 }
4708
4709 #[test]
4710 fn test_cannot_apply_volatile_status_twice() {
4711 let mut state: State = State::default();
4712 state
4713 .side_two
4714 .volatile_statuses
4715 .insert(PokemonVolatileStatus::ATTRACT);
4716 let mut choice = MOVES.get(&Choices::ATTRACT).unwrap().to_owned();
4717
4718 let mut instructions = vec![];
4719 generate_instructions_from_move(
4720 &mut state,
4721 &mut choice,
4722 &MOVES.get(&Choices::TACKLE).unwrap(),
4723 SideReference::SideOne,
4724 StateInstructions::default(),
4725 &mut instructions,
4726 false,
4727 );
4728
4729 let expected_instructions = vec![StateInstructions {
4730 percentage: 100.0,
4731 instruction_list: vec![],
4732 }];
4733
4734 assert_eq!(instructions, expected_instructions)
4735 }
4736
4737 #[test]
4738 fn test_substitute_failing_if_user_has_less_than_25_percent_hp() {
4739 let mut state: State = State::default();
4740 state.side_one.get_active().hp = 25;
4741 let mut choice = MOVES.get(&Choices::SUBSTITUTE).unwrap().to_owned();
4742
4743 let mut instructions = vec![];
4744 generate_instructions_from_move(
4745 &mut state,
4746 &mut choice,
4747 &MOVES.get(&Choices::TACKLE).unwrap(),
4748 SideReference::SideOne,
4749 StateInstructions::default(),
4750 &mut instructions,
4751 false,
4752 );
4753
4754 let expected_instructions = vec![StateInstructions {
4755 percentage: 100.0,
4756 instruction_list: vec![],
4757 }];
4758
4759 assert_eq!(instructions, expected_instructions)
4760 }
4761
4762 #[test]
4763 fn test_shedtail_failing_if_user_has_less_than_50_percent_hp() {
4764 let mut state: State = State::default();
4765 state.side_one.get_active().hp = 50;
4766 let mut choice = MOVES.get(&Choices::SHEDTAIL).unwrap().to_owned();
4767
4768 let mut instructions = vec![];
4769 generate_instructions_from_move(
4770 &mut state,
4771 &mut choice,
4772 &MOVES.get(&Choices::TACKLE).unwrap(),
4773 SideReference::SideOne,
4774 StateInstructions::default(),
4775 &mut instructions,
4776 false,
4777 );
4778
4779 let expected_instructions = vec![StateInstructions {
4780 percentage: 100.0,
4781 instruction_list: vec![],
4782 }];
4783
4784 assert_eq!(instructions, expected_instructions)
4785 }
4786
4787 #[test]
4788 fn test_basic_drag_move() {
4789 let mut state: State = State::default();
4790 let mut choice = MOVES.get(&Choices::WHIRLWIND).unwrap().to_owned();
4791
4792 let mut instructions = vec![];
4793 generate_instructions_from_move(
4794 &mut state,
4795 &mut choice,
4796 &MOVES.get(&Choices::TACKLE).unwrap(),
4797 SideReference::SideOne,
4798 StateInstructions::default(),
4799 &mut instructions,
4800 false,
4801 );
4802
4803 let expected_instructions = vec![
4804 StateInstructions {
4805 percentage: 20.0,
4806 instruction_list: vec![Instruction::Switch(SwitchInstruction {
4807 side_ref: SideReference::SideTwo,
4808 previous_index: PokemonIndex::P0,
4809 next_index: PokemonIndex::P1,
4810 })],
4811 },
4812 StateInstructions {
4813 percentage: 20.0,
4814 instruction_list: vec![Instruction::Switch(SwitchInstruction {
4815 side_ref: SideReference::SideTwo,
4816 previous_index: PokemonIndex::P0,
4817 next_index: PokemonIndex::P2,
4818 })],
4819 },
4820 StateInstructions {
4821 percentage: 20.0,
4822 instruction_list: vec![Instruction::Switch(SwitchInstruction {
4823 side_ref: SideReference::SideTwo,
4824 previous_index: PokemonIndex::P0,
4825 next_index: PokemonIndex::P3,
4826 })],
4827 },
4828 StateInstructions {
4829 percentage: 20.0,
4830 instruction_list: vec![Instruction::Switch(SwitchInstruction {
4831 side_ref: SideReference::SideTwo,
4832 previous_index: PokemonIndex::P0,
4833 next_index: PokemonIndex::P4,
4834 })],
4835 },
4836 StateInstructions {
4837 percentage: 20.0,
4838 instruction_list: vec![Instruction::Switch(SwitchInstruction {
4839 side_ref: SideReference::SideTwo,
4840 previous_index: PokemonIndex::P0,
4841 next_index: PokemonIndex::P5,
4842 })],
4843 },
4844 ];
4845
4846 assert_eq!(instructions, expected_instructions)
4847 }
4848
4849 #[test]
4850 fn test_basic_drag_move_with_fainted_reserve() {
4851 let mut state: State = State::default();
4852 state.side_two.pokemon[PokemonIndex::P1].hp = 0;
4853 state.side_two.pokemon[PokemonIndex::P3].hp = 0;
4854 let mut choice = MOVES.get(&Choices::WHIRLWIND).unwrap().to_owned();
4855
4856 let mut instructions = vec![];
4857 generate_instructions_from_move(
4858 &mut state,
4859 &mut choice,
4860 &MOVES.get(&Choices::TACKLE).unwrap(),
4861 SideReference::SideOne,
4862 StateInstructions::default(),
4863 &mut instructions,
4864 false,
4865 );
4866
4867 let expected_instructions = vec![
4868 StateInstructions {
4869 percentage: 33.333336,
4870 instruction_list: vec![Instruction::Switch(SwitchInstruction {
4871 side_ref: SideReference::SideTwo,
4872 previous_index: PokemonIndex::P0,
4873 next_index: PokemonIndex::P2,
4874 })],
4875 },
4876 StateInstructions {
4877 percentage: 33.333336,
4878 instruction_list: vec![Instruction::Switch(SwitchInstruction {
4879 side_ref: SideReference::SideTwo,
4880 previous_index: PokemonIndex::P0,
4881 next_index: PokemonIndex::P4,
4882 })],
4883 },
4884 StateInstructions {
4885 percentage: 33.333336,
4886 instruction_list: vec![Instruction::Switch(SwitchInstruction {
4887 side_ref: SideReference::SideTwo,
4888 previous_index: PokemonIndex::P0,
4889 next_index: PokemonIndex::P5,
4890 })],
4891 },
4892 ];
4893
4894 assert_eq!(instructions, expected_instructions)
4895 }
4896
4897 #[test]
4898 fn test_basic_damaging_drag_move_with_fainted_reserve() {
4899 let mut state: State = State::default();
4900 state.side_two.pokemon[PokemonIndex::P1].hp = 0;
4901 state.side_two.pokemon[PokemonIndex::P3].hp = 0;
4902 let mut choice = MOVES.get(&Choices::DRAGONTAIL).unwrap().to_owned();
4903
4904 let mut instructions = vec![];
4905 generate_instructions_from_move(
4906 &mut state,
4907 &mut choice,
4908 &MOVES.get(&Choices::TACKLE).unwrap(),
4909 SideReference::SideOne,
4910 StateInstructions::default(),
4911 &mut instructions,
4912 false,
4913 );
4914
4915 let expected_instructions = vec![
4916 StateInstructions {
4917 percentage: 10.0000019,
4918 instruction_list: vec![], },
4920 StateInstructions {
4921 percentage: 30.0,
4922 instruction_list: vec![
4923 Instruction::Damage(DamageInstruction {
4924 side_ref: SideReference::SideTwo,
4925 damage_amount: 48,
4926 }),
4927 Instruction::Switch(SwitchInstruction {
4928 side_ref: SideReference::SideTwo,
4929 previous_index: PokemonIndex::P0,
4930 next_index: PokemonIndex::P2,
4931 }),
4932 ],
4933 },
4934 StateInstructions {
4935 percentage: 30.0,
4936 instruction_list: vec![
4937 Instruction::Damage(DamageInstruction {
4938 side_ref: SideReference::SideTwo,
4939 damage_amount: 48,
4940 }),
4941 Instruction::Switch(SwitchInstruction {
4942 side_ref: SideReference::SideTwo,
4943 previous_index: PokemonIndex::P0,
4944 next_index: PokemonIndex::P4,
4945 }),
4946 ],
4947 },
4948 StateInstructions {
4949 percentage: 30.0,
4950 instruction_list: vec![
4951 Instruction::Damage(DamageInstruction {
4952 side_ref: SideReference::SideTwo,
4953 damage_amount: 48,
4954 }),
4955 Instruction::Switch(SwitchInstruction {
4956 side_ref: SideReference::SideTwo,
4957 previous_index: PokemonIndex::P0,
4958 next_index: PokemonIndex::P5,
4959 }),
4960 ],
4961 },
4962 ];
4963
4964 assert_eq!(instructions, expected_instructions)
4965 }
4966
4967 #[test]
4968 fn test_basic_damaging_drag_that_knocks_out_defender() {
4969 let mut state: State = State::default();
4970 state.side_two.pokemon[PokemonIndex::P1].hp = 0;
4971 state.side_two.pokemon[PokemonIndex::P3].hp = 0;
4972 state.side_two.get_active().hp = 5;
4973 let mut choice = MOVES.get(&Choices::DRAGONTAIL).unwrap().to_owned();
4974
4975 let mut instructions = vec![];
4976 generate_instructions_from_move(
4977 &mut state,
4978 &mut choice,
4979 &MOVES.get(&Choices::TACKLE).unwrap(),
4980 SideReference::SideOne,
4981 StateInstructions::default(),
4982 &mut instructions,
4983 false,
4984 );
4985
4986 let expected_instructions = vec![
4987 StateInstructions {
4988 percentage: 10.0000019,
4989 instruction_list: vec![], },
4991 StateInstructions {
4992 percentage: 90.0,
4993 instruction_list: vec![Instruction::Damage(DamageInstruction {
4994 side_ref: SideReference::SideTwo,
4995 damage_amount: 5,
4996 })],
4997 },
4998 ];
4999
5000 assert_eq!(instructions, expected_instructions)
5001 }
5002
5003 #[test]
5004 fn test_drag_versus_no_alive_reserved() {
5005 let mut state: State = State::default();
5006 state.side_two.pokemon[PokemonIndex::P1].hp = 0;
5007 state.side_two.pokemon[PokemonIndex::P2].hp = 0;
5008 state.side_two.pokemon[PokemonIndex::P3].hp = 0;
5009 state.side_two.pokemon[PokemonIndex::P4].hp = 0;
5010 state.side_two.pokemon[PokemonIndex::P5].hp = 0;
5011 let mut choice = MOVES.get(&Choices::WHIRLWIND).unwrap().to_owned();
5012
5013 let mut instructions = vec![];
5014 generate_instructions_from_move(
5015 &mut state,
5016 &mut choice,
5017 &MOVES.get(&Choices::TACKLE).unwrap(),
5018 SideReference::SideOne,
5019 StateInstructions::default(),
5020 &mut instructions,
5021 false,
5022 );
5023
5024 let expected_instructions = vec![StateInstructions {
5025 percentage: 100.0,
5026 instruction_list: vec![],
5027 }];
5028
5029 assert_eq!(instructions, expected_instructions)
5030 }
5031
5032 #[test]
5033 fn test_basic_drag_move_with_fainted_reserve_and_prior_instruction() {
5034 let mut state: State = State::default();
5035 state.side_two.pokemon[PokemonIndex::P1].hp = 0;
5036 state.side_two.pokemon[PokemonIndex::P3].hp = 0;
5037 let mut choice = MOVES.get(&Choices::WHIRLWIND).unwrap().to_owned();
5038
5039 let previous_instruction = StateInstructions {
5040 percentage: 50.0,
5041 instruction_list: vec![Instruction::Damage(DamageInstruction {
5042 side_ref: SideReference::SideOne,
5043 damage_amount: 5,
5044 })],
5045 };
5046
5047 let mut instructions = vec![];
5048 generate_instructions_from_move(
5049 &mut state,
5050 &mut choice,
5051 &MOVES.get(&Choices::TACKLE).unwrap(),
5052 SideReference::SideOne,
5053 previous_instruction,
5054 &mut instructions,
5055 false,
5056 );
5057
5058 let expected_instructions = vec![
5059 StateInstructions {
5060 percentage: 16.666668,
5061 instruction_list: vec![
5062 Instruction::Damage(DamageInstruction {
5063 side_ref: SideReference::SideOne,
5064 damage_amount: 5,
5065 }),
5066 Instruction::Switch(SwitchInstruction {
5067 side_ref: SideReference::SideTwo,
5068 previous_index: PokemonIndex::P0,
5069 next_index: PokemonIndex::P2,
5070 }),
5071 ],
5072 },
5073 StateInstructions {
5074 percentage: 16.666668,
5075 instruction_list: vec![
5076 Instruction::Damage(DamageInstruction {
5077 side_ref: SideReference::SideOne,
5078 damage_amount: 5,
5079 }),
5080 Instruction::Switch(SwitchInstruction {
5081 side_ref: SideReference::SideTwo,
5082 previous_index: PokemonIndex::P0,
5083 next_index: PokemonIndex::P4,
5084 }),
5085 ],
5086 },
5087 StateInstructions {
5088 percentage: 16.666668,
5089 instruction_list: vec![
5090 Instruction::Damage(DamageInstruction {
5091 side_ref: SideReference::SideOne,
5092 damage_amount: 5,
5093 }),
5094 Instruction::Switch(SwitchInstruction {
5095 side_ref: SideReference::SideTwo,
5096 previous_index: PokemonIndex::P0,
5097 next_index: PokemonIndex::P5,
5098 }),
5099 ],
5100 },
5101 ];
5102
5103 assert_eq!(instructions, expected_instructions)
5104 }
5105
5106 #[test]
5107 #[cfg(feature = "gen9")]
5108 fn test_basic_status_move() {
5109 let mut state: State = State::default();
5110 let mut choice = MOVES.get(&Choices::GLARE).unwrap().to_owned();
5111
5112 let mut instructions = vec![];
5113 generate_instructions_from_move(
5114 &mut state,
5115 &mut choice,
5116 &MOVES.get(&Choices::TACKLE).unwrap(),
5117 SideReference::SideOne,
5118 StateInstructions::default(),
5119 &mut instructions,
5120 false,
5121 );
5122
5123 let expected_instructions = vec![StateInstructions {
5124 percentage: 100.0,
5125 instruction_list: vec![Instruction::ChangeStatus(ChangeStatusInstruction {
5126 side_ref: SideReference::SideTwo,
5127 pokemon_index: PokemonIndex::P0,
5128 old_status: PokemonStatus::NONE,
5129 new_status: PokemonStatus::PARALYZE,
5130 })],
5131 }];
5132
5133 assert_eq!(instructions, expected_instructions)
5134 }
5135
5136 #[test]
5137 #[cfg(feature = "gen9")]
5138 fn test_status_move_that_can_miss() {
5139 let mut state: State = State::default();
5140 let mut choice = MOVES.get(&Choices::THUNDERWAVE).unwrap().to_owned();
5141
5142 let mut instructions = vec![];
5143 generate_instructions_from_move(
5144 &mut state,
5145 &mut choice,
5146 &MOVES.get(&Choices::TACKLE).unwrap(),
5147 SideReference::SideOne,
5148 StateInstructions::default(),
5149 &mut instructions,
5150 false,
5151 );
5152
5153 let expected_instructions = vec![
5154 StateInstructions {
5155 percentage: 10.000002,
5156 instruction_list: vec![],
5157 },
5158 StateInstructions {
5159 percentage: 90.0,
5160 instruction_list: vec![Instruction::ChangeStatus(ChangeStatusInstruction {
5161 side_ref: SideReference::SideTwo,
5162 pokemon_index: PokemonIndex::P0,
5163 old_status: PokemonStatus::NONE,
5164 new_status: PokemonStatus::PARALYZE,
5165 })],
5166 },
5167 ];
5168
5169 assert_eq!(instructions, expected_instructions)
5170 }
5171
5172 #[test]
5173 fn test_status_move_that_can_miss_but_is_blocked_by_ability() {
5174 let mut state: State = State::default();
5175 state.side_two.get_active().ability = Abilities::LIMBER;
5176 let mut choice = MOVES.get(&Choices::THUNDERWAVE).unwrap().to_owned();
5177
5178 let mut instructions = vec![];
5179 generate_instructions_from_move(
5180 &mut state,
5181 &mut choice,
5182 &MOVES.get(&Choices::TACKLE).unwrap(),
5183 SideReference::SideOne,
5184 StateInstructions::default(),
5185 &mut instructions,
5186 false,
5187 );
5188
5189 let expected_instructions = vec![StateInstructions {
5190 percentage: 100.0,
5191 instruction_list: vec![],
5192 }];
5193
5194 assert_eq!(instructions, expected_instructions)
5195 }
5196
5197 #[test]
5198 fn test_flamebody_conditional_burn_on_contact() {
5199 let mut state: State = State::default();
5200 state.side_two.get_active().ability = Abilities::FLAMEBODY;
5201 let mut choice = MOVES.get(&Choices::TACKLE).unwrap().to_owned();
5202
5203 let mut instructions = vec![];
5204 generate_instructions_from_move(
5205 &mut state,
5206 &mut choice,
5207 &MOVES.get(&Choices::TACKLE).unwrap(),
5208 SideReference::SideOne,
5209 StateInstructions::default(),
5210 &mut instructions,
5211 false,
5212 );
5213
5214 let expected_instructions = vec![
5215 StateInstructions {
5216 percentage: 70.0,
5217 instruction_list: vec![Instruction::Damage(DamageInstruction {
5218 side_ref: SideReference::SideTwo,
5219 damage_amount: 48,
5220 })],
5221 },
5222 StateInstructions {
5223 percentage: 30.0000019,
5224 instruction_list: vec![
5225 Instruction::Damage(DamageInstruction {
5226 side_ref: SideReference::SideTwo,
5227 damage_amount: 48,
5228 }),
5229 Instruction::ChangeStatus(ChangeStatusInstruction {
5230 side_ref: SideReference::SideOne,
5231 pokemon_index: PokemonIndex::P0,
5232 old_status: PokemonStatus::NONE,
5233 new_status: PokemonStatus::BURN,
5234 }),
5235 ],
5236 },
5237 ];
5238
5239 assert_eq!(instructions, expected_instructions)
5240 }
5241
5242 #[test]
5243 fn test_protectivepads_stops_flamebody() {
5244 let mut state: State = State::default();
5245 state.side_two.get_active().ability = Abilities::FLAMEBODY;
5246 state.side_one.get_active().item = Items::PROTECTIVEPADS;
5247 let mut choice = MOVES.get(&Choices::TACKLE).unwrap().to_owned();
5248
5249 let mut instructions = vec![];
5250 generate_instructions_from_move(
5251 &mut state,
5252 &mut choice,
5253 &MOVES.get(&Choices::TACKLE).unwrap(),
5254 SideReference::SideOne,
5255 StateInstructions::default(),
5256 &mut instructions,
5257 false,
5258 );
5259
5260 let expected_instructions = vec![StateInstructions {
5261 percentage: 100.0,
5262 instruction_list: vec![Instruction::Damage(DamageInstruction {
5263 side_ref: SideReference::SideTwo,
5264 damage_amount: 48,
5265 })],
5266 }];
5267
5268 assert_eq!(instructions, expected_instructions)
5269 }
5270
5271 #[test]
5272 fn test_flamebody_versus_noncontact_move() {
5273 let mut state: State = State::default();
5274 state.side_two.get_active().ability = Abilities::FLAMEBODY;
5275 let mut choice = MOVES.get(&Choices::WATERGUN).unwrap().to_owned();
5276
5277 let mut instructions = vec![];
5278 generate_instructions_from_move(
5279 &mut state,
5280 &mut choice,
5281 &MOVES.get(&Choices::TACKLE).unwrap(),
5282 SideReference::SideOne,
5283 StateInstructions::default(),
5284 &mut instructions,
5285 false,
5286 );
5287
5288 let expected_instructions = vec![StateInstructions {
5289 percentage: 100.0,
5290 instruction_list: vec![Instruction::Damage(DamageInstruction {
5291 side_ref: SideReference::SideTwo,
5292 damage_amount: 32,
5293 })],
5294 }];
5295
5296 assert_eq!(instructions, expected_instructions)
5297 }
5298
5299 #[test]
5300 fn test_flamebody_versus_fire_type() {
5301 let mut state: State = State::default();
5302 state.side_one.get_active().types.0 = PokemonType::FIRE;
5303 state.side_two.get_active().ability = Abilities::FLAMEBODY;
5304 let mut choice = MOVES.get(&Choices::WATERGUN).unwrap().to_owned();
5305
5306 let mut instructions = vec![];
5307 generate_instructions_from_move(
5308 &mut state,
5309 &mut choice,
5310 &MOVES.get(&Choices::TACKLE).unwrap(),
5311 SideReference::SideOne,
5312 StateInstructions::default(),
5313 &mut instructions,
5314 false,
5315 );
5316
5317 let expected_instructions = vec![StateInstructions {
5318 percentage: 100.0,
5319 instruction_list: vec![Instruction::Damage(DamageInstruction {
5320 side_ref: SideReference::SideTwo,
5321 damage_amount: 32,
5322 })],
5323 }];
5324
5325 assert_eq!(instructions, expected_instructions)
5326 }
5327
5328 #[test]
5329 fn test_move_with_multiple_secondaries() {
5330 let mut state: State = State::default();
5331 let mut choice = MOVES.get(&Choices::FIREFANG).unwrap().to_owned();
5332
5333 let mut instructions = vec![];
5334 generate_instructions_from_move(
5335 &mut state,
5336 &mut choice,
5337 &MOVES.get(&Choices::TACKLE).unwrap(),
5338 SideReference::SideOne,
5339 StateInstructions::default(),
5340 &mut instructions,
5341 false,
5342 );
5343
5344 let expected_instructions = vec![
5345 StateInstructions {
5346 percentage: 5.00000095,
5347 instruction_list: vec![],
5348 },
5349 StateInstructions {
5350 percentage: 76.9499969,
5351 instruction_list: vec![Instruction::Damage(DamageInstruction {
5352 side_ref: SideReference::SideTwo,
5353 damage_amount: 51,
5354 })],
5355 },
5356 StateInstructions {
5357 percentage: 8.55000019,
5358 instruction_list: vec![
5359 Instruction::Damage(DamageInstruction {
5360 side_ref: SideReference::SideTwo,
5361 damage_amount: 51,
5362 }),
5363 Instruction::ApplyVolatileStatus(ApplyVolatileStatusInstruction {
5364 side_ref: SideReference::SideTwo,
5365 volatile_status: PokemonVolatileStatus::FLINCH,
5366 }),
5367 ],
5368 },
5369 StateInstructions {
5370 percentage: 8.55000019,
5371 instruction_list: vec![
5372 Instruction::Damage(DamageInstruction {
5373 side_ref: SideReference::SideTwo,
5374 damage_amount: 51,
5375 }),
5376 Instruction::ChangeStatus(ChangeStatusInstruction {
5377 side_ref: SideReference::SideTwo,
5378 pokemon_index: PokemonIndex::P0,
5379 old_status: PokemonStatus::NONE,
5380 new_status: PokemonStatus::BURN,
5381 }),
5382 ],
5383 },
5384 StateInstructions {
5385 percentage: 0.949999988,
5386 instruction_list: vec![
5387 Instruction::Damage(DamageInstruction {
5388 side_ref: SideReference::SideTwo,
5389 damage_amount: 51,
5390 }),
5391 Instruction::ChangeStatus(ChangeStatusInstruction {
5392 side_ref: SideReference::SideTwo,
5393 pokemon_index: PokemonIndex::P0,
5394 old_status: PokemonStatus::NONE,
5395 new_status: PokemonStatus::BURN,
5396 }),
5397 Instruction::ApplyVolatileStatus(ApplyVolatileStatusInstruction {
5398 side_ref: SideReference::SideTwo,
5399 volatile_status: PokemonVolatileStatus::FLINCH,
5400 }),
5401 ],
5402 },
5403 ];
5404
5405 assert_eq!(instructions, expected_instructions)
5406 }
5407
5408 #[test]
5409 fn test_flamebody() {
5410 let mut state: State = State::default();
5411 state.side_two.get_active().ability = Abilities::FLAMEBODY;
5412 let mut choice = MOVES.get(&Choices::TACKLE).unwrap().to_owned();
5413
5414 let mut instructions = vec![];
5415 generate_instructions_from_move(
5416 &mut state,
5417 &mut choice,
5418 &MOVES.get(&Choices::TACKLE).unwrap(),
5419 SideReference::SideOne,
5420 StateInstructions::default(),
5421 &mut instructions,
5422 false,
5423 );
5424
5425 let expected_instructions = vec![
5426 StateInstructions {
5427 percentage: 70.0,
5428 instruction_list: vec![Instruction::Damage(DamageInstruction {
5429 side_ref: SideReference::SideTwo,
5430 damage_amount: 48,
5431 })],
5432 },
5433 StateInstructions {
5434 percentage: 30.000002,
5435 instruction_list: vec![
5436 Instruction::Damage(DamageInstruction {
5437 side_ref: SideReference::SideTwo,
5438 damage_amount: 48,
5439 }),
5440 Instruction::ChangeStatus(ChangeStatusInstruction {
5441 side_ref: SideReference::SideOne,
5442 pokemon_index: PokemonIndex::P0,
5443 old_status: PokemonStatus::NONE,
5444 new_status: PokemonStatus::BURN,
5445 }),
5446 ],
5447 },
5448 ];
5449
5450 assert_eq!(instructions, expected_instructions)
5451 }
5452
5453 #[test]
5454 fn test_flamebody_creating_a_move_with_multiple_secondaries() {
5455 let mut state: State = State::default();
5456 state.side_two.get_active().ability = Abilities::FLAMEBODY;
5457 let mut choice = MOVES.get(&Choices::FIREPUNCH).unwrap().to_owned();
5458
5459 let mut instructions = vec![];
5460 generate_instructions_from_move(
5461 &mut state,
5462 &mut choice,
5463 &MOVES.get(&Choices::TACKLE).unwrap(),
5464 SideReference::SideOne,
5465 StateInstructions::default(),
5466 &mut instructions,
5467 false,
5468 );
5469
5470 let expected_instructions = vec![
5471 StateInstructions {
5472 percentage: 63.0,
5473 instruction_list: vec![Instruction::Damage(DamageInstruction {
5474 side_ref: SideReference::SideTwo,
5475 damage_amount: 60,
5476 })],
5477 },
5478 StateInstructions {
5479 percentage: 27.0000019,
5480 instruction_list: vec![
5481 Instruction::Damage(DamageInstruction {
5482 side_ref: SideReference::SideTwo,
5483 damage_amount: 60,
5484 }),
5485 Instruction::ChangeStatus(ChangeStatusInstruction {
5486 side_ref: SideReference::SideOne,
5487 pokemon_index: PokemonIndex::P0,
5488 old_status: PokemonStatus::NONE,
5489 new_status: PokemonStatus::BURN,
5490 }),
5491 ],
5492 },
5493 StateInstructions {
5494 percentage: 7.0,
5495 instruction_list: vec![
5496 Instruction::Damage(DamageInstruction {
5497 side_ref: SideReference::SideTwo,
5498 damage_amount: 60,
5499 }),
5500 Instruction::ChangeStatus(ChangeStatusInstruction {
5501 side_ref: SideReference::SideTwo,
5502 pokemon_index: PokemonIndex::P0,
5503 old_status: PokemonStatus::NONE,
5504 new_status: PokemonStatus::BURN,
5505 }),
5506 ],
5507 },
5508 StateInstructions {
5509 percentage: 3.0,
5510 instruction_list: vec![
5511 Instruction::Damage(DamageInstruction {
5512 side_ref: SideReference::SideTwo,
5513 damage_amount: 60,
5514 }),
5515 Instruction::ChangeStatus(ChangeStatusInstruction {
5516 side_ref: SideReference::SideTwo,
5517 pokemon_index: PokemonIndex::P0,
5518 old_status: PokemonStatus::NONE,
5519 new_status: PokemonStatus::BURN,
5520 }),
5521 Instruction::ChangeStatus(ChangeStatusInstruction {
5522 side_ref: SideReference::SideOne,
5523 pokemon_index: PokemonIndex::P0,
5524 old_status: PokemonStatus::NONE,
5525 new_status: PokemonStatus::BURN,
5526 }),
5527 ],
5528 },
5529 ];
5530 assert_eq!(instructions, expected_instructions)
5531 }
5532
5533 #[test]
5534 fn test_substitute_does_not_block_rest() {
5535 let mut state: State = State::default();
5536 state
5537 .side_one
5538 .volatile_statuses
5539 .insert(PokemonVolatileStatus::SUBSTITUTE);
5540 state.side_one.get_active().hp = state.side_one.get_active().maxhp - 1;
5541 let mut choice = MOVES.get(&Choices::REST).unwrap().to_owned();
5542
5543 let mut instructions = vec![];
5544 generate_instructions_from_move(
5545 &mut state,
5546 &mut choice,
5547 &MOVES.get(&Choices::TACKLE).unwrap(),
5548 SideReference::SideOne,
5549 StateInstructions::default(),
5550 &mut instructions,
5551 false,
5552 );
5553
5554 let expected_instructions = vec![StateInstructions {
5555 percentage: 100.0,
5556 instruction_list: vec![
5557 Instruction::ChangeStatus(ChangeStatusInstruction {
5558 side_ref: SideReference::SideOne,
5559 pokemon_index: PokemonIndex::P0,
5560 old_status: PokemonStatus::NONE,
5561 new_status: PokemonStatus::SLEEP,
5562 }),
5563 Instruction::SetRestTurns(SetSleepTurnsInstruction {
5564 side_ref: SideReference::SideOne,
5565 pokemon_index: PokemonIndex::P0,
5566 new_turns: 3,
5567 previous_turns: 0,
5568 }),
5569 Instruction::Heal(HealInstruction {
5570 side_ref: SideReference::SideOne,
5571 heal_amount: 1,
5572 }),
5573 ],
5574 }];
5575
5576 assert_eq!(instructions, expected_instructions)
5577 }
5578
5579 #[test]
5580 fn test_basic_heal_move() {
5581 let mut state: State = State::default();
5582 state.side_one.get_active().hp = 1;
5583 let mut choice = MOVES.get(&Choices::RECOVER).unwrap().to_owned();
5584
5585 let mut instructions = vec![];
5586 generate_instructions_from_move(
5587 &mut state,
5588 &mut choice,
5589 &MOVES.get(&Choices::TACKLE).unwrap(),
5590 SideReference::SideOne,
5591 StateInstructions::default(),
5592 &mut instructions,
5593 false,
5594 );
5595
5596 let expected_instructions = vec![StateInstructions {
5597 percentage: 100.0,
5598 instruction_list: vec![Instruction::Heal(HealInstruction {
5599 side_ref: SideReference::SideOne,
5600 heal_amount: 50,
5601 })],
5602 }];
5603
5604 assert_eq!(instructions, expected_instructions)
5605 }
5606
5607 #[test]
5608 fn test_heal_move_generates_no_instruction_at_maxhp() {
5609 let mut state: State = State::default();
5610 let mut choice = MOVES.get(&Choices::RECOVER).unwrap().to_owned();
5611
5612 let mut instructions = vec![];
5613 generate_instructions_from_move(
5614 &mut state,
5615 &mut choice,
5616 &MOVES.get(&Choices::TACKLE).unwrap(),
5617 SideReference::SideOne,
5618 StateInstructions::default(),
5619 &mut instructions,
5620 false,
5621 );
5622
5623 let expected_instructions = vec![StateInstructions {
5624 percentage: 100.0,
5625 instruction_list: vec![],
5626 }];
5627
5628 assert_eq!(instructions, expected_instructions)
5629 }
5630
5631 #[test]
5632 fn test_basic_negative_heal_move() {
5633 let mut state: State = State::default();
5634 let mut choice = MOVES.get(&Choices::EXPLOSION).unwrap().to_owned();
5635
5636 let mut instructions = vec![];
5637 generate_instructions_from_move(
5638 &mut state,
5639 &mut choice,
5640 &MOVES.get(&Choices::TACKLE).unwrap(),
5641 SideReference::SideOne,
5642 StateInstructions::default(),
5643 &mut instructions,
5644 false,
5645 );
5646
5647 let expected_instructions = vec![StateInstructions {
5648 percentage: 100.0,
5649 instruction_list: vec![
5650 Instruction::Damage(DamageInstruction {
5651 side_ref: SideReference::SideOne,
5652 damage_amount: 100,
5653 }),
5654 Instruction::Damage(DamageInstruction {
5655 side_ref: SideReference::SideTwo,
5656 damage_amount: 100,
5657 }),
5658 ],
5659 }];
5660
5661 assert_eq!(instructions, expected_instructions)
5662 }
5663
5664 #[test]
5665 fn test_negative_heal_move_does_not_overkill() {
5666 let mut state: State = State::default();
5667 state.side_one.get_active().hp = 1;
5668 let mut choice = MOVES.get(&Choices::EXPLOSION).unwrap().to_owned();
5669
5670 let mut instructions = vec![];
5671 generate_instructions_from_move(
5672 &mut state,
5673 &mut choice,
5674 &MOVES.get(&Choices::TACKLE).unwrap(),
5675 SideReference::SideOne,
5676 StateInstructions::default(),
5677 &mut instructions,
5678 false,
5679 );
5680
5681 let expected_instructions = vec![StateInstructions {
5682 percentage: 100.0,
5683 instruction_list: vec![
5684 Instruction::Damage(DamageInstruction {
5685 side_ref: SideReference::SideOne,
5686 damage_amount: 1,
5687 }),
5688 Instruction::Damage(DamageInstruction {
5689 side_ref: SideReference::SideTwo,
5690 damage_amount: 100,
5691 }),
5692 ],
5693 }];
5694
5695 assert_eq!(instructions, expected_instructions)
5696 }
5697
5698 #[test]
5699 fn test_heal_move_does_not_overheal() {
5700 let mut state: State = State::default();
5701 state.side_one.get_active().hp = 55;
5702 let mut choice = MOVES.get(&Choices::RECOVER).unwrap().to_owned();
5703
5704 let mut instructions = vec![];
5705 generate_instructions_from_move(
5706 &mut state,
5707 &mut choice,
5708 &MOVES.get(&Choices::TACKLE).unwrap(),
5709 SideReference::SideOne,
5710 StateInstructions::default(),
5711 &mut instructions,
5712 false,
5713 );
5714
5715 let expected_instructions = vec![StateInstructions {
5716 percentage: 100.0,
5717 instruction_list: vec![Instruction::Heal(HealInstruction {
5718 side_ref: SideReference::SideOne,
5719 heal_amount: 45,
5720 })],
5721 }];
5722
5723 assert_eq!(instructions, expected_instructions)
5724 }
5725
5726 #[test]
5727 fn test_basic_boosting_move() {
5728 let mut state: State = State::default();
5729 let mut choice = MOVES.get(&Choices::SWORDSDANCE).unwrap().to_owned();
5730
5731 let mut instructions = vec![];
5732 generate_instructions_from_move(
5733 &mut state,
5734 &mut choice,
5735 &MOVES.get(&Choices::TACKLE).unwrap(),
5736 SideReference::SideOne,
5737 StateInstructions::default(),
5738 &mut instructions,
5739 false,
5740 );
5741
5742 let expected_instructions = vec![StateInstructions {
5743 percentage: 100.0,
5744 instruction_list: vec![Instruction::Boost(BoostInstruction {
5745 side_ref: SideReference::SideOne,
5746 stat: PokemonBoostableStat::Attack,
5747 amount: 2,
5748 })],
5749 }];
5750
5751 assert_eq!(instructions, expected_instructions)
5752 }
5753
5754 #[test]
5755 fn test_does_not_overboost() {
5756 let mut state: State = State::default();
5757 state.side_one.attack_boost = 5;
5758 let mut choice = MOVES.get(&Choices::SWORDSDANCE).unwrap().to_owned();
5759
5760 let mut instructions = vec![];
5761 generate_instructions_from_move(
5762 &mut state,
5763 &mut choice,
5764 &MOVES.get(&Choices::TACKLE).unwrap(),
5765 SideReference::SideOne,
5766 StateInstructions::default(),
5767 &mut instructions,
5768 false,
5769 );
5770
5771 let expected_instructions = vec![StateInstructions {
5772 percentage: 100.0,
5773 instruction_list: vec![Instruction::Boost(BoostInstruction {
5774 side_ref: SideReference::SideOne,
5775 stat: PokemonBoostableStat::Attack,
5776 amount: 1,
5777 })],
5778 }];
5779
5780 assert_eq!(instructions, expected_instructions)
5781 }
5782
5783 #[test]
5784 fn test_no_instruction_when_boosting_at_max() {
5785 let mut state: State = State::default();
5786 state.side_one.attack_boost = 6;
5787 let mut choice = MOVES.get(&Choices::SWORDSDANCE).unwrap().to_owned();
5788
5789 let mut instructions = vec![];
5790 generate_instructions_from_move(
5791 &mut state,
5792 &mut choice,
5793 &MOVES.get(&Choices::TACKLE).unwrap(),
5794 SideReference::SideOne,
5795 StateInstructions::default(),
5796 &mut instructions,
5797 false,
5798 );
5799
5800 let expected_instructions = vec![StateInstructions {
5801 percentage: 100.0,
5802 instruction_list: vec![],
5803 }];
5804
5805 assert_eq!(instructions, expected_instructions)
5806 }
5807
5808 #[test]
5809 fn test_boost_lowering_that_can_miss() {
5810 let mut state: State = State::default();
5811 let mut choice = MOVES.get(&Choices::KINESIS).unwrap().to_owned();
5812
5813 let mut instructions = vec![];
5814 generate_instructions_from_move(
5815 &mut state,
5816 &mut choice,
5817 &MOVES.get(&Choices::TACKLE).unwrap(),
5818 SideReference::SideOne,
5819 StateInstructions::default(),
5820 &mut instructions,
5821 false,
5822 );
5823
5824 let expected_instructions = vec![
5825 StateInstructions {
5826 percentage: 19.999998,
5827 instruction_list: vec![],
5828 },
5829 StateInstructions {
5830 percentage: 80.0,
5831 instruction_list: vec![Instruction::Boost(BoostInstruction {
5832 side_ref: SideReference::SideTwo,
5833 stat: PokemonBoostableStat::Accuracy,
5834 amount: -1,
5835 })],
5836 },
5837 ];
5838
5839 assert_eq!(instructions, expected_instructions)
5840 }
5841
5842 #[test]
5843 fn test_basic_boost_lowering() {
5844 let mut state: State = State::default();
5845 let mut choice = MOVES.get(&Choices::CHARM).unwrap().to_owned();
5846
5847 let mut instructions = vec![];
5848 generate_instructions_from_move(
5849 &mut state,
5850 &mut choice,
5851 &MOVES.get(&Choices::TACKLE).unwrap(),
5852 SideReference::SideOne,
5853 StateInstructions::default(),
5854 &mut instructions,
5855 false,
5856 );
5857
5858 let expected_instructions = vec![StateInstructions {
5859 percentage: 100.0,
5860 instruction_list: vec![Instruction::Boost(BoostInstruction {
5861 side_ref: SideReference::SideTwo,
5862 stat: PokemonBoostableStat::Attack,
5863 amount: -2,
5864 })],
5865 }];
5866
5867 assert_eq!(instructions, expected_instructions)
5868 }
5869
5870 #[test]
5871 fn test_cannot_boost_lower_than_negative_6() {
5872 let mut state: State = State::default();
5873 state.side_two.attack_boost = -5;
5874 let mut choice = MOVES.get(&Choices::CHARM).unwrap().to_owned();
5875
5876 let mut instructions = vec![];
5877 generate_instructions_from_move(
5878 &mut state,
5879 &mut choice,
5880 &MOVES.get(&Choices::TACKLE).unwrap(),
5881 SideReference::SideOne,
5882 StateInstructions::default(),
5883 &mut instructions,
5884 false,
5885 );
5886
5887 let expected_instructions = vec![StateInstructions {
5888 percentage: 100.0,
5889 instruction_list: vec![Instruction::Boost(BoostInstruction {
5890 side_ref: SideReference::SideTwo,
5891 stat: PokemonBoostableStat::Attack,
5892 amount: -1,
5893 })],
5894 }];
5895
5896 assert_eq!(instructions, expected_instructions)
5897 }
5898
5899 #[test]
5900 fn test_no_boost_when_already_at_minimum() {
5901 let mut state: State = State::default();
5902 state.side_two.attack_boost = -6;
5903 let mut choice = MOVES.get(&Choices::CHARM).unwrap().to_owned();
5904
5905 let mut instructions = vec![];
5906 generate_instructions_from_move(
5907 &mut state,
5908 &mut choice,
5909 &MOVES.get(&Choices::TACKLE).unwrap(),
5910 SideReference::SideOne,
5911 StateInstructions::default(),
5912 &mut instructions,
5913 false,
5914 );
5915
5916 let expected_instructions = vec![StateInstructions {
5917 percentage: 100.0,
5918 instruction_list: vec![],
5919 }];
5920
5921 assert_eq!(instructions, expected_instructions)
5922 }
5923
5924 #[test]
5925 fn test_clearbody_blocks_stat_lowering() {
5926 let mut state: State = State::default();
5927 state.side_two.get_active().ability = Abilities::CLEARBODY;
5928 let mut choice = MOVES.get(&Choices::CHARM).unwrap().to_owned();
5929
5930 let mut instructions = vec![];
5931 generate_instructions_from_move(
5932 &mut state,
5933 &mut choice,
5934 &MOVES.get(&Choices::TACKLE).unwrap(),
5935 SideReference::SideOne,
5936 StateInstructions::default(),
5937 &mut instructions,
5938 false,
5939 );
5940
5941 let expected_instructions = vec![StateInstructions {
5942 percentage: 100.0,
5943 instruction_list: vec![],
5944 }];
5945
5946 assert_eq!(instructions, expected_instructions)
5947 }
5948
5949 #[test]
5950 fn test_clearbody_does_not_block_self_stat_lowering() {
5951 let mut state: State = State::default();
5952 state.side_one.get_active().ability = Abilities::CLEARBODY;
5953 let mut choice = MOVES.get(&Choices::SHELLSMASH).unwrap().to_owned();
5954
5955 let mut instructions = vec![];
5956 generate_instructions_from_move(
5957 &mut state,
5958 &mut choice,
5959 &MOVES.get(&Choices::TACKLE).unwrap(),
5960 SideReference::SideOne,
5961 StateInstructions::default(),
5962 &mut instructions,
5963 false,
5964 );
5965
5966 let expected_instructions = vec![StateInstructions {
5967 percentage: 100.0,
5968 instruction_list: vec![
5969 Instruction::Boost(BoostInstruction {
5970 side_ref: SideReference::SideOne,
5971 stat: PokemonBoostableStat::Attack,
5972 amount: 2,
5973 }),
5974 Instruction::Boost(BoostInstruction {
5975 side_ref: SideReference::SideOne,
5976 stat: PokemonBoostableStat::Defense,
5977 amount: -1,
5978 }),
5979 Instruction::Boost(BoostInstruction {
5980 side_ref: SideReference::SideOne,
5981 stat: PokemonBoostableStat::SpecialAttack,
5982 amount: 2,
5983 }),
5984 Instruction::Boost(BoostInstruction {
5985 side_ref: SideReference::SideOne,
5986 stat: PokemonBoostableStat::SpecialDefense,
5987 amount: -1,
5988 }),
5989 Instruction::Boost(BoostInstruction {
5990 side_ref: SideReference::SideOne,
5991 stat: PokemonBoostableStat::Speed,
5992 amount: 2,
5993 }),
5994 ],
5995 }];
5996
5997 assert_eq!(instructions, expected_instructions)
5998 }
5999
6000 #[test]
6001 fn test_defog_does_not_change_terrain_if_terrain_is_none() {
6002 let mut state: State = State::default();
6003
6004 let mut choice = MOVES.get(&Choices::DEFOG).unwrap().to_owned();
6005
6006 let mut instructions = vec![];
6007 generate_instructions_from_move(
6008 &mut state,
6009 &mut choice,
6010 &MOVES.get(&Choices::TACKLE).unwrap(),
6011 SideReference::SideOne,
6012 StateInstructions::default(),
6013 &mut instructions,
6014 false,
6015 );
6016
6017 let expected_instructions = vec![StateInstructions {
6018 percentage: 100.0,
6019 instruction_list: vec![],
6020 }];
6021
6022 assert_eq!(instructions, expected_instructions)
6023 }
6024
6025 #[test]
6026 fn test_defog_clears_terrain() {
6027 let mut state: State = State::default();
6028 state.terrain.terrain_type = Terrain::ELECTRICTERRAIN;
6029 state.terrain.turns_remaining = 1;
6030
6031 let mut choice = MOVES.get(&Choices::DEFOG).unwrap().to_owned();
6032
6033 let mut instructions = vec![];
6034 generate_instructions_from_move(
6035 &mut state,
6036 &mut choice,
6037 &MOVES.get(&Choices::TACKLE).unwrap(),
6038 SideReference::SideOne,
6039 StateInstructions::default(),
6040 &mut instructions,
6041 false,
6042 );
6043
6044 let expected_instructions = vec![StateInstructions {
6045 percentage: 100.0,
6046 instruction_list: vec![Instruction::ChangeTerrain(ChangeTerrain {
6047 new_terrain: Terrain::NONE,
6048 new_terrain_turns_remaining: 0,
6049 previous_terrain: Terrain::ELECTRICTERRAIN,
6050 previous_terrain_turns_remaining: 1,
6051 })],
6052 }];
6053
6054 assert_eq!(instructions, expected_instructions)
6055 }
6056
6057 #[test]
6058 fn test_defog_clears_terrain_and_side_conditions() {
6059 let mut state: State = State::default();
6060 state.terrain.terrain_type = Terrain::ELECTRICTERRAIN;
6061 state.terrain.turns_remaining = 1;
6062 state.side_one.side_conditions.reflect = 1;
6063 state.side_two.side_conditions.reflect = 1;
6064
6065 let mut choice = MOVES.get(&Choices::DEFOG).unwrap().to_owned();
6066
6067 let mut instructions = vec![];
6068 generate_instructions_from_move(
6069 &mut state,
6070 &mut choice,
6071 &MOVES.get(&Choices::TACKLE).unwrap(),
6072 SideReference::SideOne,
6073 StateInstructions::default(),
6074 &mut instructions,
6075 false,
6076 );
6077
6078 let expected_instructions = vec![StateInstructions {
6079 percentage: 100.0,
6080 instruction_list: vec![
6081 Instruction::ChangeTerrain(ChangeTerrain {
6082 new_terrain: Terrain::NONE,
6083 new_terrain_turns_remaining: 0,
6084 previous_terrain: Terrain::ELECTRICTERRAIN,
6085 previous_terrain_turns_remaining: 1,
6086 }),
6087 Instruction::ChangeSideCondition(ChangeSideConditionInstruction {
6088 side_ref: SideReference::SideOne,
6089 side_condition: PokemonSideCondition::Reflect,
6090 amount: -1,
6091 }),
6092 Instruction::ChangeSideCondition(ChangeSideConditionInstruction {
6093 side_ref: SideReference::SideTwo,
6094 side_condition: PokemonSideCondition::Reflect,
6095 amount: -1,
6096 }),
6097 ],
6098 }];
6099 assert_eq!(instructions, expected_instructions)
6100 }
6101
6102 #[test]
6103 fn test_tidyup_clears_side_conditions_and_substitutes() {
6104 let mut state: State = State::default();
6105 state.terrain.terrain_type = Terrain::ELECTRICTERRAIN;
6106 state
6107 .side_one
6108 .volatile_statuses
6109 .insert(PokemonVolatileStatus::SUBSTITUTE);
6110 state
6111 .side_two
6112 .volatile_statuses
6113 .insert(PokemonVolatileStatus::SUBSTITUTE);
6114 state.side_one.substitute_health = 10;
6115 state.side_two.substitute_health = 25;
6116 state.terrain.turns_remaining = 1;
6117 state.side_one.side_conditions.spikes = 2;
6118 state.side_two.side_conditions.stealth_rock = 1;
6119
6120 let mut choice = MOVES.get(&Choices::TIDYUP).unwrap().to_owned();
6121
6122 let mut instructions = vec![];
6123 generate_instructions_from_move(
6124 &mut state,
6125 &mut choice,
6126 &MOVES.get(&Choices::TACKLE).unwrap(),
6127 SideReference::SideOne,
6128 StateInstructions::default(),
6129 &mut instructions,
6130 false,
6131 );
6132
6133 let expected_instructions = vec![StateInstructions {
6134 percentage: 100.0,
6135 instruction_list: vec![
6136 Instruction::ChangeSideCondition(ChangeSideConditionInstruction {
6137 side_ref: SideReference::SideOne,
6138 side_condition: PokemonSideCondition::Spikes,
6139 amount: -2,
6140 }),
6141 Instruction::ChangeSideCondition(ChangeSideConditionInstruction {
6142 side_ref: SideReference::SideTwo,
6143 side_condition: PokemonSideCondition::Stealthrock,
6144 amount: -1,
6145 }),
6146 Instruction::ChangeSubstituteHealth(ChangeSubsituteHealthInstruction {
6147 side_ref: SideReference::SideOne,
6148 health_change: -10,
6149 }),
6150 Instruction::RemoveVolatileStatus(RemoveVolatileStatusInstruction {
6151 side_ref: SideReference::SideOne,
6152 volatile_status: PokemonVolatileStatus::SUBSTITUTE,
6153 }),
6154 Instruction::ChangeSubstituteHealth(ChangeSubsituteHealthInstruction {
6155 side_ref: SideReference::SideTwo,
6156 health_change: -25,
6157 }),
6158 Instruction::RemoveVolatileStatus(RemoveVolatileStatusInstruction {
6159 side_ref: SideReference::SideTwo,
6160 volatile_status: PokemonVolatileStatus::SUBSTITUTE,
6161 }),
6162 Instruction::Boost(BoostInstruction {
6163 side_ref: SideReference::SideOne,
6164 stat: PokemonBoostableStat::Attack,
6165 amount: 1,
6166 }),
6167 Instruction::Boost(BoostInstruction {
6168 side_ref: SideReference::SideOne,
6169 stat: PokemonBoostableStat::Speed,
6170 amount: 1,
6171 }),
6172 ],
6173 }];
6174 assert_eq!(instructions, expected_instructions)
6175 }
6176
6177 #[test]
6178 #[cfg(any(feature = "gen8", feature = "gen9"))]
6179 fn test_rapidspin_clears_hazards() {
6180 let mut state: State = State::default();
6181 state.side_one.side_conditions.stealth_rock = 1;
6182
6183 let mut choice = MOVES.get(&Choices::RAPIDSPIN).unwrap().to_owned();
6184
6185 let mut instructions = vec![];
6186 generate_instructions_from_move(
6187 &mut state,
6188 &mut choice,
6189 &MOVES.get(&Choices::TACKLE).unwrap(),
6190 SideReference::SideOne,
6191 StateInstructions::default(),
6192 &mut instructions,
6193 false,
6194 );
6195
6196 let expected_instructions = vec![StateInstructions {
6197 percentage: 100.0,
6198 instruction_list: vec![
6199 Instruction::Damage(DamageInstruction {
6200 side_ref: SideReference::SideTwo,
6201 damage_amount: 61,
6202 }),
6203 Instruction::ChangeSideCondition(ChangeSideConditionInstruction {
6204 side_ref: SideReference::SideOne,
6205 side_condition: PokemonSideCondition::Stealthrock,
6206 amount: -1,
6207 }),
6208 Instruction::Boost(BoostInstruction {
6209 side_ref: SideReference::SideOne,
6210 amount: 1,
6211 stat: PokemonBoostableStat::Speed,
6212 }),
6213 ],
6214 }];
6215
6216 assert_eq!(instructions, expected_instructions)
6217 }
6218
6219 #[test]
6220 fn test_missing_rapidspin_does_not_clear_hazards() {
6221 let mut state: State = State::default();
6222 state.side_two.get_active().types = (PokemonType::GHOST, PokemonType::NORMAL);
6223 state.side_one.side_conditions.stealth_rock = 1;
6224
6225 let mut choice = MOVES.get(&Choices::RAPIDSPIN).unwrap().to_owned();
6226
6227 let mut instructions = vec![];
6228 generate_instructions_from_move(
6229 &mut state,
6230 &mut choice,
6231 &MOVES.get(&Choices::TACKLE).unwrap(),
6232 SideReference::SideOne,
6233 StateInstructions::default(),
6234 &mut instructions,
6235 false,
6236 );
6237
6238 let expected_instructions = vec![StateInstructions {
6239 percentage: 100.0,
6240 instruction_list: vec![],
6241 }];
6242 assert_eq!(instructions, expected_instructions)
6243 }
6244
6245 #[test]
6246 fn test_acid_into_steel_type() {
6247 let mut state: State = State::default();
6248 state.side_two.get_active().types = (PokemonType::STEEL, PokemonType::NORMAL);
6249
6250 let mut choice = MOVES.get(&Choices::ACID).unwrap().to_owned();
6251
6252 let mut instructions = vec![];
6253 generate_instructions_from_move(
6254 &mut state,
6255 &mut choice,
6256 &MOVES.get(&Choices::TACKLE).unwrap(),
6257 SideReference::SideOne,
6258 StateInstructions::default(),
6259 &mut instructions,
6260 false,
6261 );
6262
6263 let expected_instructions = vec![StateInstructions {
6264 percentage: 100.0,
6265 instruction_list: vec![],
6266 }];
6267 assert_eq!(instructions, expected_instructions)
6268 }
6269
6270 #[test]
6271 #[cfg(any(feature = "gen8", feature = "gen9"))]
6272 fn test_rapidspin_clears_multiple_hazards() {
6273 let mut state: State = State::default();
6274 state.side_one.side_conditions.stealth_rock = 1;
6275 state.side_one.side_conditions.toxic_spikes = 2;
6276 state.side_one.side_conditions.spikes = 3;
6277 state.side_one.side_conditions.sticky_web = 1;
6278
6279 let mut choice = MOVES.get(&Choices::RAPIDSPIN).unwrap().to_owned();
6280
6281 let mut instructions = vec![];
6282 generate_instructions_from_move(
6283 &mut state,
6284 &mut choice,
6285 &MOVES.get(&Choices::TACKLE).unwrap(),
6286 SideReference::SideOne,
6287 StateInstructions::default(),
6288 &mut instructions,
6289 false,
6290 );
6291
6292 let expected_instructions = vec![StateInstructions {
6293 percentage: 100.0,
6294 instruction_list: vec![
6295 Instruction::Damage(DamageInstruction {
6296 side_ref: SideReference::SideTwo,
6297 damage_amount: 61,
6298 }),
6299 Instruction::ChangeSideCondition(ChangeSideConditionInstruction {
6300 side_ref: SideReference::SideOne,
6301 side_condition: PokemonSideCondition::Stealthrock,
6302 amount: -1,
6303 }),
6304 Instruction::ChangeSideCondition(ChangeSideConditionInstruction {
6305 side_ref: SideReference::SideOne,
6306 side_condition: PokemonSideCondition::Spikes,
6307 amount: -3,
6308 }),
6309 Instruction::ChangeSideCondition(ChangeSideConditionInstruction {
6310 side_ref: SideReference::SideOne,
6311 side_condition: PokemonSideCondition::ToxicSpikes,
6312 amount: -2,
6313 }),
6314 Instruction::ChangeSideCondition(ChangeSideConditionInstruction {
6315 side_ref: SideReference::SideOne,
6316 side_condition: PokemonSideCondition::StickyWeb,
6317 amount: -1,
6318 }),
6319 Instruction::Boost(BoostInstruction {
6320 side_ref: SideReference::SideOne,
6321 amount: 1,
6322 stat: PokemonBoostableStat::Speed,
6323 }),
6324 ],
6325 }];
6326
6327 assert_eq!(instructions, expected_instructions)
6328 }
6329
6330 #[test]
6331 #[cfg(any(feature = "gen8", feature = "gen9"))]
6332 fn test_rapidspin_does_not_clear_opponent_hazards() {
6333 let mut state: State = State::default();
6334 state.side_two.side_conditions.stealth_rock = 1;
6335 state.side_two.side_conditions.toxic_spikes = 2;
6336 state.side_two.side_conditions.spikes = 3;
6337 state.side_two.side_conditions.sticky_web = 1;
6338
6339 let mut choice = MOVES.get(&Choices::RAPIDSPIN).unwrap().to_owned();
6340
6341 let mut instructions = vec![];
6342 generate_instructions_from_move(
6343 &mut state,
6344 &mut choice,
6345 &MOVES.get(&Choices::TACKLE).unwrap(),
6346 SideReference::SideOne,
6347 StateInstructions::default(),
6348 &mut instructions,
6349 false,
6350 );
6351
6352 let expected_instructions = vec![StateInstructions {
6353 percentage: 100.0,
6354 instruction_list: vec![
6355 Instruction::Damage(DamageInstruction {
6356 side_ref: SideReference::SideTwo,
6357 damage_amount: 61,
6358 }),
6359 Instruction::Boost(BoostInstruction {
6360 side_ref: SideReference::SideOne,
6361 amount: 1,
6362 stat: PokemonBoostableStat::Speed,
6363 }),
6364 ],
6365 }];
6366
6367 assert_eq!(instructions, expected_instructions)
6368 }
6369
6370 #[test]
6371 fn test_courtchange_basic_swap() {
6372 let mut state: State = State::default();
6373 state.side_one.side_conditions.stealth_rock = 1;
6374
6375 let mut choice = MOVES.get(&Choices::COURTCHANGE).unwrap().to_owned();
6376
6377 let mut instructions = vec![];
6378 generate_instructions_from_move(
6379 &mut state,
6380 &mut choice,
6381 &MOVES.get(&Choices::TACKLE).unwrap(),
6382 SideReference::SideOne,
6383 StateInstructions::default(),
6384 &mut instructions,
6385 false,
6386 );
6387
6388 let expected_instructions = vec![StateInstructions {
6389 percentage: 100.0,
6390 instruction_list: vec![
6391 Instruction::ChangeSideCondition(ChangeSideConditionInstruction {
6392 side_ref: SideReference::SideOne,
6393 side_condition: PokemonSideCondition::Stealthrock,
6394 amount: -1,
6395 }),
6396 Instruction::ChangeSideCondition(ChangeSideConditionInstruction {
6397 side_ref: SideReference::SideTwo,
6398 side_condition: PokemonSideCondition::Stealthrock,
6399 amount: 1,
6400 }),
6401 ],
6402 }];
6403
6404 assert_eq!(instructions, expected_instructions)
6405 }
6406
6407 #[test]
6408 fn test_courtchange_complicated_swap() {
6409 let mut state: State = State::default();
6410 state.side_one.side_conditions.stealth_rock = 1;
6411 state.side_two.side_conditions.toxic_spikes = 2;
6412 state.side_two.side_conditions.spikes = 3;
6413 state.side_two.side_conditions.sticky_web = 1;
6414
6415 let mut choice = MOVES.get(&Choices::COURTCHANGE).unwrap().to_owned();
6416
6417 let mut instructions = vec![];
6418 generate_instructions_from_move(
6419 &mut state,
6420 &mut choice,
6421 &MOVES.get(&Choices::TACKLE).unwrap(),
6422 SideReference::SideOne,
6423 StateInstructions::default(),
6424 &mut instructions,
6425 false,
6426 );
6427
6428 let expected_instructions = vec![StateInstructions {
6429 percentage: 100.0,
6430 instruction_list: vec![
6431 Instruction::ChangeSideCondition(ChangeSideConditionInstruction {
6432 side_ref: SideReference::SideOne,
6433 side_condition: PokemonSideCondition::Stealthrock,
6434 amount: -1,
6435 }),
6436 Instruction::ChangeSideCondition(ChangeSideConditionInstruction {
6437 side_ref: SideReference::SideTwo,
6438 side_condition: PokemonSideCondition::Stealthrock,
6439 amount: 1,
6440 }),
6441 Instruction::ChangeSideCondition(ChangeSideConditionInstruction {
6442 side_ref: SideReference::SideTwo,
6443 side_condition: PokemonSideCondition::Spikes,
6444 amount: -3,
6445 }),
6446 Instruction::ChangeSideCondition(ChangeSideConditionInstruction {
6447 side_ref: SideReference::SideOne,
6448 side_condition: PokemonSideCondition::Spikes,
6449 amount: 3,
6450 }),
6451 Instruction::ChangeSideCondition(ChangeSideConditionInstruction {
6452 side_ref: SideReference::SideTwo,
6453 side_condition: PokemonSideCondition::ToxicSpikes,
6454 amount: -2,
6455 }),
6456 Instruction::ChangeSideCondition(ChangeSideConditionInstruction {
6457 side_ref: SideReference::SideOne,
6458 side_condition: PokemonSideCondition::ToxicSpikes,
6459 amount: 2,
6460 }),
6461 Instruction::ChangeSideCondition(ChangeSideConditionInstruction {
6462 side_ref: SideReference::SideTwo,
6463 side_condition: PokemonSideCondition::StickyWeb,
6464 amount: -1,
6465 }),
6466 Instruction::ChangeSideCondition(ChangeSideConditionInstruction {
6467 side_ref: SideReference::SideOne,
6468 side_condition: PokemonSideCondition::StickyWeb,
6469 amount: 1,
6470 }),
6471 ],
6472 }];
6473
6474 assert_eq!(instructions, expected_instructions)
6475 }
6476
6477 #[test]
6478 fn test_stoneaxe_does_not_set_stealthrock_if_already_set() {
6479 let mut state: State = State::default();
6480 state.side_two.side_conditions.stealth_rock = 1;
6481 let mut choice = MOVES.get(&Choices::STONEAXE).unwrap().to_owned();
6482
6483 let mut instructions = vec![];
6484 generate_instructions_from_move(
6485 &mut state,
6486 &mut choice,
6487 &MOVES.get(&Choices::TACKLE).unwrap(),
6488 SideReference::SideOne,
6489 StateInstructions::default(),
6490 &mut instructions,
6491 false,
6492 );
6493
6494 let expected_instructions = vec![
6495 StateInstructions {
6496 percentage: 10.000002,
6497 instruction_list: vec![],
6498 },
6499 StateInstructions {
6500 percentage: 90.0,
6501 instruction_list: vec![Instruction::Damage(DamageInstruction {
6502 side_ref: SideReference::SideTwo,
6503 damage_amount: 51,
6504 })],
6505 },
6506 ];
6507
6508 assert_eq!(instructions, expected_instructions)
6509 }
6510
6511 #[test]
6512 fn test_flinched_pokemon_cannot_move() {
6513 let mut state: State = State::default();
6514 let mut choice = MOVES.get(&Choices::TACKLE).unwrap().to_owned();
6515 state
6516 .side_one
6517 .volatile_statuses
6518 .insert(PokemonVolatileStatus::FLINCH);
6519
6520 let mut instructions = vec![];
6521 generate_instructions_from_move(
6522 &mut state,
6523 &mut choice,
6524 &MOVES.get(&Choices::TACKLE).unwrap(),
6525 SideReference::SideOne,
6526 StateInstructions::default(),
6527 &mut instructions,
6528 false,
6529 );
6530 assert_eq!(instructions, vec![StateInstructions::default()])
6531 }
6532
6533 #[test]
6534 fn test_dead_pokemon_moving_second_does_nothing() {
6535 let mut state: State = State::default();
6536 let mut choice = MOVES.get(&Choices::TACKLE).unwrap().to_owned();
6537 choice.first_move = false;
6538 state.side_one.get_active().hp = 0;
6539
6540 let mut instructions = vec![];
6541 generate_instructions_from_move(
6542 &mut state,
6543 &mut choice,
6544 &MOVES.get(&Choices::TACKLE).unwrap(),
6545 SideReference::SideOne,
6546 StateInstructions::default(),
6547 &mut instructions,
6548 false,
6549 );
6550 assert_eq!(instructions, vec![StateInstructions::default()])
6551 }
6552
6553 #[test]
6554 fn test_cannot_ohko_versus_study() {
6555 let mut state: State = State::default();
6556 let mut choice = MOVES.get(&Choices::EARTHQUAKE).unwrap().to_owned();
6557 state.side_two.get_active().ability = Abilities::STURDY;
6558 state.side_two.get_active().hp = 50;
6559 state.side_two.get_active().maxhp = 50;
6560
6561 let mut instructions = vec![];
6562 generate_instructions_from_move(
6563 &mut state,
6564 &mut choice,
6565 &MOVES.get(&Choices::TACKLE).unwrap(),
6566 SideReference::SideOne,
6567 StateInstructions::default(),
6568 &mut instructions,
6569 false,
6570 );
6571
6572 let expected_instructions: StateInstructions = StateInstructions {
6573 percentage: 100.0,
6574 instruction_list: vec![Instruction::Damage(DamageInstruction {
6575 side_ref: SideReference::SideTwo,
6576 damage_amount: 49,
6577 })],
6578 };
6579
6580 assert_eq!(instructions, vec![expected_instructions])
6581 }
6582
6583 #[test]
6584 fn test_cannot_ohko_versus_sash() {
6585 let mut state: State = State::default();
6586 let mut choice = MOVES.get(&Choices::EARTHQUAKE).unwrap().to_owned();
6587 state.side_two.get_active().item = Items::FOCUSSASH;
6588 state.side_two.get_active().hp = 50;
6589 state.side_two.get_active().maxhp = 50;
6590
6591 let mut instructions = vec![];
6592 generate_instructions_from_move(
6593 &mut state,
6594 &mut choice,
6595 &MOVES.get(&Choices::TACKLE).unwrap(),
6596 SideReference::SideOne,
6597 StateInstructions::default(),
6598 &mut instructions,
6599 false,
6600 );
6601
6602 let expected_instructions: StateInstructions = StateInstructions {
6603 percentage: 100.0,
6604 instruction_list: vec![Instruction::Damage(DamageInstruction {
6605 side_ref: SideReference::SideTwo,
6606 damage_amount: 49,
6607 })],
6608 };
6609
6610 assert_eq!(instructions, vec![expected_instructions])
6611 }
6612
6613 #[test]
6614 fn test_sturdy_does_not_affect_non_ohko_move() {
6615 let mut state: State = State::default();
6616 let mut choice = MOVES.get(&Choices::EARTHQUAKE).unwrap().to_owned();
6617 state.side_two.get_active().ability = Abilities::STURDY;
6618 state.side_two.get_active().hp = 45;
6619 state.side_two.get_active().maxhp = 50;
6620
6621 let mut instructions = vec![];
6622 generate_instructions_from_move(
6623 &mut state,
6624 &mut choice,
6625 &MOVES.get(&Choices::TACKLE).unwrap(),
6626 SideReference::SideOne,
6627 StateInstructions::default(),
6628 &mut instructions,
6629 false,
6630 );
6631
6632 let expected_instructions: StateInstructions = StateInstructions {
6633 percentage: 100.0,
6634 instruction_list: vec![Instruction::Damage(DamageInstruction {
6635 side_ref: SideReference::SideTwo,
6636 damage_amount: 45,
6637 })],
6638 };
6639
6640 assert_eq!(instructions, vec![expected_instructions])
6641 }
6642
6643 #[test]
6644 fn test_beastboost_boosts_on_kill() {
6645 let mut state: State = State::default();
6646 let mut choice = MOVES.get(&Choices::TACKLE).unwrap().to_owned();
6647 state.side_one.get_active().ability = Abilities::BEASTBOOST;
6648 state.side_one.get_active().attack = 500; state.side_two.get_active().hp = 1;
6650
6651 let mut instructions = vec![];
6652 generate_instructions_from_move(
6653 &mut state,
6654 &mut choice,
6655 &MOVES.get(&Choices::TACKLE).unwrap(),
6656 SideReference::SideOne,
6657 StateInstructions::default(),
6658 &mut instructions,
6659 false,
6660 );
6661
6662 let expected_instructions: StateInstructions = StateInstructions {
6663 percentage: 100.0,
6664 instruction_list: vec![
6665 Instruction::Damage(DamageInstruction {
6666 side_ref: SideReference::SideTwo,
6667 damage_amount: 1,
6668 }),
6669 Instruction::Boost(BoostInstruction {
6670 side_ref: SideReference::SideOne,
6671 stat: PokemonBoostableStat::Attack,
6672 amount: 1,
6673 }),
6674 ],
6675 };
6676 assert_eq!(instructions, vec![expected_instructions])
6677 }
6678
6679 #[test]
6680 fn test_beastboost_boosts_different_stat_on_kill() {
6681 let mut state: State = State::default();
6682 let mut choice = MOVES.get(&Choices::TACKLE).unwrap().to_owned();
6683 state.side_one.get_active().ability = Abilities::BEASTBOOST;
6684 state.side_one.get_active().defense = 500; state.side_two.get_active().hp = 1;
6686
6687 let mut instructions = vec![];
6688 generate_instructions_from_move(
6689 &mut state,
6690 &mut choice,
6691 &MOVES.get(&Choices::TACKLE).unwrap(),
6692 SideReference::SideOne,
6693 StateInstructions::default(),
6694 &mut instructions,
6695 false,
6696 );
6697
6698 let expected_instructions: StateInstructions = StateInstructions {
6699 percentage: 100.0,
6700 instruction_list: vec![
6701 Instruction::Damage(DamageInstruction {
6702 side_ref: SideReference::SideTwo,
6703 damage_amount: 1,
6704 }),
6705 Instruction::Boost(BoostInstruction {
6706 side_ref: SideReference::SideOne,
6707 stat: PokemonBoostableStat::Defense,
6708 amount: 1,
6709 }),
6710 ],
6711 };
6712 assert_eq!(instructions, vec![expected_instructions])
6713 }
6714
6715 #[test]
6716 fn test_beastboost_does_not_overboost() {
6717 let mut state: State = State::default();
6718 let mut choice = MOVES.get(&Choices::TACKLE).unwrap().to_owned();
6719 state.side_one.get_active().ability = Abilities::BEASTBOOST;
6720 state.side_one.get_active().attack = 500; state.side_one.attack_boost = 6; state.side_two.get_active().hp = 1;
6723
6724 let mut instructions = vec![];
6725 generate_instructions_from_move(
6726 &mut state,
6727 &mut choice,
6728 &MOVES.get(&Choices::TACKLE).unwrap(),
6729 SideReference::SideOne,
6730 StateInstructions::default(),
6731 &mut instructions,
6732 false,
6733 );
6734
6735 let expected_instructions: StateInstructions = StateInstructions {
6736 percentage: 100.0,
6737 instruction_list: vec![Instruction::Damage(DamageInstruction {
6738 side_ref: SideReference::SideTwo,
6739 damage_amount: 1,
6740 })],
6741 };
6742
6743 assert_eq!(instructions, vec![expected_instructions])
6744 }
6745
6746 #[test]
6747 fn test_beastboost_does_not_boost_without_kill() {
6748 let mut state: State = State::default();
6749 let mut choice = MOVES.get(&Choices::TACKLE).unwrap().to_owned();
6750 state.side_one.get_active().ability = Abilities::BEASTBOOST;
6751 state.side_one.get_active().attack = 150; state.side_two.get_active().hp = 100;
6753
6754 let mut instructions = vec![];
6755 generate_instructions_from_move(
6756 &mut state,
6757 &mut choice,
6758 &MOVES.get(&Choices::TACKLE).unwrap(),
6759 SideReference::SideOne,
6760 StateInstructions::default(),
6761 &mut instructions,
6762 false,
6763 );
6764
6765 let expected_instructions: StateInstructions = StateInstructions {
6766 percentage: 100.0,
6767 instruction_list: vec![Instruction::Damage(DamageInstruction {
6768 side_ref: SideReference::SideTwo,
6769 damage_amount: 72,
6770 })],
6771 };
6772
6773 assert_eq!(instructions, vec![expected_instructions])
6774 }
6775
6776 #[test]
6777 fn test_drain_move_heals() {
6778 let mut state: State = State::default();
6779 let mut choice = MOVES.get(&Choices::ABSORB).unwrap().to_owned();
6780 state.side_one.get_active().hp = 100;
6781 state.side_one.get_active().maxhp = 200;
6782
6783 let mut instructions = vec![];
6784 generate_instructions_from_move(
6785 &mut state,
6786 &mut choice,
6787 &MOVES.get(&Choices::TACKLE).unwrap(),
6788 SideReference::SideOne,
6789 StateInstructions::default(),
6790 &mut instructions,
6791 false,
6792 );
6793
6794 let expected_instructions: StateInstructions = StateInstructions {
6795 percentage: 100.0,
6796 instruction_list: vec![
6797 Instruction::Damage(DamageInstruction {
6798 side_ref: SideReference::SideTwo,
6799 damage_amount: 16,
6800 }),
6801 Instruction::Heal(HealInstruction {
6802 side_ref: SideReference::SideOne,
6803 heal_amount: 8,
6804 }),
6805 ],
6806 };
6807
6808 assert_eq!(instructions, vec![expected_instructions])
6809 }
6810
6811 #[test]
6812 fn test_drain_move_does_not_overheal() {
6813 let mut state: State = State::default();
6814 let mut choice = MOVES.get(&Choices::ABSORB).unwrap().to_owned();
6815 state.side_one.get_active().hp = 100;
6816 state.side_one.get_active().maxhp = 105;
6817
6818 let mut instructions = vec![];
6819 generate_instructions_from_move(
6820 &mut state,
6821 &mut choice,
6822 &MOVES.get(&Choices::TACKLE).unwrap(),
6823 SideReference::SideOne,
6824 StateInstructions::default(),
6825 &mut instructions,
6826 false,
6827 );
6828
6829 let expected_instructions: StateInstructions = StateInstructions {
6830 percentage: 100.0,
6831 instruction_list: vec![
6832 Instruction::Damage(DamageInstruction {
6833 side_ref: SideReference::SideTwo,
6834 damage_amount: 16,
6835 }),
6836 Instruction::Heal(HealInstruction {
6837 side_ref: SideReference::SideOne,
6838 heal_amount: 5,
6839 }),
6840 ],
6841 };
6842
6843 assert_eq!(instructions, vec![expected_instructions])
6844 }
6845
6846 #[test]
6847 fn test_recoil_damage() {
6848 let mut state: State = State::default();
6849 let mut choice = MOVES.get(&Choices::BRAVEBIRD).unwrap().to_owned();
6850 state.side_one.get_active().hp = 105;
6851 state.side_one.get_active().maxhp = 105;
6852
6853 let mut instructions = vec![];
6854 generate_instructions_from_move(
6855 &mut state,
6856 &mut choice,
6857 &MOVES.get(&Choices::TACKLE).unwrap(),
6858 SideReference::SideOne,
6859 StateInstructions::default(),
6860 &mut instructions,
6861 false,
6862 );
6863
6864 let expected_instructions: StateInstructions = StateInstructions {
6865 percentage: 100.0,
6866 instruction_list: vec![
6867 Instruction::Damage(DamageInstruction {
6868 side_ref: SideReference::SideTwo,
6869 damage_amount: 94,
6870 }),
6871 Instruction::Damage(DamageInstruction {
6872 side_ref: SideReference::SideOne,
6873 damage_amount: 31,
6874 }),
6875 ],
6876 };
6877
6878 assert_eq!(instructions, vec![expected_instructions])
6879 }
6880
6881 #[test]
6882 fn test_recoil_cannot_overkill() {
6883 let mut state: State = State::default();
6884 let mut choice = MOVES.get(&Choices::BRAVEBIRD).unwrap().to_owned();
6885 state.side_one.get_active().hp = 5;
6886 state.side_one.get_active().maxhp = 105;
6887
6888 let mut instructions = vec![];
6889 generate_instructions_from_move(
6890 &mut state,
6891 &mut choice,
6892 &MOVES.get(&Choices::TACKLE).unwrap(),
6893 SideReference::SideOne,
6894 StateInstructions::default(),
6895 &mut instructions,
6896 false,
6897 );
6898
6899 let expected_instructions: StateInstructions = StateInstructions {
6900 percentage: 100.0,
6901 instruction_list: vec![
6902 Instruction::Damage(DamageInstruction {
6903 side_ref: SideReference::SideTwo,
6904 damage_amount: 94,
6905 }),
6906 Instruction::Damage(DamageInstruction {
6907 side_ref: SideReference::SideOne,
6908 damage_amount: 5,
6909 }),
6910 ],
6911 };
6912
6913 assert_eq!(instructions, vec![expected_instructions])
6914 }
6915
6916 #[test]
6917 fn test_drain_and_recoil_together() {
6918 let mut state: State = State::default();
6919 let mut choice = MOVES.get(&Choices::ABSORB).unwrap().to_owned();
6920 choice.recoil = Some(0.33);
6921 state.side_one.get_active().hp = 1;
6922 state.side_one.get_active().maxhp = 105;
6923
6924 let mut instructions = vec![];
6925 generate_instructions_from_move(
6926 &mut state,
6927 &mut choice,
6928 &MOVES.get(&Choices::TACKLE).unwrap(),
6929 SideReference::SideOne,
6930 StateInstructions::default(),
6931 &mut instructions,
6932 false,
6933 );
6934
6935 let expected_instructions: StateInstructions = StateInstructions {
6936 percentage: 100.0,
6937 instruction_list: vec![
6938 Instruction::Damage(DamageInstruction {
6939 side_ref: SideReference::SideTwo,
6940 damage_amount: 16,
6941 }),
6942 Instruction::Heal(HealInstruction {
6943 side_ref: SideReference::SideOne,
6944 heal_amount: 8,
6945 }),
6946 Instruction::Damage(DamageInstruction {
6947 side_ref: SideReference::SideOne,
6948 damage_amount: 5,
6949 }),
6950 ],
6951 };
6952
6953 assert_eq!(instructions, vec![expected_instructions])
6954 }
6955
6956 #[test]
6957 fn test_crash_move_missing() {
6958 let mut state: State = State::default();
6959 let mut choice = MOVES.get(&Choices::JUMPKICK).unwrap().to_owned();
6960
6961 let mut instructions = vec![];
6962 generate_instructions_from_move(
6963 &mut state,
6964 &mut choice,
6965 &MOVES.get(&Choices::TACKLE).unwrap(),
6966 SideReference::SideOne,
6967 StateInstructions::default(),
6968 &mut instructions,
6969 false,
6970 );
6971
6972 let expected_instructions: Vec<StateInstructions> = vec![
6973 StateInstructions {
6974 percentage: 5.000001,
6975 instruction_list: vec![Instruction::Damage(DamageInstruction {
6976 side_ref: SideReference::SideOne,
6977 damage_amount: 50,
6978 })],
6979 },
6980 StateInstructions {
6981 percentage: 95.0,
6982 instruction_list: vec![Instruction::Damage(DamageInstruction {
6983 side_ref: SideReference::SideTwo,
6984 damage_amount: 100,
6985 })],
6986 },
6987 ];
6988
6989 assert_eq!(instructions, expected_instructions)
6990 }
6991
6992 #[test]
6993 fn test_crash_move_missing_versus_ghost_type() {
6994 let mut state: State = State::default();
6995 state.side_two.get_active().types.0 = PokemonType::GHOST;
6996 let mut choice = MOVES.get(&Choices::JUMPKICK).unwrap().to_owned();
6997
6998 let mut instructions = vec![];
6999 generate_instructions_from_move(
7000 &mut state,
7001 &mut choice,
7002 &MOVES.get(&Choices::TACKLE).unwrap(),
7003 SideReference::SideOne,
7004 StateInstructions::default(),
7005 &mut instructions,
7006 false,
7007 );
7008
7009 let expected_instructions: Vec<StateInstructions> = vec![StateInstructions {
7010 percentage: 100.0,
7011 instruction_list: vec![Instruction::Damage(DamageInstruction {
7012 side_ref: SideReference::SideOne,
7013 damage_amount: 50,
7014 })],
7015 }];
7016
7017 assert_eq!(instructions, expected_instructions)
7018 }
7019
7020 #[test]
7021 fn test_crash_move_missing_cannot_overkill() {
7022 let mut state: State = State::default();
7023 state.get_side(&SideReference::SideOne).get_active().hp = 5;
7024 let mut choice = MOVES.get(&Choices::JUMPKICK).unwrap().to_owned();
7025
7026 let mut instructions = vec![];
7027 generate_instructions_from_move(
7028 &mut state,
7029 &mut choice,
7030 &MOVES.get(&Choices::TACKLE).unwrap(),
7031 SideReference::SideOne,
7032 StateInstructions::default(),
7033 &mut instructions,
7034 false,
7035 );
7036
7037 let expected_instructions: Vec<StateInstructions> = vec![
7038 StateInstructions {
7039 percentage: 5.000001,
7040 instruction_list: vec![Instruction::Damage(DamageInstruction {
7041 side_ref: SideReference::SideOne,
7042 damage_amount: 5,
7043 })],
7044 },
7045 StateInstructions {
7046 percentage: 95.0,
7047 instruction_list: vec![Instruction::Damage(DamageInstruction {
7048 side_ref: SideReference::SideTwo,
7049 damage_amount: 100,
7050 })],
7051 },
7052 ];
7053
7054 assert_eq!(instructions, expected_instructions)
7055 }
7056
7057 #[test]
7058 #[cfg(feature = "gen9")]
7059 fn test_knockoff_removing_item() {
7060 let mut state: State = State::default();
7061 let mut choice = MOVES.get(&Choices::KNOCKOFF).unwrap().to_owned();
7062 state.get_side(&SideReference::SideTwo).get_active().item = Items::HEAVYDUTYBOOTS;
7063
7064 let mut instructions = vec![];
7065 generate_instructions_from_move(
7066 &mut state,
7067 &mut choice,
7068 &MOVES.get(&Choices::TACKLE).unwrap(),
7069 SideReference::SideOne,
7070 StateInstructions::default(),
7071 &mut instructions,
7072 false,
7073 );
7074
7075 let expected_instructions: StateInstructions = StateInstructions {
7076 percentage: 100.0,
7077 instruction_list: vec![
7078 Instruction::Damage(DamageInstruction {
7079 side_ref: SideReference::SideTwo,
7080 damage_amount: 76,
7081 }),
7082 Instruction::ChangeItem(ChangeItemInstruction {
7083 side_ref: SideReference::SideTwo,
7084 current_item: Items::HEAVYDUTYBOOTS,
7085 new_item: Items::NONE,
7086 }),
7087 ],
7088 };
7089
7090 assert_eq!(instructions, vec![expected_instructions])
7091 }
7092
7093 #[test]
7094 fn test_blunderpolicy_boost() {
7095 let mut state: State = State::default();
7096 let mut choice = MOVES.get(&Choices::CROSSCHOP).unwrap().to_owned();
7097 state.get_side(&SideReference::SideOne).get_active().item = Items::BLUNDERPOLICY;
7098
7099 let mut instructions = vec![];
7100 generate_instructions_from_move(
7101 &mut state,
7102 &mut choice,
7103 &MOVES.get(&Choices::TACKLE).unwrap(),
7104 SideReference::SideOne,
7105 StateInstructions::default(),
7106 &mut instructions,
7107 false,
7108 );
7109
7110 let expected_instructions: Vec<StateInstructions> = vec![
7111 StateInstructions {
7112 percentage: 19.999998,
7113 instruction_list: vec![
7114 Instruction::Boost(BoostInstruction {
7115 side_ref: SideReference::SideOne,
7116 stat: PokemonBoostableStat::Speed,
7117 amount: 2,
7118 }),
7119 Instruction::ChangeItem(ChangeItemInstruction {
7120 side_ref: SideReference::SideOne,
7121 current_item: Items::BLUNDERPOLICY,
7122 new_item: Items::NONE,
7123 }),
7124 ],
7125 },
7126 StateInstructions {
7127 percentage: 80.0,
7128 instruction_list: vec![Instruction::Damage(DamageInstruction {
7129 side_ref: SideReference::SideTwo,
7130 damage_amount: 100,
7131 })],
7132 },
7133 ];
7134
7135 assert_eq!(instructions, expected_instructions);
7136 }
7137
7138 #[test]
7139 fn test_basic_switch_functionality_with_no_prior_instructions() {
7140 let mut state: State = State::default();
7141 let mut choice = Choice {
7142 ..Default::default()
7143 };
7144
7145 choice.switch_id = PokemonIndex::P1;
7146
7147 let expected_instructions: StateInstructions = StateInstructions {
7148 percentage: 100.0,
7149 instruction_list: vec![Instruction::Switch(SwitchInstruction {
7150 side_ref: SideReference::SideOne,
7151 previous_index: PokemonIndex::P0,
7152 next_index: PokemonIndex::P1,
7153 })],
7154 ..Default::default()
7155 };
7156
7157 let mut incoming_instructions = StateInstructions::default();
7158 generate_instructions_from_switch(
7159 &mut state,
7160 choice.switch_id,
7161 SideReference::SideOne,
7162 &mut incoming_instructions,
7163 );
7164
7165 assert_eq!(expected_instructions, incoming_instructions);
7166 }
7167
7168 #[test]
7169 fn test_basic_switch_with_volatile_statuses() {
7170 let mut state: State = State::default();
7171 state
7172 .side_one
7173 .volatile_statuses
7174 .insert(PokemonVolatileStatus::LEECHSEED);
7175 let mut choice = Choice {
7176 ..Default::default()
7177 };
7178 choice.switch_id = PokemonIndex::P1;
7179
7180 let expected_instructions: StateInstructions = StateInstructions {
7181 percentage: 100.0,
7182 instruction_list: vec![
7183 Instruction::RemoveVolatileStatus(RemoveVolatileStatusInstruction {
7184 side_ref: SideReference::SideOne,
7185 volatile_status: PokemonVolatileStatus::LEECHSEED,
7186 }),
7187 Instruction::Switch(SwitchInstruction {
7188 side_ref: SideReference::SideOne,
7189 previous_index: PokemonIndex::P0,
7190 next_index: PokemonIndex::P1,
7191 }),
7192 ],
7193 ..Default::default()
7194 };
7195
7196 let mut incoming_instructions = StateInstructions::default();
7197 generate_instructions_from_switch(
7198 &mut state,
7199 choice.switch_id,
7200 SideReference::SideOne,
7201 &mut incoming_instructions,
7202 );
7203
7204 assert_eq!(expected_instructions, incoming_instructions);
7205 }
7206
7207 #[test]
7208 fn test_basic_switch_with_toxic_count() {
7209 let mut state: State = State::default();
7210 state.side_one.side_conditions.toxic_count = 2;
7211 let mut choice = Choice {
7212 ..Default::default()
7213 };
7214 choice.switch_id = PokemonIndex::P1;
7215
7216 let expected_instructions: StateInstructions = StateInstructions {
7217 percentage: 100.0,
7218 instruction_list: vec![
7219 Instruction::ChangeSideCondition(ChangeSideConditionInstruction {
7220 side_ref: SideReference::SideOne,
7221 side_condition: PokemonSideCondition::ToxicCount,
7222 amount: -2,
7223 }),
7224 Instruction::Switch(SwitchInstruction {
7225 side_ref: SideReference::SideOne,
7226 previous_index: PokemonIndex::P0,
7227 next_index: PokemonIndex::P1,
7228 }),
7229 ],
7230 ..Default::default()
7231 };
7232
7233 let mut incoming_instructions = StateInstructions::default();
7234 generate_instructions_from_switch(
7235 &mut state,
7236 choice.switch_id,
7237 SideReference::SideOne,
7238 &mut incoming_instructions,
7239 );
7240
7241 assert_eq!(expected_instructions, incoming_instructions);
7242 }
7243
7244 #[test]
7245 fn test_basic_switch_with_boost() {
7246 let mut state: State = State::default();
7247 state.side_one.attack_boost = 2;
7248 state.side_one.speed_boost = 5;
7249 let mut choice = Choice {
7250 ..Default::default()
7251 };
7252 choice.switch_id = PokemonIndex::P1;
7253
7254 let expected_instructions: StateInstructions = StateInstructions {
7255 percentage: 100.0,
7256 instruction_list: vec![
7257 Instruction::Boost(BoostInstruction {
7258 side_ref: SideReference::SideOne,
7259 stat: PokemonBoostableStat::Attack,
7260 amount: -2,
7261 }),
7262 Instruction::Boost(BoostInstruction {
7263 side_ref: SideReference::SideOne,
7264 stat: PokemonBoostableStat::Speed,
7265 amount: -5,
7266 }),
7267 Instruction::Switch(SwitchInstruction {
7268 side_ref: SideReference::SideOne,
7269 previous_index: PokemonIndex::P0,
7270 next_index: PokemonIndex::P1,
7271 }),
7272 ],
7273 ..Default::default()
7274 };
7275
7276 let mut incoming_instructions = StateInstructions::default();
7277 generate_instructions_from_switch(
7278 &mut state,
7279 choice.switch_id,
7280 SideReference::SideOne,
7281 &mut incoming_instructions,
7282 );
7283
7284 assert_eq!(expected_instructions, incoming_instructions);
7285 }
7286
7287 #[test]
7288 fn test_basic_switch_with_disabled_move() {
7289 let mut state: State = State::default();
7290 state.side_one.get_active().moves.m0 = Move {
7291 id: Choices::NONE,
7292 disabled: true,
7293 pp: 32,
7294 ..Default::default()
7295 };
7296 state.side_one.get_active().moves.m1 = Move {
7297 id: Choices::NONE,
7298 disabled: false,
7299 pp: 32,
7300 ..Default::default()
7301 };
7302
7303 let mut choice = Choice {
7304 ..Default::default()
7305 };
7306 choice.switch_id = PokemonIndex::P1;
7307
7308 let expected_instructions: StateInstructions = StateInstructions {
7309 percentage: 100.0,
7310 instruction_list: vec![
7311 Instruction::EnableMove(EnableMoveInstruction {
7312 side_ref: SideReference::SideOne,
7313 move_index: PokemonMoveIndex::M0,
7314 }),
7315 Instruction::Switch(SwitchInstruction {
7316 side_ref: SideReference::SideOne,
7317 previous_index: PokemonIndex::P0,
7318 next_index: PokemonIndex::P1,
7319 }),
7320 ],
7321 ..Default::default()
7322 };
7323
7324 let mut incoming_instructions = StateInstructions::default();
7325 generate_instructions_from_switch(
7326 &mut state,
7327 choice.switch_id,
7328 SideReference::SideOne,
7329 &mut incoming_instructions,
7330 );
7331
7332 assert_eq!(expected_instructions, incoming_instructions);
7333 }
7334
7335 #[test]
7336 fn test_basic_switch_with_multiple_disabled_moves() {
7337 let mut state: State = State::default();
7338 state.side_one.get_active().moves.m0 = Move {
7339 id: Choices::NONE,
7340 disabled: true,
7341 pp: 32,
7342 ..Default::default()
7343 };
7344 state.side_one.get_active().moves.m1 = Move {
7345 id: Choices::NONE,
7346 disabled: true,
7347 pp: 32,
7348 ..Default::default()
7349 };
7350 state.side_one.get_active().moves.m2 = Move {
7351 id: Choices::NONE,
7352 disabled: false,
7353 pp: 32,
7354 ..Default::default()
7355 };
7356 state.side_one.get_active().moves.m3 = Move {
7357 id: Choices::NONE,
7358 disabled: true,
7359 pp: 32,
7360 ..Default::default()
7361 };
7362 let mut choice = Choice {
7363 ..Default::default()
7364 };
7365 choice.switch_id = PokemonIndex::P1;
7366
7367 let expected_instructions: StateInstructions = StateInstructions {
7368 percentage: 100.0,
7369 instruction_list: vec![
7370 Instruction::EnableMove(EnableMoveInstruction {
7371 side_ref: SideReference::SideOne,
7372 move_index: PokemonMoveIndex::M0,
7373 }),
7374 Instruction::EnableMove(EnableMoveInstruction {
7375 side_ref: SideReference::SideOne,
7376 move_index: PokemonMoveIndex::M1,
7377 }),
7378 Instruction::EnableMove(EnableMoveInstruction {
7379 side_ref: SideReference::SideOne,
7380 move_index: PokemonMoveIndex::M3,
7381 }),
7382 Instruction::Switch(SwitchInstruction {
7383 side_ref: SideReference::SideOne,
7384 previous_index: PokemonIndex::P0,
7385 next_index: PokemonIndex::P1,
7386 }),
7387 ],
7388 ..Default::default()
7389 };
7390
7391 let mut incoming_instructions = StateInstructions::default();
7392 generate_instructions_from_switch(
7393 &mut state,
7394 choice.switch_id,
7395 SideReference::SideOne,
7396 &mut incoming_instructions,
7397 );
7398
7399 assert_eq!(expected_instructions, incoming_instructions);
7400 }
7401
7402 #[test]
7403 fn test_basic_switch_functionality_with_a_prior_instruction() {
7404 let mut state: State = State::default();
7405 let mut incoming_instructions = StateInstructions::default();
7406 let mut choice = Choice {
7407 ..Default::default()
7408 };
7409
7410 choice.switch_id = PokemonIndex::P1;
7411 incoming_instructions
7412 .instruction_list
7413 .push(Instruction::Damage(DamageInstruction {
7414 side_ref: SideReference::SideOne,
7415 damage_amount: 1,
7416 }));
7417
7418 let expected_instructions: StateInstructions = StateInstructions {
7419 percentage: 100.0,
7420 instruction_list: vec![
7421 Instruction::Damage(DamageInstruction {
7422 side_ref: SideReference::SideOne,
7423 damage_amount: 1,
7424 }),
7425 Instruction::Switch(SwitchInstruction {
7426 side_ref: SideReference::SideOne,
7427 previous_index: PokemonIndex::P0,
7428 next_index: PokemonIndex::P1,
7429 }),
7430 ],
7431 ..Default::default()
7432 };
7433
7434 generate_instructions_from_switch(
7435 &mut state,
7436 choice.switch_id,
7437 SideReference::SideOne,
7438 &mut incoming_instructions,
7439 );
7440
7441 assert_eq!(expected_instructions, incoming_instructions);
7442 }
7443
7444 #[test]
7445 fn test_switch_with_regenerator() {
7446 let mut state: State = State::default();
7447 state.side_one.get_active().hp -= 10;
7448 state.side_one.get_active().ability = Abilities::REGENERATOR;
7449 state.side_one.get_active().base_ability = Abilities::REGENERATOR;
7450 let mut choice = Choice {
7451 ..Default::default()
7452 };
7453 choice.switch_id = PokemonIndex::P1;
7454
7455 let expected_instructions: StateInstructions = StateInstructions {
7456 percentage: 100.0,
7457 instruction_list: vec![
7458 Instruction::Heal(HealInstruction {
7459 side_ref: SideReference::SideOne,
7460 heal_amount: 10,
7461 }),
7462 Instruction::Switch(SwitchInstruction {
7463 side_ref: SideReference::SideOne,
7464 previous_index: PokemonIndex::P0,
7465 next_index: PokemonIndex::P1,
7466 }),
7467 ],
7468 ..Default::default()
7469 };
7470
7471 let mut incoming_instructions = StateInstructions::default();
7472 generate_instructions_from_switch(
7473 &mut state,
7474 choice.switch_id,
7475 SideReference::SideOne,
7476 &mut incoming_instructions,
7477 );
7478
7479 assert_eq!(expected_instructions, incoming_instructions);
7480 }
7481
7482 #[test]
7483 fn test_switch_with_regenerator_plus_move_enabling() {
7484 let mut state: State = State::default();
7485 state.side_one.get_active().moves.m0 = Move {
7486 id: Choices::NONE,
7487 disabled: true,
7488 pp: 32,
7489 ..Default::default()
7490 };
7491 state.side_one.get_active().moves.m1 = Move {
7492 id: Choices::NONE,
7493 disabled: true,
7494 pp: 32,
7495 ..Default::default()
7496 };
7497 state.side_one.get_active().moves.m2 = Move {
7498 id: Choices::NONE,
7499 disabled: false,
7500 pp: 32,
7501 ..Default::default()
7502 };
7503 state.side_one.get_active().moves.m3 = Move {
7504 id: Choices::NONE,
7505 disabled: true,
7506 pp: 32,
7507 ..Default::default()
7508 };
7509 state.side_one.get_active().hp -= 10;
7510 state.side_one.get_active().ability = Abilities::REGENERATOR;
7511 state.side_one.get_active().base_ability = Abilities::REGENERATOR;
7512 let mut choice = Choice {
7513 ..Default::default()
7514 };
7515 choice.switch_id = PokemonIndex::P1;
7516
7517 let expected_instructions: StateInstructions = StateInstructions {
7518 percentage: 100.0,
7519 instruction_list: vec![
7520 Instruction::EnableMove(EnableMoveInstruction {
7521 side_ref: SideReference::SideOne,
7522 move_index: PokemonMoveIndex::M0,
7523 }),
7524 Instruction::EnableMove(EnableMoveInstruction {
7525 side_ref: SideReference::SideOne,
7526 move_index: PokemonMoveIndex::M1,
7527 }),
7528 Instruction::EnableMove(EnableMoveInstruction {
7529 side_ref: SideReference::SideOne,
7530 move_index: PokemonMoveIndex::M3,
7531 }),
7532 Instruction::Heal(HealInstruction {
7533 side_ref: SideReference::SideOne,
7534 heal_amount: 10,
7535 }),
7536 Instruction::Switch(SwitchInstruction {
7537 side_ref: SideReference::SideOne,
7538 previous_index: PokemonIndex::P0,
7539 next_index: PokemonIndex::P1,
7540 }),
7541 ],
7542 ..Default::default()
7543 };
7544
7545 let mut incoming_instructions = StateInstructions::default();
7546 generate_instructions_from_switch(
7547 &mut state,
7548 choice.switch_id,
7549 SideReference::SideOne,
7550 &mut incoming_instructions,
7551 );
7552
7553 assert_eq!(expected_instructions, incoming_instructions);
7554 }
7555
7556 #[test]
7557 fn test_switch_with_regenerator_but_no_damage_taken() {
7558 let mut state: State = State::default();
7559 state.side_one.get_active().ability = Abilities::REGENERATOR;
7560 state.side_one.get_active().base_ability = Abilities::REGENERATOR;
7561 let mut choice = Choice {
7562 ..Default::default()
7563 };
7564 choice.switch_id = PokemonIndex::P1;
7565
7566 let expected_instructions: StateInstructions = StateInstructions {
7567 percentage: 100.0,
7568 instruction_list: vec![Instruction::Switch(SwitchInstruction {
7569 side_ref: SideReference::SideOne,
7570 previous_index: PokemonIndex::P0,
7571 next_index: PokemonIndex::P1,
7572 })],
7573 ..Default::default()
7574 };
7575
7576 let mut incoming_instructions = StateInstructions::default();
7577 generate_instructions_from_switch(
7578 &mut state,
7579 choice.switch_id,
7580 SideReference::SideOne,
7581 &mut incoming_instructions,
7582 );
7583
7584 assert_eq!(expected_instructions, incoming_instructions);
7585 }
7586
7587 #[test]
7588 fn test_fainted_pokemon_with_regenerator_does_not_heal() {
7589 let mut state: State = State::default();
7590 state.side_one.get_active().ability = Abilities::REGENERATOR;
7591 state.side_one.get_active().base_ability = Abilities::REGENERATOR;
7592 state.side_one.get_active().hp = 0;
7593 let mut choice = Choice {
7594 ..Default::default()
7595 };
7596 choice.switch_id = PokemonIndex::P1;
7597
7598 let expected_instructions: StateInstructions = StateInstructions {
7599 percentage: 100.0,
7600 instruction_list: vec![Instruction::Switch(SwitchInstruction {
7601 side_ref: SideReference::SideOne,
7602 previous_index: PokemonIndex::P0,
7603 next_index: PokemonIndex::P1,
7604 })],
7605 ..Default::default()
7606 };
7607
7608 let mut incoming_instructions = StateInstructions::default();
7609 generate_instructions_from_switch(
7610 &mut state,
7611 choice.switch_id,
7612 SideReference::SideOne,
7613 &mut incoming_instructions,
7614 );
7615
7616 assert_eq!(expected_instructions, incoming_instructions);
7617 }
7618
7619 #[test]
7620 fn test_regenerator_only_heals_one_third() {
7621 let mut state: State = State::default();
7622 state.side_one.get_active().ability = Abilities::REGENERATOR;
7623 state.side_one.get_active().base_ability = Abilities::REGENERATOR;
7624 state.side_one.get_active().hp = 3;
7625 let mut choice = Choice {
7626 ..Default::default()
7627 };
7628 choice.switch_id = PokemonIndex::P1;
7629
7630 let expected_instructions: StateInstructions = StateInstructions {
7631 percentage: 100.0,
7632 instruction_list: vec![
7633 Instruction::Heal(HealInstruction {
7634 side_ref: SideReference::SideOne,
7635 heal_amount: 33,
7636 }),
7637 Instruction::Switch(SwitchInstruction {
7638 side_ref: SideReference::SideOne,
7639 previous_index: PokemonIndex::P0,
7640 next_index: PokemonIndex::P1,
7641 }),
7642 ],
7643 ..Default::default()
7644 };
7645
7646 let mut incoming_instructions = StateInstructions::default();
7647 generate_instructions_from_switch(
7648 &mut state,
7649 choice.switch_id,
7650 SideReference::SideOne,
7651 &mut incoming_instructions,
7652 );
7653
7654 assert_eq!(expected_instructions, incoming_instructions);
7655 }
7656
7657 #[test]
7658 fn test_naturalcure() {
7659 let mut state: State = State::default();
7660 state.side_one.get_active().ability = Abilities::NATURALCURE;
7661 state.side_one.get_active().base_ability = Abilities::NATURALCURE;
7662 state.side_one.get_active().status = PokemonStatus::PARALYZE;
7663 let mut choice = Choice {
7664 ..Default::default()
7665 };
7666 choice.switch_id = PokemonIndex::P1;
7667
7668 let expected_instructions: StateInstructions = StateInstructions {
7669 percentage: 100.0,
7670 instruction_list: vec![
7671 Instruction::ChangeStatus(ChangeStatusInstruction {
7672 side_ref: SideReference::SideOne,
7673 pokemon_index: PokemonIndex::P0,
7674 old_status: PokemonStatus::PARALYZE,
7675 new_status: PokemonStatus::NONE,
7676 }),
7677 Instruction::Switch(SwitchInstruction {
7678 side_ref: SideReference::SideOne,
7679 previous_index: PokemonIndex::P0,
7680 next_index: PokemonIndex::P1,
7681 }),
7682 ],
7683 ..Default::default()
7684 };
7685
7686 let mut incoming_instructions = StateInstructions::default();
7687 generate_instructions_from_switch(
7688 &mut state,
7689 choice.switch_id,
7690 SideReference::SideOne,
7691 &mut incoming_instructions,
7692 );
7693
7694 assert_eq!(expected_instructions, incoming_instructions);
7695 }
7696
7697 #[test]
7698 fn test_naturalcure_with_no_status() {
7699 let mut state: State = State::default();
7700 state.side_one.get_active().ability = Abilities::NATURALCURE;
7701 state.side_one.get_active().base_ability = Abilities::NATURALCURE;
7702 state.side_one.get_active().status = PokemonStatus::NONE;
7703 let mut choice = Choice {
7704 ..Default::default()
7705 };
7706 choice.switch_id = PokemonIndex::P1;
7707
7708 let expected_instructions: StateInstructions = StateInstructions {
7709 percentage: 100.0,
7710 instruction_list: vec![Instruction::Switch(SwitchInstruction {
7711 side_ref: SideReference::SideOne,
7712 previous_index: PokemonIndex::P0,
7713 next_index: PokemonIndex::P1,
7714 })],
7715 ..Default::default()
7716 };
7717
7718 let mut incoming_instructions = StateInstructions::default();
7719 generate_instructions_from_switch(
7720 &mut state,
7721 choice.switch_id,
7722 SideReference::SideOne,
7723 &mut incoming_instructions,
7724 );
7725
7726 assert_eq!(expected_instructions, incoming_instructions);
7727 }
7728
7729 #[test]
7730 fn test_switching_into_stealthrock() {
7731 let mut state: State = State::default();
7732 state.side_one.side_conditions.stealth_rock = 1;
7733 let mut choice = Choice {
7734 ..Default::default()
7735 };
7736 choice.switch_id = PokemonIndex::P1;
7737
7738 let expected_instructions: StateInstructions = StateInstructions {
7739 percentage: 100.0,
7740 instruction_list: vec![
7741 Instruction::Switch(SwitchInstruction {
7742 side_ref: SideReference::SideOne,
7743 previous_index: PokemonIndex::P0,
7744 next_index: PokemonIndex::P1,
7745 }),
7746 Instruction::Damage(DamageInstruction {
7747 side_ref: SideReference::SideOne,
7748 damage_amount: state.side_one.get_active().hp / 8,
7749 }),
7750 ],
7751 ..Default::default()
7752 };
7753
7754 let mut incoming_instructions = StateInstructions::default();
7755 generate_instructions_from_switch(
7756 &mut state,
7757 choice.switch_id,
7758 SideReference::SideOne,
7759 &mut incoming_instructions,
7760 );
7761
7762 assert_eq!(expected_instructions, incoming_instructions);
7763 }
7764
7765 #[test]
7766 fn test_switching_into_resisted_stealthrock() {
7767 let mut state: State = State::default();
7768 state.side_one.side_conditions.stealth_rock = 1;
7769 state.side_one.pokemon[PokemonIndex::P1].types = (PokemonType::GROUND, PokemonType::NORMAL);
7770 let mut choice = Choice {
7771 ..Default::default()
7772 };
7773 choice.switch_id = PokemonIndex::P1;
7774
7775 let expected_instructions: StateInstructions = StateInstructions {
7776 percentage: 100.0,
7777 instruction_list: vec![
7778 Instruction::Switch(SwitchInstruction {
7779 side_ref: SideReference::SideOne,
7780 previous_index: PokemonIndex::P0,
7781 next_index: PokemonIndex::P1,
7782 }),
7783 Instruction::Damage(DamageInstruction {
7784 side_ref: SideReference::SideOne,
7785 damage_amount: state.side_one.get_active().hp / 16,
7786 }),
7787 ],
7788 ..Default::default()
7789 };
7790
7791 let mut incoming_instructions = StateInstructions::default();
7792 generate_instructions_from_switch(
7793 &mut state,
7794 choice.switch_id,
7795 SideReference::SideOne,
7796 &mut incoming_instructions,
7797 );
7798
7799 assert_eq!(expected_instructions, incoming_instructions);
7800 }
7801
7802 #[test]
7803 fn test_switching_into_stealthrock_does_not_overkill() {
7804 let mut state: State = State::default();
7805 state.side_one.side_conditions.stealth_rock = 1;
7806 state.side_one.pokemon[PokemonIndex::P1].hp = 5;
7807 let mut choice = Choice {
7808 ..Default::default()
7809 };
7810 choice.switch_id = PokemonIndex::P1;
7811
7812 let expected_instructions: StateInstructions = StateInstructions {
7813 percentage: 100.0,
7814 instruction_list: vec![
7815 Instruction::Switch(SwitchInstruction {
7816 side_ref: SideReference::SideOne,
7817 previous_index: PokemonIndex::P0,
7818 next_index: PokemonIndex::P1,
7819 }),
7820 Instruction::Damage(DamageInstruction {
7821 side_ref: SideReference::SideOne,
7822 damage_amount: 5,
7823 }),
7824 ],
7825 ..Default::default()
7826 };
7827
7828 let mut incoming_instructions = StateInstructions::default();
7829 generate_instructions_from_switch(
7830 &mut state,
7831 choice.switch_id,
7832 SideReference::SideOne,
7833 &mut incoming_instructions,
7834 );
7835
7836 assert_eq!(expected_instructions, incoming_instructions);
7837 }
7838
7839 #[test]
7840 fn test_switching_into_stickyweb() {
7841 let mut state: State = State::default();
7842 state.side_one.side_conditions.sticky_web = 1;
7843 let mut choice = Choice {
7844 ..Default::default()
7845 };
7846 choice.switch_id = PokemonIndex::P1;
7847
7848 let expected_instructions: StateInstructions = StateInstructions {
7849 percentage: 100.0,
7850 instruction_list: vec![
7851 Instruction::Switch(SwitchInstruction {
7852 side_ref: SideReference::SideOne,
7853 previous_index: PokemonIndex::P0,
7854 next_index: PokemonIndex::P1,
7855 }),
7856 Instruction::Boost(BoostInstruction {
7857 side_ref: SideReference::SideOne,
7858 stat: PokemonBoostableStat::Speed,
7859 amount: -1,
7860 }),
7861 ],
7862 ..Default::default()
7863 };
7864
7865 let mut incoming_instructions = StateInstructions::default();
7866 generate_instructions_from_switch(
7867 &mut state,
7868 choice.switch_id,
7869 SideReference::SideOne,
7870 &mut incoming_instructions,
7871 );
7872
7873 assert_eq!(expected_instructions, incoming_instructions);
7874 }
7875
7876 #[test]
7877 fn test_switching_into_stickyweb_with_heavydutyboots() {
7878 let mut state: State = State::default();
7879 state.side_one.side_conditions.sticky_web = 1;
7880 state.side_one.pokemon[PokemonIndex::P1].item = Items::HEAVYDUTYBOOTS;
7881 let mut choice = Choice {
7882 ..Default::default()
7883 };
7884 choice.switch_id = PokemonIndex::P1;
7885
7886 let expected_instructions: StateInstructions = StateInstructions {
7887 percentage: 100.0,
7888 instruction_list: vec![Instruction::Switch(SwitchInstruction {
7889 side_ref: SideReference::SideOne,
7890 previous_index: PokemonIndex::P0,
7891 next_index: PokemonIndex::P1,
7892 })],
7893 ..Default::default()
7894 };
7895
7896 let mut incoming_instructions = StateInstructions::default();
7897 generate_instructions_from_switch(
7898 &mut state,
7899 choice.switch_id,
7900 SideReference::SideOne,
7901 &mut incoming_instructions,
7902 );
7903
7904 assert_eq!(expected_instructions, incoming_instructions);
7905 }
7906
7907 #[test]
7908 fn test_switching_into_stickyweb_with_contrary() {
7909 let mut state: State = State::default();
7910 state.side_one.side_conditions.sticky_web = 1;
7911 state.side_one.pokemon[PokemonIndex::P1].ability = Abilities::CONTRARY;
7912 let mut choice = Choice {
7913 ..Default::default()
7914 };
7915 choice.switch_id = PokemonIndex::P1;
7916
7917 let expected_instructions: StateInstructions = StateInstructions {
7918 percentage: 100.0,
7919 instruction_list: vec![
7920 Instruction::Switch(SwitchInstruction {
7921 side_ref: SideReference::SideOne,
7922 previous_index: PokemonIndex::P0,
7923 next_index: PokemonIndex::P1,
7924 }),
7925 Instruction::Boost(BoostInstruction {
7926 side_ref: SideReference::SideOne,
7927 stat: PokemonBoostableStat::Speed,
7928 amount: 1,
7929 }),
7930 ],
7931 ..Default::default()
7932 };
7933
7934 let mut incoming_instructions = StateInstructions::default();
7935 generate_instructions_from_switch(
7936 &mut state,
7937 choice.switch_id,
7938 SideReference::SideOne,
7939 &mut incoming_instructions,
7940 );
7941
7942 assert_eq!(expected_instructions, incoming_instructions);
7943 }
7944
7945 #[test]
7946 fn test_switching_into_single_layer_toxicspikes() {
7947 let mut state: State = State::default();
7948 state.side_one.side_conditions.toxic_spikes = 1;
7949 let mut choice = Choice {
7950 ..Default::default()
7951 };
7952 choice.switch_id = PokemonIndex::P1;
7953
7954 let expected_instructions: StateInstructions = StateInstructions {
7955 percentage: 100.0,
7956 instruction_list: vec![
7957 Instruction::Switch(SwitchInstruction {
7958 side_ref: SideReference::SideOne,
7959 previous_index: PokemonIndex::P0,
7960 next_index: PokemonIndex::P1,
7961 }),
7962 Instruction::ChangeStatus(ChangeStatusInstruction {
7963 side_ref: SideReference::SideOne,
7964 pokemon_index: PokemonIndex::P1,
7965 old_status: PokemonStatus::NONE,
7966 new_status: PokemonStatus::POISON,
7967 }),
7968 ],
7969 ..Default::default()
7970 };
7971
7972 let mut incoming_instructions = StateInstructions::default();
7973 generate_instructions_from_switch(
7974 &mut state,
7975 choice.switch_id,
7976 SideReference::SideOne,
7977 &mut incoming_instructions,
7978 );
7979
7980 assert_eq!(expected_instructions, incoming_instructions);
7981 }
7982
7983 #[test]
7984 fn test_switching_into_double_layer_toxicspikes() {
7985 let mut state: State = State::default();
7986 state.side_one.side_conditions.toxic_spikes = 2;
7987 let mut choice = Choice {
7988 ..Default::default()
7989 };
7990 choice.switch_id = PokemonIndex::P1;
7991
7992 let expected_instructions: StateInstructions = StateInstructions {
7993 percentage: 100.0,
7994 instruction_list: vec![
7995 Instruction::Switch(SwitchInstruction {
7996 side_ref: SideReference::SideOne,
7997 previous_index: PokemonIndex::P0,
7998 next_index: PokemonIndex::P1,
7999 }),
8000 Instruction::ChangeStatus(ChangeStatusInstruction {
8001 side_ref: SideReference::SideOne,
8002 pokemon_index: PokemonIndex::P1,
8003 old_status: PokemonStatus::NONE,
8004 new_status: PokemonStatus::TOXIC,
8005 }),
8006 ],
8007 ..Default::default()
8008 };
8009
8010 let mut incoming_instructions = StateInstructions::default();
8011 generate_instructions_from_switch(
8012 &mut state,
8013 choice.switch_id,
8014 SideReference::SideOne,
8015 &mut incoming_instructions,
8016 );
8017
8018 assert_eq!(expected_instructions, incoming_instructions);
8019 }
8020
8021 #[test]
8022 fn test_switching_into_double_layer_toxicspikes_as_flying_type() {
8023 let mut state: State = State::default();
8024 state.side_one.side_conditions.toxic_spikes = 2;
8025 state.side_one.pokemon[PokemonIndex::P1].types.0 = PokemonType::FLYING;
8026 let mut choice = Choice {
8027 ..Default::default()
8028 };
8029 choice.switch_id = PokemonIndex::P1;
8030
8031 let expected_instructions: StateInstructions = StateInstructions {
8032 percentage: 100.0,
8033 instruction_list: vec![Instruction::Switch(SwitchInstruction {
8034 side_ref: SideReference::SideOne,
8035 previous_index: PokemonIndex::P0,
8036 next_index: PokemonIndex::P1,
8037 })],
8038 ..Default::default()
8039 };
8040
8041 let mut incoming_instructions = StateInstructions::default();
8042 generate_instructions_from_switch(
8043 &mut state,
8044 choice.switch_id,
8045 SideReference::SideOne,
8046 &mut incoming_instructions,
8047 );
8048
8049 assert_eq!(expected_instructions, incoming_instructions);
8050 }
8051
8052 #[test]
8053 fn test_switching_into_double_layer_toxicspikes_as_poison_and_flying_type() {
8054 let mut state: State = State::default();
8055 state.side_one.side_conditions.toxic_spikes = 2;
8056 state.side_one.pokemon[PokemonIndex::P1].types.0 = PokemonType::FLYING;
8057 state.side_one.pokemon[PokemonIndex::P1].types.1 = PokemonType::POISON;
8058 let mut choice = Choice {
8059 ..Default::default()
8060 };
8061 choice.switch_id = PokemonIndex::P1;
8062
8063 let expected_instructions: StateInstructions = StateInstructions {
8064 percentage: 100.0,
8065 instruction_list: vec![Instruction::Switch(SwitchInstruction {
8066 side_ref: SideReference::SideOne,
8067 previous_index: PokemonIndex::P0,
8068 next_index: PokemonIndex::P1,
8069 })],
8070 ..Default::default()
8071 };
8072
8073 let mut incoming_instructions = StateInstructions::default();
8074 generate_instructions_from_switch(
8075 &mut state,
8076 choice.switch_id,
8077 SideReference::SideOne,
8078 &mut incoming_instructions,
8079 );
8080
8081 assert_eq!(expected_instructions, incoming_instructions);
8082 }
8083
8084 #[test]
8085 fn test_switching_in_with_intimidate() {
8086 let mut state: State = State::default();
8087 state.side_one.pokemon[PokemonIndex::P1].ability = Abilities::INTIMIDATE;
8088 let mut choice = Choice {
8089 ..Default::default()
8090 };
8091 choice.switch_id = PokemonIndex::P1;
8092
8093 let expected_instructions: StateInstructions = StateInstructions {
8094 percentage: 100.0,
8095 instruction_list: vec![
8096 Instruction::Switch(SwitchInstruction {
8097 side_ref: SideReference::SideOne,
8098 previous_index: PokemonIndex::P0,
8099 next_index: PokemonIndex::P1,
8100 }),
8101 Instruction::Boost(BoostInstruction {
8102 side_ref: SideReference::SideTwo,
8103 stat: PokemonBoostableStat::Attack,
8104 amount: -1,
8105 }),
8106 ],
8107 ..Default::default()
8108 };
8109
8110 let mut incoming_instructions = StateInstructions::default();
8111 generate_instructions_from_switch(
8112 &mut state,
8113 choice.switch_id,
8114 SideReference::SideOne,
8115 &mut incoming_instructions,
8116 );
8117
8118 assert_eq!(expected_instructions, incoming_instructions);
8119 }
8120
8121 #[test]
8122 fn test_switching_in_with_intimidate_when_opponent_is_already_lowest_atk_boost() {
8123 let mut state: State = State::default();
8124 state.side_one.pokemon[PokemonIndex::P1].ability = Abilities::INTIMIDATE;
8125 state.side_two.attack_boost = -6;
8126 let mut choice = Choice {
8127 ..Default::default()
8128 };
8129 choice.switch_id = PokemonIndex::P1;
8130
8131 let expected_instructions: StateInstructions = StateInstructions {
8132 percentage: 100.0,
8133 instruction_list: vec![Instruction::Switch(SwitchInstruction {
8134 side_ref: SideReference::SideOne,
8135 previous_index: PokemonIndex::P0,
8136 next_index: PokemonIndex::P1,
8137 })],
8138 ..Default::default()
8139 };
8140
8141 let mut incoming_instructions = StateInstructions::default();
8142 generate_instructions_from_switch(
8143 &mut state,
8144 choice.switch_id,
8145 SideReference::SideOne,
8146 &mut incoming_instructions,
8147 );
8148
8149 assert_eq!(expected_instructions, incoming_instructions);
8150 }
8151
8152 #[test]
8153 fn test_switching_in_with_intimidate_versus_clearbody() {
8154 let mut state: State = State::default();
8155 state.side_one.pokemon[PokemonIndex::P1].ability = Abilities::INTIMIDATE;
8156 state.side_two.get_active().ability = Abilities::CLEARBODY;
8157 let mut choice = Choice {
8158 ..Default::default()
8159 };
8160 choice.switch_id = PokemonIndex::P1;
8161
8162 let expected_instructions: StateInstructions = StateInstructions {
8163 percentage: 100.0,
8164 instruction_list: vec![Instruction::Switch(SwitchInstruction {
8165 side_ref: SideReference::SideOne,
8166 previous_index: PokemonIndex::P0,
8167 next_index: PokemonIndex::P1,
8168 })],
8169 ..Default::default()
8170 };
8171
8172 let mut incoming_instructions = StateInstructions::default();
8173 generate_instructions_from_switch(
8174 &mut state,
8175 choice.switch_id,
8176 SideReference::SideOne,
8177 &mut incoming_instructions,
8178 );
8179
8180 assert_eq!(expected_instructions, incoming_instructions);
8181 }
8182
8183 #[test]
8184 fn test_switching_into_double_layer_toxicspikes_as_poison_type() {
8185 let mut state: State = State::default();
8186 state.side_one.pokemon[PokemonIndex::P1].types.0 = PokemonType::POISON;
8187 state.side_one.side_conditions.toxic_spikes = 2;
8188 let mut choice = Choice {
8189 ..Default::default()
8190 };
8191 choice.switch_id = PokemonIndex::P1;
8192
8193 let expected_instructions: StateInstructions = StateInstructions {
8194 percentage: 100.0,
8195 instruction_list: vec![
8196 Instruction::Switch(SwitchInstruction {
8197 side_ref: SideReference::SideOne,
8198 previous_index: PokemonIndex::P0,
8199 next_index: PokemonIndex::P1,
8200 }),
8201 Instruction::ChangeSideCondition(ChangeSideConditionInstruction {
8202 side_ref: SideReference::SideOne,
8203 side_condition: PokemonSideCondition::ToxicSpikes,
8204 amount: -2,
8205 }),
8206 ],
8207 ..Default::default()
8208 };
8209
8210 let mut incoming_instructions = StateInstructions::default();
8211 generate_instructions_from_switch(
8212 &mut state,
8213 choice.switch_id,
8214 SideReference::SideOne,
8215 &mut incoming_instructions,
8216 );
8217
8218 assert_eq!(expected_instructions, incoming_instructions);
8219 }
8220
8221 #[test]
8222 fn test_switching_into_stealthrock_and_spikes_does_not_overkill() {
8223 let mut state: State = State::default();
8224 state.side_one.side_conditions.stealth_rock = 1;
8225 state.side_one.side_conditions.spikes = 1;
8226 state.side_one.pokemon[PokemonIndex::P1].hp = 15;
8227 let mut choice = Choice {
8228 ..Default::default()
8229 };
8230 choice.switch_id = PokemonIndex::P1;
8231
8232 let expected_instructions: StateInstructions = StateInstructions {
8233 percentage: 100.0,
8234 instruction_list: vec![
8235 Instruction::Switch(SwitchInstruction {
8236 side_ref: SideReference::SideOne,
8237 previous_index: PokemonIndex::P0,
8238 next_index: PokemonIndex::P1,
8239 }),
8240 Instruction::Damage(DamageInstruction {
8241 side_ref: SideReference::SideOne,
8242 damage_amount: 12,
8243 }),
8244 Instruction::Damage(DamageInstruction {
8245 side_ref: SideReference::SideOne,
8246 damage_amount: 3,
8247 }),
8248 ],
8249 ..Default::default()
8250 };
8251
8252 let mut incoming_instructions = StateInstructions::default();
8253 generate_instructions_from_switch(
8254 &mut state,
8255 choice.switch_id,
8256 SideReference::SideOne,
8257 &mut incoming_instructions,
8258 );
8259
8260 assert_eq!(expected_instructions, incoming_instructions);
8261 }
8262
8263 #[test]
8264 fn test_switching_into_stealthrock_and_multiple_layers_of_spikes_does_not_overkill() {
8265 let mut state: State = State::default();
8266 state.side_one.side_conditions.stealth_rock = 1;
8267 state.side_one.side_conditions.spikes = 3;
8268 state.side_one.pokemon[PokemonIndex::P1].hp = 25;
8269 let mut choice = Choice {
8270 ..Default::default()
8271 };
8272 choice.switch_id = PokemonIndex::P1;
8273
8274 let expected_instructions: StateInstructions = StateInstructions {
8275 percentage: 100.0,
8276 instruction_list: vec![
8277 Instruction::Switch(SwitchInstruction {
8278 side_ref: SideReference::SideOne,
8279 previous_index: PokemonIndex::P0,
8280 next_index: PokemonIndex::P1,
8281 }),
8282 Instruction::Damage(DamageInstruction {
8283 side_ref: SideReference::SideOne,
8284 damage_amount: 12,
8285 }),
8286 Instruction::Damage(DamageInstruction {
8287 side_ref: SideReference::SideOne,
8288 damage_amount: 13,
8289 }),
8290 ],
8291 ..Default::default()
8292 };
8293
8294 let mut incoming_instructions = StateInstructions::default();
8295 generate_instructions_from_switch(
8296 &mut state,
8297 choice.switch_id,
8298 SideReference::SideOne,
8299 &mut incoming_instructions,
8300 );
8301
8302 assert_eq!(expected_instructions, incoming_instructions);
8303 }
8304
8305 #[test]
8306 fn test_healthy_pokemon_with_no_prior_instructions() {
8307 let mut state = State::default();
8308 let mut incoming_instructions = StateInstructions::default();
8309
8310 let expected_instructions = StateInstructions::default();
8311
8312 generate_instructions_from_existing_status_conditions(
8313 &mut state,
8314 &SideReference::SideOne,
8315 &Choice::default(),
8316 &mut incoming_instructions,
8317 &mut vec![],
8318 );
8319
8320 assert_eq!(expected_instructions, incoming_instructions);
8321 }
8322
8323 #[test]
8324 fn test_rest_turns_at_3_with_no_prior_instructions() {
8325 let mut state = State::default();
8326 state.side_one.get_active().status = PokemonStatus::SLEEP;
8327 state.side_one.get_active().rest_turns = 3;
8328 let mut incoming_instructions = StateInstructions::default();
8329
8330 let expected_instructions = StateInstructions {
8331 percentage: 100.0,
8332 instruction_list: vec![Instruction::DecrementRestTurns(
8333 DecrementRestTurnsInstruction {
8334 side_ref: SideReference::SideOne,
8335 },
8336 )],
8337 };
8338
8339 let expected_frozen_instructions: &mut Vec<StateInstructions> = &mut vec![];
8340
8341 let frozen_instructions = &mut vec![];
8342 generate_instructions_from_existing_status_conditions(
8343 &mut state,
8344 &SideReference::SideOne,
8345 &Choice::default(),
8346 &mut incoming_instructions,
8347 frozen_instructions,
8348 );
8349
8350 assert_eq!(expected_instructions, incoming_instructions);
8351 assert_eq!(expected_frozen_instructions, frozen_instructions);
8352 }
8353
8354 #[test]
8355 fn test_rest_turns_at_2_with_no_prior_instructions() {
8356 let mut state = State::default();
8357 state.side_one.get_active().status = PokemonStatus::SLEEP;
8358 state.side_one.get_active().rest_turns = 2;
8359 let mut incoming_instructions = StateInstructions::default();
8360
8361 let expected_instructions = StateInstructions {
8362 percentage: 100.0,
8363 instruction_list: vec![Instruction::DecrementRestTurns(
8364 DecrementRestTurnsInstruction {
8365 side_ref: SideReference::SideOne,
8366 },
8367 )],
8368 };
8369
8370 let expected_frozen_instructions: &mut Vec<StateInstructions> = &mut vec![];
8371
8372 let frozen_instructions = &mut vec![];
8373
8374 generate_instructions_from_existing_status_conditions(
8375 &mut state,
8376 &SideReference::SideOne,
8377 &Choice::default(),
8378 &mut incoming_instructions,
8379 frozen_instructions,
8380 );
8381
8382 assert_eq!(expected_instructions, incoming_instructions);
8383 assert_eq!(expected_frozen_instructions, frozen_instructions);
8384 }
8385
8386 #[test]
8387 fn test_paralyzed_pokemon_with_no_prior_instructions() {
8388 let mut state = State::default();
8389 state.side_one.get_active().status = PokemonStatus::PARALYZE;
8390 let mut incoming_instructions = StateInstructions::default();
8391
8392 let expected_instructions = StateInstructions {
8393 percentage: 75.0,
8394 instruction_list: vec![],
8395 };
8396
8397 let expected_frozen_instructions = &mut vec![StateInstructions {
8398 percentage: 25.0,
8399 instruction_list: vec![],
8400 }];
8401
8402 let frozen_instructions = &mut vec![];
8403
8404 generate_instructions_from_existing_status_conditions(
8405 &mut state,
8406 &SideReference::SideOne,
8407 &Choice::default(),
8408 &mut incoming_instructions,
8409 frozen_instructions,
8410 );
8411
8412 assert_eq!(expected_instructions, incoming_instructions);
8413 assert_eq!(expected_frozen_instructions, frozen_instructions);
8414 }
8415
8416 #[test]
8417 fn test_confused_pokemon_with_no_prior_instructions() {
8418 let mut state = State::default();
8419 state
8420 .side_one
8421 .volatile_statuses
8422 .insert(PokemonVolatileStatus::CONFUSION);
8423 let mut incoming_instructions = StateInstructions::default();
8424
8425 let expected_instructions = StateInstructions {
8426 percentage: 100.0 * (1.0 - HIT_SELF_IN_CONFUSION_CHANCE),
8427 instruction_list: vec![],
8428 };
8429
8430 let expected_frozen_instructions = &mut vec![StateInstructions {
8431 percentage: 100.0 * (HIT_SELF_IN_CONFUSION_CHANCE),
8432 instruction_list: vec![Instruction::Damage(DamageInstruction {
8433 side_ref: SideReference::SideOne,
8434 damage_amount: 35,
8435 })],
8436 }];
8437
8438 let frozen_instructions = &mut vec![];
8439
8440 generate_instructions_from_existing_status_conditions(
8441 &mut state,
8442 &SideReference::SideOne,
8443 &Choice::default(),
8444 &mut incoming_instructions,
8445 frozen_instructions,
8446 );
8447
8448 assert_eq!(expected_instructions, incoming_instructions);
8449 assert_eq!(expected_frozen_instructions, frozen_instructions);
8450 }
8451
8452 #[test]
8453 fn test_confused_pokemon_with_prior_instruction() {
8454 let mut state = State::default();
8455 state
8456 .side_one
8457 .volatile_statuses
8458 .insert(PokemonVolatileStatus::CONFUSION);
8459 let mut incoming_instructions = StateInstructions::default();
8460 incoming_instructions.instruction_list = vec![Instruction::Damage(DamageInstruction {
8461 side_ref: SideReference::SideOne,
8462 damage_amount: 1,
8463 })];
8464
8465 let expected_instructions = StateInstructions {
8466 percentage: 100.0 * (1.0 - HIT_SELF_IN_CONFUSION_CHANCE),
8467 instruction_list: vec![Instruction::Damage(DamageInstruction {
8468 side_ref: SideReference::SideOne,
8469 damage_amount: 1,
8470 })],
8471 };
8472
8473 let expected_frozen_instructions = &mut vec![StateInstructions {
8474 percentage: 100.0 * HIT_SELF_IN_CONFUSION_CHANCE,
8475 instruction_list: vec![
8476 Instruction::Damage(DamageInstruction {
8477 side_ref: SideReference::SideOne,
8478 damage_amount: 1,
8479 }),
8480 Instruction::Damage(DamageInstruction {
8481 side_ref: SideReference::SideOne,
8482 damage_amount: 35,
8483 }),
8484 ],
8485 }];
8486
8487 let frozen_instructions = &mut vec![];
8488
8489 generate_instructions_from_existing_status_conditions(
8490 &mut state,
8491 &SideReference::SideOne,
8492 &Choice::default(),
8493 &mut incoming_instructions,
8494 frozen_instructions,
8495 );
8496
8497 assert_eq!(expected_instructions, incoming_instructions);
8498 assert_eq!(expected_frozen_instructions, frozen_instructions);
8499 }
8500
8501 #[test]
8502 fn test_confused_pokemon_with_prior_instruction_does_not_overkill() {
8503 let mut state = State::default();
8504 state
8505 .side_one
8506 .volatile_statuses
8507 .insert(PokemonVolatileStatus::CONFUSION);
8508 let mut incoming_instructions = StateInstructions::default();
8509 state.side_one.get_active().hp = 2;
8510 incoming_instructions.instruction_list = vec![Instruction::Damage(DamageInstruction {
8511 side_ref: SideReference::SideOne,
8512 damage_amount: 1,
8513 })];
8514
8515 let expected_instructions = StateInstructions {
8516 percentage: 100.0 * (1.0 - HIT_SELF_IN_CONFUSION_CHANCE),
8517 instruction_list: vec![Instruction::Damage(DamageInstruction {
8518 side_ref: SideReference::SideOne,
8519 damage_amount: 1,
8520 })],
8521 };
8522
8523 let expected_frozen_instructions = &mut vec![StateInstructions {
8524 percentage: 100.0 * HIT_SELF_IN_CONFUSION_CHANCE,
8525 instruction_list: vec![
8526 Instruction::Damage(DamageInstruction {
8527 side_ref: SideReference::SideOne,
8528 damage_amount: 1,
8529 }),
8530 Instruction::Damage(DamageInstruction {
8531 side_ref: SideReference::SideOne,
8532 damage_amount: 2,
8533 }),
8534 ],
8535 }];
8536
8537 let frozen_instructions = &mut vec![];
8538
8539 generate_instructions_from_existing_status_conditions(
8540 &mut state,
8541 &SideReference::SideOne,
8542 &Choice::default(),
8543 &mut incoming_instructions,
8544 frozen_instructions,
8545 );
8546
8547 assert_eq!(expected_instructions, incoming_instructions);
8548 assert_eq!(expected_frozen_instructions, frozen_instructions);
8549 }
8550
8551 #[test]
8552 fn test_frozen_pokemon_with_no_prior_instructions() {
8553 let mut state = State::default();
8554 state.side_one.get_active().status = PokemonStatus::FREEZE;
8555 let mut incoming_instructions = StateInstructions::default();
8556
8557 let expected_instructions = StateInstructions {
8558 percentage: 20.0,
8559 instruction_list: vec![Instruction::ChangeStatus(ChangeStatusInstruction {
8560 side_ref: SideReference::SideOne,
8561 pokemon_index: state.side_one.active_index,
8562 old_status: PokemonStatus::FREEZE,
8563 new_status: PokemonStatus::NONE,
8564 })],
8565 };
8566
8567 let expected_frozen_instructions = &mut vec![StateInstructions {
8568 percentage: 80.0,
8569 instruction_list: vec![],
8570 }];
8571
8572 let frozen_instructions = &mut vec![];
8573
8574 generate_instructions_from_existing_status_conditions(
8575 &mut state,
8576 &SideReference::SideOne,
8577 &Choice::default(),
8578 &mut incoming_instructions,
8579 frozen_instructions,
8580 );
8581
8582 assert_eq!(expected_instructions, incoming_instructions);
8583 assert_eq!(expected_frozen_instructions, frozen_instructions);
8584 }
8585
8586 #[test]
8587 fn test_asleep_pokemon_with_no_prior_instructions() {
8588 let mut state = State::default();
8589 state.side_one.get_active().status = PokemonStatus::SLEEP;
8590 state.side_one.get_active().sleep_turns = MAX_SLEEP_TURNS;
8591 let mut incoming_instructions = StateInstructions::default();
8592
8593 let expected_instructions = StateInstructions {
8594 percentage: 100.0,
8595 instruction_list: vec![
8596 Instruction::ChangeStatus(ChangeStatusInstruction {
8597 side_ref: SideReference::SideOne,
8598 pokemon_index: state.side_one.active_index,
8599 old_status: PokemonStatus::SLEEP,
8600 new_status: PokemonStatus::NONE,
8601 }),
8602 Instruction::SetSleepTurns(SetSleepTurnsInstruction {
8603 side_ref: SideReference::SideOne,
8604 pokemon_index: PokemonIndex::P0,
8605 new_turns: 0,
8606 previous_turns: MAX_SLEEP_TURNS,
8607 }),
8608 ],
8609 };
8610
8611 let expected_frozen_instructions: &mut Vec<StateInstructions> = &mut vec![];
8612
8613 let frozen_instructions = &mut vec![];
8614
8615 generate_instructions_from_existing_status_conditions(
8616 &mut state,
8617 &SideReference::SideOne,
8618 &Choice::default(),
8619 &mut incoming_instructions,
8620 frozen_instructions,
8621 );
8622
8623 assert_eq!(expected_instructions, incoming_instructions);
8624 assert_eq!(expected_frozen_instructions, frozen_instructions);
8625 }
8626
8627 #[test]
8628 fn test_asleep_waking_up_and_confused() {
8629 let mut state = State::default();
8630 state.side_one.get_active().status = PokemonStatus::SLEEP;
8631 state.side_one.get_active().sleep_turns = MAX_SLEEP_TURNS;
8632 state
8633 .side_one
8634 .volatile_statuses
8635 .insert(PokemonVolatileStatus::CONFUSION);
8636 let mut incoming_instructions = StateInstructions::default();
8637
8638 let expected_instructions = StateInstructions {
8639 percentage: 100.0 * (1.0 - HIT_SELF_IN_CONFUSION_CHANCE),
8640 instruction_list: vec![
8641 Instruction::ChangeStatus(ChangeStatusInstruction {
8642 side_ref: SideReference::SideOne,
8643 pokemon_index: state.side_one.active_index,
8644 old_status: PokemonStatus::SLEEP,
8645 new_status: PokemonStatus::NONE,
8646 }),
8647 Instruction::SetSleepTurns(SetSleepTurnsInstruction {
8648 side_ref: SideReference::SideOne,
8649 pokemon_index: PokemonIndex::P0,
8650 new_turns: 0,
8651 previous_turns: MAX_SLEEP_TURNS,
8652 }),
8653 ],
8654 };
8655
8656 let expected_frozen_instructions = &mut vec![StateInstructions {
8657 percentage: 100.0 * HIT_SELF_IN_CONFUSION_CHANCE,
8658 instruction_list: vec![
8659 Instruction::ChangeStatus(ChangeStatusInstruction {
8660 side_ref: SideReference::SideOne,
8661 pokemon_index: state.side_one.active_index,
8662 old_status: PokemonStatus::SLEEP,
8663 new_status: PokemonStatus::NONE,
8664 }),
8665 Instruction::SetSleepTurns(SetSleepTurnsInstruction {
8666 side_ref: SideReference::SideOne,
8667 pokemon_index: PokemonIndex::P0,
8668 new_turns: 0,
8669 previous_turns: MAX_SLEEP_TURNS,
8670 }),
8671 Instruction::Damage(DamageInstruction {
8672 side_ref: SideReference::SideOne,
8673 damage_amount: 35,
8674 }),
8675 ],
8676 }];
8677
8678 let frozen_instructions = &mut vec![];
8679
8680 generate_instructions_from_existing_status_conditions(
8681 &mut state,
8682 &SideReference::SideOne,
8683 &Choice::default(),
8684 &mut incoming_instructions,
8685 frozen_instructions,
8686 );
8687
8688 assert_eq!(expected_instructions, incoming_instructions);
8689 assert_eq!(expected_frozen_instructions, frozen_instructions);
8690 }
8691
8692 #[test]
8693 fn test_asleep_pokemon_waking_up_with_1_rest_turn() {
8694 let mut state = State::default();
8695 state.side_one.get_active().status = PokemonStatus::SLEEP;
8696 state.side_one.get_active().rest_turns = 1;
8697 let mut incoming_instructions = StateInstructions::default();
8698
8699 let expected_instructions = StateInstructions {
8700 percentage: 100.0,
8701 instruction_list: vec![
8702 Instruction::ChangeStatus(ChangeStatusInstruction {
8703 side_ref: SideReference::SideOne,
8704 pokemon_index: state.side_one.active_index,
8705 old_status: PokemonStatus::SLEEP,
8706 new_status: PokemonStatus::NONE,
8707 }),
8708 Instruction::DecrementRestTurns(DecrementRestTurnsInstruction {
8709 side_ref: SideReference::SideOne,
8710 }),
8711 ],
8712 };
8713
8714 let expected_frozen_instructions: &mut Vec<StateInstructions> = &mut vec![];
8715 let frozen_instructions = &mut vec![];
8716
8717 generate_instructions_from_existing_status_conditions(
8718 &mut state,
8719 &SideReference::SideOne,
8720 &Choice::default(),
8721 &mut incoming_instructions,
8722 frozen_instructions,
8723 );
8724
8725 assert_eq!(expected_instructions, incoming_instructions);
8726 assert_eq!(expected_frozen_instructions, frozen_instructions);
8727 }
8728
8729 #[test]
8730 fn test_asleep_pokemon_staying_asleep_with_two_rest_turns() {
8731 let mut state = State::default();
8732 state.side_one.get_active().status = PokemonStatus::SLEEP;
8733 state.side_one.get_active().rest_turns = 1;
8734 let mut incoming_instructions = StateInstructions::default();
8735
8736 let expected_instructions = StateInstructions {
8737 percentage: 100.0,
8738 instruction_list: vec![
8739 Instruction::ChangeStatus(ChangeStatusInstruction {
8740 side_ref: SideReference::SideOne,
8741 pokemon_index: state.side_one.active_index,
8742 old_status: PokemonStatus::SLEEP,
8743 new_status: PokemonStatus::NONE,
8744 }),
8745 Instruction::DecrementRestTurns(DecrementRestTurnsInstruction {
8746 side_ref: SideReference::SideOne,
8747 }),
8748 ],
8749 };
8750
8751 let expected_frozen_instructions: &mut Vec<StateInstructions> = &mut vec![];
8752 let frozen_instructions = &mut vec![];
8753
8754 generate_instructions_from_existing_status_conditions(
8755 &mut state,
8756 &SideReference::SideOne,
8757 &Choice::default(),
8758 &mut incoming_instructions,
8759 frozen_instructions,
8760 );
8761
8762 assert_eq!(expected_instructions, incoming_instructions);
8763 assert_eq!(expected_frozen_instructions, frozen_instructions);
8764 }
8765
8766 #[test]
8767 fn test_paralyzed_pokemon_preserves_prior_instructions() {
8768 let mut state = State::default();
8769 state.side_one.get_active().status = PokemonStatus::PARALYZE;
8770 let mut incoming_instructions = StateInstructions::default();
8771 incoming_instructions.instruction_list = vec![Instruction::Damage(DamageInstruction {
8772 side_ref: SideReference::SideOne,
8773 damage_amount: 1,
8774 })];
8775
8776 let expected_instructions = StateInstructions {
8777 percentage: 75.0,
8778 instruction_list: vec![Instruction::Damage(DamageInstruction {
8779 side_ref: SideReference::SideOne,
8780 damage_amount: 1,
8781 })],
8782 };
8783
8784 let expected_frozen_instructions = &mut vec![StateInstructions {
8785 percentage: 25.0,
8786 instruction_list: vec![Instruction::Damage(DamageInstruction {
8787 side_ref: SideReference::SideOne,
8788 damage_amount: 1,
8789 })],
8790 }];
8791
8792 let frozen_instructions = &mut vec![];
8793
8794 generate_instructions_from_existing_status_conditions(
8795 &mut state,
8796 &SideReference::SideOne,
8797 &Choice::default(),
8798 &mut incoming_instructions,
8799 frozen_instructions,
8800 );
8801
8802 assert_eq!(expected_instructions, incoming_instructions);
8803 assert_eq!(expected_frozen_instructions, frozen_instructions);
8804 }
8805
8806 #[test]
8807 fn test_basic_side_two_moves_first() {
8808 let mut state = State::default();
8809 let side_one_choice = MOVES.get(&Choices::TACKLE).unwrap().to_owned();
8810 let side_two_choice = MOVES.get(&Choices::TACKLE).unwrap().to_owned();
8811 state.side_one.get_active().speed = 100;
8812 state.side_two.get_active().speed = 101;
8813
8814 assert_eq!(
8815 SideMovesFirst::SideTwo,
8816 moves_first(
8817 &state,
8818 &side_one_choice,
8819 &side_two_choice,
8820 &mut StateInstructions::default()
8821 )
8822 )
8823 }
8824
8825 #[test]
8826 fn test_custap_berry_when_less_than_25_percent_activates() {
8827 let mut state = State::default();
8828 let side_one_choice = MOVES.get(&Choices::TACKLE).unwrap().to_owned();
8829 let side_two_choice = MOVES.get(&Choices::TACKLE).unwrap().to_owned();
8830 state.side_one.get_active().item = Items::CUSTAPBERRY;
8831 state.side_one.get_active().hp = 24;
8832 state.side_one.get_active().speed = 100;
8833 state.side_two.get_active().speed = 101;
8834
8835 assert_eq!(
8836 SideMovesFirst::SideOne,
8837 moves_first(
8838 &state,
8839 &side_one_choice,
8840 &side_two_choice,
8841 &mut StateInstructions::default()
8842 )
8843 )
8844 }
8845
8846 #[test]
8847 fn test_quarkdrivespe_boost_works() {
8848 let mut state = State::default();
8849 let side_one_choice = MOVES.get(&Choices::TACKLE).unwrap().to_owned();
8850 let side_two_choice = MOVES.get(&Choices::TACKLE).unwrap().to_owned();
8851 state
8852 .side_one
8853 .volatile_statuses
8854 .insert(PokemonVolatileStatus::QUARKDRIVESPE);
8855 state.side_one.get_active().hp = 24;
8856 state.side_one.get_active().speed = 100;
8857 state.side_two.get_active().speed = 101;
8858
8859 assert_eq!(
8860 SideMovesFirst::SideOne,
8861 moves_first(
8862 &state,
8863 &side_one_choice,
8864 &side_two_choice,
8865 &mut StateInstructions::default()
8866 )
8867 )
8868 }
8869
8870 #[test]
8871 fn test_protosynthesisspe_boost_works() {
8872 let mut state = State::default();
8873 let side_one_choice = MOVES.get(&Choices::TACKLE).unwrap().to_owned();
8874 let side_two_choice = MOVES.get(&Choices::TACKLE).unwrap().to_owned();
8875 state
8876 .side_one
8877 .volatile_statuses
8878 .insert(PokemonVolatileStatus::PROTOSYNTHESISSPE);
8879 state.side_one.get_active().hp = 24;
8880 state.side_one.get_active().speed = 100;
8881 state.side_two.get_active().speed = 101;
8882
8883 assert_eq!(
8884 SideMovesFirst::SideOne,
8885 moves_first(
8886 &state,
8887 &side_one_choice,
8888 &side_two_choice,
8889 &mut StateInstructions::default()
8890 )
8891 )
8892 }
8893
8894 #[test]
8895 fn test_custap_berry_when_greater_than_25_percent_does_not_activate() {
8896 let mut state = State::default();
8897 let side_one_choice = MOVES.get(&Choices::TACKLE).unwrap().to_owned();
8898 let side_two_choice = MOVES.get(&Choices::TACKLE).unwrap().to_owned();
8899 state.side_one.get_active().item = Items::CUSTAPBERRY;
8900 state.side_one.get_active().speed = 100;
8901 state.side_two.get_active().speed = 101;
8902
8903 assert_eq!(
8904 SideMovesFirst::SideTwo,
8905 moves_first(
8906 &state,
8907 &side_one_choice,
8908 &side_two_choice,
8909 &mut StateInstructions::default()
8910 )
8911 )
8912 }
8913
8914 #[test]
8915 fn test_custap_berry_does_not_matter_when_opponent_uses_increased_priority_move() {
8916 let mut state = State::default();
8917 let side_one_choice = MOVES.get(&Choices::TACKLE).unwrap().to_owned();
8918 let side_two_choice = MOVES.get(&Choices::QUICKATTACK).unwrap().to_owned();
8919 state.side_one.get_active().item = Items::CUSTAPBERRY;
8920 state.side_one.get_active().hp = 24;
8921 state.side_one.get_active().speed = 100;
8922 state.side_two.get_active().speed = 101;
8923
8924 assert_eq!(
8925 SideMovesFirst::SideTwo,
8926 moves_first(
8927 &state,
8928 &side_one_choice,
8929 &side_two_choice,
8930 &mut StateInstructions::default()
8931 )
8932 )
8933 }
8934
8935 #[test]
8936 fn test_slowstart_halves_effective_speed() {
8937 let mut state = State::default();
8938 let side_one_choice = MOVES.get(&Choices::TACKLE).unwrap().to_owned();
8939 let side_two_choice = MOVES.get(&Choices::TACKLE).unwrap().to_owned();
8940 state.side_one.get_active().speed = 100;
8941 state.side_two.get_active().speed = 101;
8942 state
8943 .side_two
8944 .volatile_statuses
8945 .insert(PokemonVolatileStatus::SLOWSTART);
8946
8947 assert_eq!(
8948 SideMovesFirst::SideOne,
8949 moves_first(
8950 &state,
8951 &side_one_choice,
8952 &side_two_choice,
8953 &mut StateInstructions::default()
8954 )
8955 )
8956 }
8957
8958 #[test]
8959 fn test_basic_side_one_moves_first() {
8960 let mut state = State::default();
8961 let side_one_choice = MOVES.get(&Choices::TACKLE).unwrap().to_owned();
8962 let side_two_choice = MOVES.get(&Choices::TACKLE).unwrap().to_owned();
8963 state.side_one.get_active().speed = 101;
8964 state.side_two.get_active().speed = 100;
8965
8966 assert_eq!(
8967 SideMovesFirst::SideOne,
8968 moves_first(
8969 &state,
8970 &side_one_choice,
8971 &side_two_choice,
8972 &mut StateInstructions::default()
8973 )
8974 )
8975 }
8976
8977 #[test]
8978 fn test_paralysis_reduces_effective_speed() {
8979 let mut state = State::default();
8980 let side_one_choice = MOVES.get(&Choices::TACKLE).unwrap().to_owned();
8981 let side_two_choice = MOVES.get(&Choices::TACKLE).unwrap().to_owned();
8982
8983 state.side_one.get_active().status = PokemonStatus::PARALYZE;
8984 state.side_one.get_active().speed = 101;
8985 state.side_two.get_active().speed = 100;
8986
8987 assert_eq!(
8988 SideMovesFirst::SideTwo,
8989 moves_first(
8990 &state,
8991 &side_one_choice,
8992 &side_two_choice,
8993 &mut StateInstructions::default()
8994 )
8995 )
8996 }
8997
8998 #[test]
8999 #[cfg(any(feature = "gen7", feature = "gen8", feature = "gen9"))]
9000 fn test_later_gen_speed_cutting_in_half() {
9001 let mut state = State::default();
9002 state.side_one.get_active().status = PokemonStatus::PARALYZE;
9003 state.side_one.get_active().speed = 100;
9004
9005 assert_eq!(50, get_effective_speed(&state, &SideReference::SideOne))
9006 }
9007
9008 #[test]
9009 #[cfg(any(feature = "gen3", feature = "gen4", feature = "gen5", feature = "gen6"))]
9010 fn test_earlier_gen_speed_cutting_by_75_percent() {
9011 let mut state = State::default();
9012 state.side_one.get_active().status = PokemonStatus::PARALYZE;
9013 state.side_one.get_active().speed = 100;
9014
9015 assert_eq!(25, get_effective_speed(&state, &SideReference::SideOne))
9016 }
9017
9018 #[test]
9019 fn test_choicescarf_multiplying_speed() {
9020 let mut state = State::default();
9021 state.side_one.get_active().speed = 100;
9022 state.side_one.get_active().item = Items::CHOICESCARF;
9023
9024 assert_eq!(150, get_effective_speed(&state, &SideReference::SideOne))
9025 }
9026
9027 #[test]
9028 fn test_iron_ball_halving_speed() {
9029 let mut state = State::default();
9030 state.side_one.get_active().speed = 100;
9031 state.side_one.get_active().item = Items::IRONBALL;
9032
9033 assert_eq!(50, get_effective_speed(&state, &SideReference::SideOne))
9034 }
9035
9036 #[test]
9037 fn test_speed_tie_goes_to_side_two() {
9038 let mut state = State::default();
9039 let side_one_choice = MOVES.get(&Choices::TACKLE).unwrap().to_owned();
9040 let side_two_choice = MOVES.get(&Choices::TACKLE).unwrap().to_owned();
9041 state.side_one.get_active().speed = 100;
9042 state.side_two.get_active().speed = 100;
9043
9044 assert_eq!(
9045 SideMovesFirst::SpeedTie,
9046 moves_first(
9047 &state,
9048 &side_one_choice,
9049 &side_two_choice,
9050 &mut StateInstructions::default()
9051 )
9052 )
9053 }
9054
9055 #[test]
9056 fn test_higher_priority_ignores_speed_diff() {
9057 let mut state = State::default();
9058 let side_one_choice = MOVES.get(&Choices::QUICKATTACK).unwrap().to_owned();
9059 let side_two_choice = MOVES.get(&Choices::TACKLE).unwrap().to_owned();
9060 state.side_one.get_active().speed = 100;
9061 state.side_two.get_active().speed = 101;
9062
9063 assert_eq!(
9064 SideMovesFirst::SideOne,
9065 moves_first(
9066 &state,
9067 &side_one_choice,
9068 &side_two_choice,
9069 &mut StateInstructions::default()
9070 )
9071 )
9072 }
9073
9074 #[test]
9075 fn test_side_two_higher_priority_ignores_speed_diff() {
9076 let mut state = State::default();
9077 let side_one_choice = MOVES.get(&Choices::TACKLE).unwrap().to_owned();
9078 let side_two_choice = MOVES.get(&Choices::QUICKATTACK).unwrap().to_owned();
9079 state.side_one.get_active().speed = 101;
9080 state.side_two.get_active().speed = 100;
9081
9082 assert_eq!(
9083 SideMovesFirst::SideTwo,
9084 moves_first(
9085 &state,
9086 &side_one_choice,
9087 &side_two_choice,
9088 &mut StateInstructions::default()
9089 )
9090 )
9091 }
9092
9093 #[test]
9094 fn test_both_higher_priority_defaults_back_to_speed() {
9095 let mut state = State::default();
9096 let side_one_choice = MOVES.get(&Choices::QUICKATTACK).unwrap().to_owned();
9097 let side_two_choice = MOVES.get(&Choices::QUICKATTACK).unwrap().to_owned();
9098 state.side_one.get_active().speed = 101;
9099 state.side_two.get_active().speed = 100;
9100
9101 assert_eq!(
9102 SideMovesFirst::SideOne,
9103 moves_first(
9104 &state,
9105 &side_one_choice,
9106 &side_two_choice,
9107 &mut StateInstructions::default()
9108 )
9109 )
9110 }
9111
9112 #[test]
9113 fn test_switch_always_goes_first() {
9114 let mut state = State::default();
9115 let mut side_one_choice = MOVES.get(&Choices::SPLASH).unwrap().to_owned();
9116 side_one_choice.category = MoveCategory::Switch;
9117 let side_two_choice = MOVES.get(&Choices::QUICKATTACK).unwrap().to_owned();
9118 state.side_one.get_active().speed = 99;
9119 state.side_two.get_active().speed = 100;
9120
9121 assert_eq!(
9122 SideMovesFirst::SideOne,
9123 moves_first(
9124 &state,
9125 &side_one_choice,
9126 &side_two_choice,
9127 &mut StateInstructions::default()
9128 )
9129 )
9130 }
9131
9132 #[test]
9133 fn test_double_switch_checks_higher_speed() {
9134 let mut state = State::default();
9135 let mut side_one_choice = MOVES.get(&Choices::SPLASH).unwrap().to_owned();
9136 side_one_choice.category = MoveCategory::Switch;
9137 let mut side_two_choice = MOVES.get(&Choices::SPLASH).unwrap().to_owned();
9138 side_two_choice.category = MoveCategory::Switch;
9139
9140 state.side_one.get_active().speed = 99;
9141 state.side_two.get_active().speed = 100;
9142
9143 assert_eq!(
9144 SideMovesFirst::SideTwo,
9145 moves_first(
9146 &state,
9147 &side_one_choice,
9148 &side_two_choice,
9149 &mut StateInstructions::default()
9150 )
9151 )
9152 }
9153
9154 #[test]
9155 fn test_pursuit_goes_before_switch() {
9156 let mut state = State::default();
9157 let side_one_choice = MOVES.get(&Choices::PURSUIT).unwrap().to_owned();
9158 let mut side_two_choice = MOVES.get(&Choices::SPLASH).unwrap().to_owned();
9159 side_two_choice.category = MoveCategory::Switch;
9160
9161 state.side_one.get_active().speed = 50;
9162 state.side_two.get_active().speed = 100;
9163
9164 assert_eq!(
9165 SideMovesFirst::SideOne,
9166 moves_first(
9167 &state,
9168 &side_one_choice,
9169 &side_two_choice,
9170 &mut StateInstructions::default()
9171 )
9172 )
9173 }
9174
9175 #[test]
9176 fn test_end_of_turn_hail_damage() {
9177 let mut state = State::default();
9178 state.weather.weather_type = Weather::HAIL;
9179
9180 let mut incoming_instructions = StateInstructions::default();
9181 add_end_of_turn_instructions(
9182 &mut state,
9183 &mut incoming_instructions,
9184 &SideReference::SideOne,
9185 );
9186
9187 let expected_instructions = StateInstructions {
9188 percentage: 100.0,
9189 instruction_list: vec![
9190 Instruction::Damage(DamageInstruction {
9191 side_ref: SideReference::SideOne,
9192 damage_amount: 6,
9193 }),
9194 Instruction::Damage(DamageInstruction {
9195 side_ref: SideReference::SideTwo,
9196 damage_amount: 6,
9197 }),
9198 ],
9199 };
9200
9201 assert_eq!(expected_instructions, incoming_instructions)
9202 }
9203
9204 #[test]
9205 fn test_end_of_turn_hail_damage_against_ice_type() {
9206 let mut state = State::default();
9207 state.weather.weather_type = Weather::HAIL;
9208 state.side_two.get_active().types.0 = PokemonType::ICE;
9209
9210 let mut incoming_instructions = StateInstructions::default();
9211 add_end_of_turn_instructions(
9212 &mut state,
9213 &mut incoming_instructions,
9214 &SideReference::SideOne,
9215 );
9216
9217 let expected_instructions = StateInstructions {
9218 percentage: 100.0,
9219 instruction_list: vec![
9220 Instruction::Damage(DamageInstruction {
9222 side_ref: SideReference::SideOne,
9223 damage_amount: 6,
9224 }),
9225 ],
9226 };
9227
9228 assert_eq!(expected_instructions, incoming_instructions)
9229 }
9230
9231 #[test]
9232 fn test_end_of_turn_sand_damage() {
9233 let mut state = State::default();
9234 state.weather.weather_type = Weather::SAND;
9235
9236 let mut incoming_instructions = StateInstructions::default();
9237 add_end_of_turn_instructions(
9238 &mut state,
9239 &mut incoming_instructions,
9240 &SideReference::SideOne,
9241 );
9242
9243 let expected_instructions = StateInstructions {
9244 percentage: 100.0,
9245 instruction_list: vec![
9246 Instruction::Damage(DamageInstruction {
9247 side_ref: SideReference::SideOne,
9248 damage_amount: 6,
9249 }),
9250 Instruction::Damage(DamageInstruction {
9251 side_ref: SideReference::SideTwo,
9252 damage_amount: 6,
9253 }),
9254 ],
9255 };
9256
9257 assert_eq!(expected_instructions, incoming_instructions)
9258 }
9259
9260 #[test]
9261 fn test_end_of_turn_sand_damage_against_ground_type() {
9262 let mut state = State::default();
9263 state.weather.weather_type = Weather::SAND;
9264 state.side_two.get_active().types.0 = PokemonType::GROUND;
9265
9266 let mut incoming_instructions = StateInstructions::default();
9267 add_end_of_turn_instructions(
9268 &mut state,
9269 &mut incoming_instructions,
9270 &SideReference::SideOne,
9271 );
9272
9273 let expected_instructions = StateInstructions {
9274 percentage: 100.0,
9275
9276 instruction_list: vec![Instruction::Damage(DamageInstruction {
9278 side_ref: SideReference::SideOne,
9279 damage_amount: 6,
9280 })],
9281 };
9282
9283 assert_eq!(expected_instructions, incoming_instructions)
9284 }
9285
9286 #[test]
9287 fn test_hail_does_not_overkill() {
9288 let mut state = State::default();
9289 state.weather.weather_type = Weather::HAIL;
9290 state.side_one.get_active().hp = 3;
9291
9292 let mut incoming_instructions = StateInstructions::default();
9293 add_end_of_turn_instructions(
9294 &mut state,
9295 &mut incoming_instructions,
9296 &SideReference::SideOne,
9297 );
9298
9299 let expected_instructions = StateInstructions {
9300 percentage: 100.0,
9301 instruction_list: vec![
9302 Instruction::Damage(DamageInstruction {
9303 side_ref: SideReference::SideOne,
9304 damage_amount: 3,
9305 }),
9306 Instruction::Damage(DamageInstruction {
9307 side_ref: SideReference::SideTwo,
9308 damage_amount: 6,
9309 }),
9310 ],
9311 };
9312
9313 assert_eq!(expected_instructions, incoming_instructions)
9314 }
9315
9316 #[test]
9317 fn test_fainted_pkmn_does_not_take_hail_dmg() {
9318 let mut state = State::default();
9319 state.weather.weather_type = Weather::HAIL;
9320 state.side_one.get_active().hp = 0;
9321
9322 let mut incoming_instructions = StateInstructions::default();
9323 add_end_of_turn_instructions(
9324 &mut state,
9325 &mut incoming_instructions,
9326 &SideReference::SideOne,
9327 );
9328
9329 let expected_instructions = StateInstructions {
9330 percentage: 100.0,
9331 instruction_list: vec![Instruction::Damage(DamageInstruction {
9332 side_ref: SideReference::SideTwo,
9333 damage_amount: 6,
9334 })],
9335 };
9336
9337 assert_eq!(expected_instructions, incoming_instructions)
9338 }
9339
9340 #[test]
9341 #[cfg(not(feature = "gen4"))]
9342 fn test_wished_pokemon_gets_healed() {
9343 let mut state = State::default();
9344 state.side_one.wish = (1, 5);
9345 state.side_one.get_active().hp = 50;
9346
9347 let mut incoming_instructions = StateInstructions::default();
9348 add_end_of_turn_instructions(
9349 &mut state,
9350 &mut incoming_instructions,
9351 &SideReference::SideOne,
9352 );
9353
9354 let expected_instructions = StateInstructions {
9355 percentage: 100.0,
9356 instruction_list: vec![
9357 Instruction::Heal(HealInstruction {
9358 side_ref: SideReference::SideOne,
9359 heal_amount: 5,
9360 }),
9361 Instruction::DecrementWish(DecrementWishInstruction {
9362 side_ref: SideReference::SideOne,
9363 }),
9364 ],
9365 };
9366
9367 assert_eq!(expected_instructions, incoming_instructions)
9368 }
9369
9370 #[test]
9371 fn test_wish_does_not_overheal() {
9372 let mut state = State::default();
9373 state.side_one.wish = (1, 50);
9374 state.side_one.get_active().hp = 95;
9375
9376 let mut incoming_instructions = StateInstructions::default();
9377 add_end_of_turn_instructions(
9378 &mut state,
9379 &mut incoming_instructions,
9380 &SideReference::SideOne,
9381 );
9382
9383 let expected_instructions = StateInstructions {
9384 percentage: 100.0,
9385 instruction_list: vec![
9386 Instruction::Heal(HealInstruction {
9387 side_ref: SideReference::SideOne,
9388 heal_amount: 5,
9389 }),
9390 Instruction::DecrementWish(DecrementWishInstruction {
9391 side_ref: SideReference::SideOne,
9392 }),
9393 ],
9394 };
9395
9396 assert_eq!(expected_instructions, incoming_instructions)
9397 }
9398
9399 #[test]
9400 fn test_wish_does_nothing_when_maxhp() {
9401 let mut state = State::default();
9402 state.side_one.wish = (1, 50);
9403 state.side_one.get_active().hp = 100;
9404
9405 let mut incoming_instructions = StateInstructions::default();
9406 add_end_of_turn_instructions(
9407 &mut state,
9408 &mut incoming_instructions,
9409 &SideReference::SideOne,
9410 );
9411
9412 let expected_instructions = StateInstructions {
9413 percentage: 100.0,
9414 instruction_list: vec![Instruction::DecrementWish(DecrementWishInstruction {
9415 side_ref: SideReference::SideOne,
9416 })],
9417 };
9418
9419 assert_eq!(expected_instructions, incoming_instructions)
9420 }
9421
9422 #[test]
9423 fn test_wish_does_nothing_when_fainted() {
9424 let mut state = State::default();
9425 state.side_one.wish = (1, 50);
9426 state.side_one.get_active().hp = 0;
9427
9428 let mut incoming_instructions = StateInstructions::default();
9429 add_end_of_turn_instructions(
9430 &mut state,
9431 &mut incoming_instructions,
9432 &SideReference::SideOne,
9433 );
9434
9435 let expected_instructions = StateInstructions {
9436 percentage: 100.0,
9437 instruction_list: vec![Instruction::DecrementWish(DecrementWishInstruction {
9438 side_ref: SideReference::SideOne,
9439 })],
9440 };
9441
9442 assert_eq!(expected_instructions, incoming_instructions)
9443 }
9444
9445 #[test]
9446 fn test_wish_at_2_does_not_heal() {
9447 let mut state = State::default();
9448 state.side_one.wish = (2, 50);
9449 state.side_one.get_active().hp = 95;
9450
9451 let mut incoming_instructions = StateInstructions::default();
9452 add_end_of_turn_instructions(
9453 &mut state,
9454 &mut incoming_instructions,
9455 &SideReference::SideOne,
9456 );
9457
9458 let expected_instructions = StateInstructions {
9459 percentage: 100.0,
9460 instruction_list: vec![Instruction::DecrementWish(DecrementWishInstruction {
9461 side_ref: SideReference::SideOne,
9462 })],
9463 };
9464
9465 assert_eq!(expected_instructions, incoming_instructions)
9466 }
9467
9468 #[test]
9469 fn test_leftovers_heals_at_end_of_turn() {
9470 let mut state = State::default();
9471 state.side_one.get_active().hp = 50;
9472 state.side_one.get_active().item = Items::LEFTOVERS;
9473
9474 let mut incoming_instructions = StateInstructions::default();
9475 add_end_of_turn_instructions(
9476 &mut state,
9477 &mut incoming_instructions,
9478 &SideReference::SideOne,
9479 );
9480
9481 let expected_instructions = StateInstructions {
9482 percentage: 100.0,
9483 instruction_list: vec![Instruction::Heal(HealInstruction {
9484 side_ref: SideReference::SideOne,
9485 heal_amount: 6,
9486 })],
9487 };
9488
9489 assert_eq!(expected_instructions, incoming_instructions)
9490 }
9491
9492 #[test]
9493 fn test_leftovers_does_not_overheal() {
9494 let mut state = State::default();
9495 state.side_one.get_active().hp = 99;
9496 state.side_one.get_active().item = Items::LEFTOVERS;
9497
9498 let mut incoming_instructions = StateInstructions::default();
9499 add_end_of_turn_instructions(
9500 &mut state,
9501 &mut incoming_instructions,
9502 &SideReference::SideOne,
9503 );
9504
9505 let expected_instructions = StateInstructions {
9506 percentage: 100.0,
9507 instruction_list: vec![Instruction::Heal(HealInstruction {
9508 side_ref: SideReference::SideOne,
9509 heal_amount: 1,
9510 })],
9511 };
9512
9513 assert_eq!(expected_instructions, incoming_instructions)
9514 }
9515
9516 #[test]
9517 fn test_leftovers_generates_no_instruction_at_maxhp() {
9518 let mut state = State::default();
9519 state.side_one.get_active().hp = 100;
9520 state.side_one.get_active().item = Items::LEFTOVERS;
9521
9522 let mut incoming_instructions = StateInstructions::default();
9523 add_end_of_turn_instructions(
9524 &mut state,
9525 &mut incoming_instructions,
9526 &SideReference::SideOne,
9527 );
9528
9529 let expected_instructions = StateInstructions {
9530 percentage: 100.0,
9531 instruction_list: vec![],
9532 };
9533
9534 assert_eq!(expected_instructions, incoming_instructions)
9535 }
9536
9537 #[test]
9538 fn test_leftovers_generates_no_instruction_when_fainted() {
9539 let mut state = State::default();
9540 state.side_one.get_active().hp = 0;
9541 state.side_one.get_active().item = Items::LEFTOVERS;
9542
9543 let mut incoming_instructions = StateInstructions::default();
9544 add_end_of_turn_instructions(
9545 &mut state,
9546 &mut incoming_instructions,
9547 &SideReference::SideOne,
9548 );
9549
9550 let expected_instructions = StateInstructions {
9551 percentage: 100.0,
9552 instruction_list: vec![],
9553 };
9554
9555 assert_eq!(expected_instructions, incoming_instructions)
9556 }
9557
9558 #[test]
9559 fn test_blacksludge_heal_as_poison_type() {
9560 let mut state = State::default();
9561 state.side_one.get_active().hp = 50;
9562 state.side_one.get_active().item = Items::BLACKSLUDGE;
9563 state.side_one.get_active().types.0 = PokemonType::POISON;
9564
9565 let mut incoming_instructions = StateInstructions::default();
9566 add_end_of_turn_instructions(
9567 &mut state,
9568 &mut incoming_instructions,
9569 &SideReference::SideOne,
9570 );
9571
9572 let expected_instructions = StateInstructions {
9573 percentage: 100.0,
9574 instruction_list: vec![Instruction::Heal(HealInstruction {
9575 side_ref: SideReference::SideOne,
9576 heal_amount: 6,
9577 })],
9578 };
9579
9580 assert_eq!(expected_instructions, incoming_instructions)
9581 }
9582
9583 #[test]
9584 fn test_blacksludge_damage_as_non_poison_type() {
9585 let mut state = State::default();
9586 state.side_one.get_active().hp = 50;
9587 state.side_one.get_active().item = Items::BLACKSLUDGE;
9588
9589 let mut incoming_instructions = StateInstructions::default();
9590 add_end_of_turn_instructions(
9591 &mut state,
9592 &mut incoming_instructions,
9593 &SideReference::SideOne,
9594 );
9595
9596 let expected_instructions = StateInstructions {
9597 percentage: 100.0,
9598 instruction_list: vec![Instruction::Damage(DamageInstruction {
9599 side_ref: SideReference::SideOne,
9600 damage_amount: 6,
9601 })],
9602 };
9603
9604 assert_eq!(expected_instructions, incoming_instructions)
9605 }
9606
9607 #[test]
9608 fn test_blacksludge_does_not_overheal() {
9609 let mut state = State::default();
9610 state.side_one.get_active().hp = 99;
9611 state.side_one.get_active().item = Items::BLACKSLUDGE;
9612 state.side_one.get_active().types.0 = PokemonType::POISON;
9613
9614 let mut incoming_instructions = StateInstructions::default();
9615 add_end_of_turn_instructions(
9616 &mut state,
9617 &mut incoming_instructions,
9618 &SideReference::SideOne,
9619 );
9620
9621 let expected_instructions = StateInstructions {
9622 percentage: 100.0,
9623 instruction_list: vec![Instruction::Heal(HealInstruction {
9624 side_ref: SideReference::SideOne,
9625 heal_amount: 1,
9626 })],
9627 };
9628
9629 assert_eq!(expected_instructions, incoming_instructions)
9630 }
9631
9632 #[test]
9633 fn test_flameorb_end_of_turn_burn() {
9634 let mut state = State::default();
9635 state.side_one.get_active().item = Items::FLAMEORB;
9636
9637 let mut incoming_instructions = StateInstructions::default();
9638 add_end_of_turn_instructions(
9639 &mut state,
9640 &mut incoming_instructions,
9641 &SideReference::SideOne,
9642 );
9643
9644 let expected_instructions = StateInstructions {
9645 percentage: 100.0,
9646 instruction_list: vec![Instruction::ChangeStatus(ChangeStatusInstruction {
9647 side_ref: SideReference::SideOne,
9648 pokemon_index: PokemonIndex::P0,
9649 old_status: PokemonStatus::NONE,
9650 new_status: PokemonStatus::BURN,
9651 })],
9652 };
9653
9654 assert_eq!(expected_instructions, incoming_instructions)
9655 }
9656
9657 #[test]
9658 fn test_fire_type_cannot_be_burned_by_flameorb() {
9659 let mut state = State::default();
9660 state.side_one.get_active().item = Items::FLAMEORB;
9661 state.side_one.get_active().types.0 = PokemonType::FIRE;
9662 let mut incoming_instructions = StateInstructions::default();
9663 add_end_of_turn_instructions(
9664 &mut state,
9665 &mut incoming_instructions,
9666 &SideReference::SideOne,
9667 );
9668
9669 let expected_instructions = StateInstructions {
9670 percentage: 100.0,
9671 instruction_list: vec![],
9672 };
9673
9674 assert_eq!(expected_instructions, incoming_instructions)
9675 }
9676
9677 #[test]
9678 fn test_toxicorb_applies_status() {
9679 let mut state = State::default();
9680 state.side_one.get_active().item = Items::TOXICORB;
9681
9682 let mut incoming_instructions = StateInstructions::default();
9683 add_end_of_turn_instructions(
9684 &mut state,
9685 &mut incoming_instructions,
9686 &SideReference::SideOne,
9687 );
9688
9689 let expected_instructions = StateInstructions {
9690 percentage: 100.0,
9691 instruction_list: vec![Instruction::ChangeStatus(ChangeStatusInstruction {
9692 side_ref: SideReference::SideOne,
9693 pokemon_index: PokemonIndex::P0,
9694 old_status: PokemonStatus::NONE,
9695 new_status: PokemonStatus::TOXIC,
9696 })],
9697 };
9698
9699 assert_eq!(expected_instructions, incoming_instructions)
9700 }
9701
9702 #[test]
9703 fn test_toxicorb_does_not_apply_to_poison_type() {
9704 let mut state = State::default();
9705 state.side_one.get_active().item = Items::TOXICORB;
9706 state.side_one.get_active().types.0 = PokemonType::POISON;
9707
9708 let mut incoming_instructions = StateInstructions::default();
9709 add_end_of_turn_instructions(
9710 &mut state,
9711 &mut incoming_instructions,
9712 &SideReference::SideOne,
9713 );
9714
9715 let expected_instructions = StateInstructions {
9716 percentage: 100.0,
9717 instruction_list: vec![],
9718 };
9719
9720 assert_eq!(expected_instructions, incoming_instructions)
9721 }
9722
9723 #[test]
9724 fn test_poisonheal_heals_at_end_of_turn() {
9725 let mut state = State::default();
9726 state.side_one.get_active().ability = Abilities::POISONHEAL;
9727 state.side_one.get_active().status = PokemonStatus::POISON;
9728 state.side_one.get_active().hp = 50;
9729
9730 let mut incoming_instructions = StateInstructions::default();
9731 add_end_of_turn_instructions(
9732 &mut state,
9733 &mut incoming_instructions,
9734 &SideReference::SideOne,
9735 );
9736
9737 let expected_instructions = StateInstructions {
9738 percentage: 100.0,
9739 instruction_list: vec![Instruction::Heal(HealInstruction {
9740 side_ref: SideReference::SideOne,
9741 heal_amount: 12,
9742 })],
9743 };
9744
9745 assert_eq!(expected_instructions, incoming_instructions)
9746 }
9747
9748 #[test]
9749 fn test_poisonheal_while_toxiced_still_increases_toxic_count() {
9750 let mut state = State::default();
9751 state.side_one.get_active().ability = Abilities::POISONHEAL;
9752 state.side_one.get_active().status = PokemonStatus::TOXIC;
9753 state.side_one.get_active().hp = 50;
9754
9755 let mut incoming_instructions = StateInstructions::default();
9756 add_end_of_turn_instructions(
9757 &mut state,
9758 &mut incoming_instructions,
9759 &SideReference::SideOne,
9760 );
9761
9762 let expected_instructions = StateInstructions {
9763 percentage: 100.0,
9764 instruction_list: vec![
9765 Instruction::ChangeSideCondition(ChangeSideConditionInstruction {
9766 side_ref: SideReference::SideOne,
9767 side_condition: PokemonSideCondition::ToxicCount,
9768 amount: 1,
9769 }),
9770 Instruction::Heal(HealInstruction {
9771 side_ref: SideReference::SideOne,
9772 heal_amount: 12,
9773 }),
9774 ],
9775 };
9776
9777 assert_eq!(expected_instructions, incoming_instructions)
9778 }
9779
9780 #[test]
9781 fn test_poisonheal_does_not_overheal() {
9782 let mut state = State::default();
9783 state.side_one.get_active().ability = Abilities::POISONHEAL;
9784 state.side_one.get_active().status = PokemonStatus::POISON;
9785 state.side_one.get_active().hp = 99;
9786
9787 let mut incoming_instructions = StateInstructions::default();
9788 add_end_of_turn_instructions(
9789 &mut state,
9790 &mut incoming_instructions,
9791 &SideReference::SideOne,
9792 );
9793
9794 let expected_instructions = StateInstructions {
9795 percentage: 100.0,
9796 instruction_list: vec![Instruction::Heal(HealInstruction {
9797 side_ref: SideReference::SideOne,
9798 heal_amount: 1,
9799 })],
9800 };
9801
9802 assert_eq!(expected_instructions, incoming_instructions)
9803 }
9804
9805 #[test]
9806 fn test_poisonheal_does_nothing_at_maxhp() {
9807 let mut state = State::default();
9808 state.side_one.get_active().ability = Abilities::POISONHEAL;
9809 state.side_one.get_active().status = PokemonStatus::POISON;
9810
9811 let mut incoming_instructions = StateInstructions::default();
9812 add_end_of_turn_instructions(
9813 &mut state,
9814 &mut incoming_instructions,
9815 &SideReference::SideOne,
9816 );
9817
9818 let expected_instructions = StateInstructions {
9819 percentage: 100.0,
9820 instruction_list: vec![],
9821 };
9822
9823 assert_eq!(expected_instructions, incoming_instructions)
9824 }
9825
9826 #[test]
9827 fn test_speedboost() {
9828 let mut state = State::default();
9829 state.side_one.get_active().ability = Abilities::SPEEDBOOST;
9830
9831 let mut incoming_instructions = StateInstructions::default();
9832 add_end_of_turn_instructions(
9833 &mut state,
9834 &mut incoming_instructions,
9835 &SideReference::SideOne,
9836 );
9837
9838 let expected_instructions = StateInstructions {
9839 percentage: 100.0,
9840 instruction_list: vec![Instruction::Boost(BoostInstruction {
9841 side_ref: SideReference::SideOne,
9842 stat: PokemonBoostableStat::Speed,
9843 amount: 1,
9844 })],
9845 };
9846
9847 assert_eq!(expected_instructions, incoming_instructions)
9848 }
9849
9850 #[test]
9851 fn test_speedboost_does_not_boost_beyond_6() {
9852 let mut state = State::default();
9853 state.side_one.get_active().ability = Abilities::SPEEDBOOST;
9854 state.side_one.speed_boost = 6;
9855
9856 let mut incoming_instructions = StateInstructions::default();
9857 add_end_of_turn_instructions(
9858 &mut state,
9859 &mut incoming_instructions,
9860 &SideReference::SideOne,
9861 );
9862
9863 let expected_instructions = StateInstructions {
9864 percentage: 100.0,
9865 instruction_list: vec![],
9866 };
9867
9868 assert_eq!(expected_instructions, incoming_instructions)
9869 }
9870
9871 #[test]
9872 fn test_end_of_turn_poison_damage() {
9873 let mut state = State::default();
9874 state.side_one.get_active().status = PokemonStatus::POISON;
9875
9876 let mut incoming_instructions = StateInstructions::default();
9877 add_end_of_turn_instructions(
9878 &mut state,
9879 &mut incoming_instructions,
9880 &SideReference::SideOne,
9881 );
9882
9883 let expected_instructions = StateInstructions {
9884 percentage: 100.0,
9885 instruction_list: vec![Instruction::Damage(DamageInstruction {
9886 side_ref: SideReference::SideOne,
9887 damage_amount: 12,
9888 })],
9889 };
9890
9891 assert_eq!(expected_instructions, incoming_instructions)
9892 }
9893
9894 #[test]
9895 fn test_poison_damage_does_not_overkill() {
9896 let mut state = State::default();
9897 state.side_one.get_active().status = PokemonStatus::POISON;
9898 state.side_one.get_active().hp = 5;
9899
9900 let mut incoming_instructions = StateInstructions::default();
9901 add_end_of_turn_instructions(
9902 &mut state,
9903 &mut incoming_instructions,
9904 &SideReference::SideOne,
9905 );
9906
9907 let expected_instructions = StateInstructions {
9908 percentage: 100.0,
9909 instruction_list: vec![Instruction::Damage(DamageInstruction {
9910 side_ref: SideReference::SideOne,
9911 damage_amount: 5,
9912 })],
9913 };
9914
9915 assert_eq!(expected_instructions, incoming_instructions)
9916 }
9917
9918 #[test]
9919 #[cfg(any(feature = "gen9", feature = "gen8", feature = "gen7"))]
9920 fn test_end_of_turn_burn_damage() {
9921 let mut state = State::default();
9922 state.side_one.get_active().status = PokemonStatus::BURN;
9923
9924 let mut incoming_instructions = StateInstructions::default();
9925 add_end_of_turn_instructions(
9926 &mut state,
9927 &mut incoming_instructions,
9928 &SideReference::SideOne,
9929 );
9930
9931 let expected_instructions = StateInstructions {
9932 percentage: 100.0,
9933 instruction_list: vec![Instruction::Damage(DamageInstruction {
9934 side_ref: SideReference::SideOne,
9935 damage_amount: 6,
9936 })],
9937 };
9938
9939 assert_eq!(expected_instructions, incoming_instructions)
9940 }
9941
9942 #[test]
9943 #[cfg(any(feature = "gen3", feature = "gen4", feature = "gen5", feature = "gen6"))]
9944 fn test_early_generation_burn_one_eigth() {
9945 let mut state = State::default();
9946 state.side_one.get_active().status = PokemonStatus::BURN;
9947
9948 let mut incoming_instructions = StateInstructions::default();
9949 add_end_of_turn_instructions(
9950 &mut state,
9951 &mut incoming_instructions,
9952 &SideReference::SideOne,
9953 );
9954
9955 let expected_instructions = StateInstructions {
9956 percentage: 100.0,
9957 instruction_list: vec![Instruction::Damage(DamageInstruction {
9958 side_ref: SideReference::SideOne,
9959 damage_amount: 12,
9960 })],
9961 };
9962
9963 assert_eq!(expected_instructions, incoming_instructions)
9964 }
9965
9966 #[test]
9967 fn test_burn_damage_does_not_overkill() {
9968 let mut state = State::default();
9969 state.side_one.get_active().status = PokemonStatus::BURN;
9970 state.side_one.get_active().hp = 5;
9971
9972 let mut incoming_instructions = StateInstructions::default();
9973 add_end_of_turn_instructions(
9974 &mut state,
9975 &mut incoming_instructions,
9976 &SideReference::SideOne,
9977 );
9978
9979 let expected_instructions = StateInstructions {
9980 percentage: 100.0,
9981 instruction_list: vec![Instruction::Damage(DamageInstruction {
9982 side_ref: SideReference::SideOne,
9983 damage_amount: 5,
9984 })],
9985 };
9986
9987 assert_eq!(expected_instructions, incoming_instructions)
9988 }
9989
9990 #[test]
9991 fn test_burn_damage_ignored_if_has_magicguard() {
9992 let mut state = State::default();
9993 state.side_one.get_active().status = PokemonStatus::BURN;
9994 state.side_one.get_active().ability = Abilities::MAGICGUARD;
9995 state.side_one.get_active().hp = 5;
9996
9997 let mut incoming_instructions = StateInstructions::default();
9998 add_end_of_turn_instructions(
9999 &mut state,
10000 &mut incoming_instructions,
10001 &SideReference::SideOne,
10002 );
10003
10004 let expected_instructions = StateInstructions {
10005 percentage: 100.0,
10006 instruction_list: vec![],
10007 };
10008
10009 assert_eq!(expected_instructions, incoming_instructions)
10010 }
10011
10012 #[test]
10013 fn test_first_toxic_damage() {
10014 let mut state = State::default();
10015 state.side_one.get_active().status = PokemonStatus::TOXIC;
10016
10017 let mut incoming_instructions = StateInstructions::default();
10018 add_end_of_turn_instructions(
10019 &mut state,
10020 &mut incoming_instructions,
10021 &SideReference::SideOne,
10022 );
10023
10024 let expected_instructions = StateInstructions {
10025 percentage: 100.0,
10026 instruction_list: vec![
10027 Instruction::Damage(DamageInstruction {
10028 side_ref: SideReference::SideOne,
10029 damage_amount: 6,
10030 }),
10031 Instruction::ChangeSideCondition(ChangeSideConditionInstruction {
10032 side_ref: SideReference::SideOne,
10033 side_condition: PokemonSideCondition::ToxicCount,
10034 amount: 1,
10035 }),
10036 ],
10037 };
10038
10039 assert_eq!(expected_instructions, incoming_instructions)
10040 }
10041
10042 #[test]
10043 fn test_leechseed_sap() {
10044 let mut state = State::default();
10045 state
10046 .side_one
10047 .volatile_statuses
10048 .insert(PokemonVolatileStatus::LEECHSEED);
10049 state.side_one.get_active().hp = 50;
10050 state.side_two.get_active().hp = 50;
10051
10052 let mut incoming_instructions = StateInstructions::default();
10053 add_end_of_turn_instructions(
10054 &mut state,
10055 &mut incoming_instructions,
10056 &SideReference::SideOne,
10057 );
10058
10059 let expected_instructions = StateInstructions {
10060 percentage: 100.0,
10061 instruction_list: vec![
10062 Instruction::Damage(DamageInstruction {
10063 side_ref: SideReference::SideOne,
10064 damage_amount: 12,
10065 }),
10066 Instruction::Heal(HealInstruction {
10067 side_ref: SideReference::SideTwo,
10068 heal_amount: 12,
10069 }),
10070 ],
10071 };
10072
10073 assert_eq!(expected_instructions, incoming_instructions)
10074 }
10075
10076 #[test]
10077 fn test_leechseed_sap_does_not_heal_if_receiving_side_is_maxhp() {
10078 let mut state = State::default();
10079 state
10080 .side_one
10081 .volatile_statuses
10082 .insert(PokemonVolatileStatus::LEECHSEED);
10083 state.side_one.get_active().hp = 50;
10084 state.side_two.get_active().hp = 100;
10085
10086 let mut incoming_instructions = StateInstructions::default();
10087 add_end_of_turn_instructions(
10088 &mut state,
10089 &mut incoming_instructions,
10090 &SideReference::SideOne,
10091 );
10092
10093 let expected_instructions = StateInstructions {
10094 percentage: 100.0,
10095 instruction_list: vec![Instruction::Damage(DamageInstruction {
10096 side_ref: SideReference::SideOne,
10097 damage_amount: 12,
10098 })],
10099 };
10100
10101 assert_eq!(expected_instructions, incoming_instructions)
10102 }
10103
10104 #[test]
10105 fn test_leechseed_sap_does_not_overkill() {
10106 let mut state = State::default();
10107 state
10108 .side_one
10109 .volatile_statuses
10110 .insert(PokemonVolatileStatus::LEECHSEED);
10111 state.side_one.get_active().hp = 5;
10112 state.side_two.get_active().hp = 50;
10113
10114 let mut incoming_instructions = StateInstructions::default();
10115 add_end_of_turn_instructions(
10116 &mut state,
10117 &mut incoming_instructions,
10118 &SideReference::SideOne,
10119 );
10120
10121 let expected_instructions = StateInstructions {
10122 percentage: 100.0,
10123 instruction_list: vec![
10124 Instruction::Damage(DamageInstruction {
10125 side_ref: SideReference::SideOne,
10126 damage_amount: 5,
10127 }),
10128 Instruction::Heal(HealInstruction {
10129 side_ref: SideReference::SideTwo,
10130 heal_amount: 5,
10131 }),
10132 ],
10133 };
10134
10135 assert_eq!(expected_instructions, incoming_instructions)
10136 }
10137
10138 #[test]
10139 fn test_leechseed_sap_does_not_overheal() {
10140 let mut state = State::default();
10141 state
10142 .side_one
10143 .volatile_statuses
10144 .insert(PokemonVolatileStatus::LEECHSEED);
10145 state.side_one.get_active().hp = 50;
10146 state.side_two.get_active().hp = 95;
10147
10148 let mut incoming_instructions = StateInstructions::default();
10149 add_end_of_turn_instructions(
10150 &mut state,
10151 &mut incoming_instructions,
10152 &SideReference::SideOne,
10153 );
10154
10155 let expected_instructions = StateInstructions {
10156 percentage: 100.0,
10157 instruction_list: vec![
10158 Instruction::Damage(DamageInstruction {
10159 side_ref: SideReference::SideOne,
10160 damage_amount: 12,
10161 }),
10162 Instruction::Heal(HealInstruction {
10163 side_ref: SideReference::SideTwo,
10164 heal_amount: 5,
10165 }),
10166 ],
10167 };
10168
10169 assert_eq!(expected_instructions, incoming_instructions)
10170 }
10171
10172 #[test]
10173 fn test_protect_volatile_being_removed() {
10174 let mut state = State::default();
10175 state
10176 .side_one
10177 .volatile_statuses
10178 .insert(PokemonVolatileStatus::PROTECT);
10179
10180 let mut incoming_instructions = StateInstructions::default();
10181 add_end_of_turn_instructions(
10182 &mut state,
10183 &mut incoming_instructions,
10184 &SideReference::SideOne,
10185 );
10186
10187 let expected_instructions = StateInstructions {
10188 percentage: 100.0,
10189 instruction_list: vec![
10190 Instruction::RemoveVolatileStatus(RemoveVolatileStatusInstruction {
10191 side_ref: SideReference::SideOne,
10192 volatile_status: PokemonVolatileStatus::PROTECT,
10193 }),
10194 Instruction::ChangeSideCondition(ChangeSideConditionInstruction {
10195 side_ref: SideReference::SideOne,
10196 side_condition: PokemonSideCondition::Protect,
10197 amount: 1,
10198 }),
10199 ],
10200 };
10201
10202 assert_eq!(expected_instructions, incoming_instructions)
10203 }
10204
10205 #[test]
10206 fn test_protect_side_condition_being_removed() {
10207 let mut state = State::default();
10208 state.side_one.side_conditions.protect = 2;
10209
10210 let mut incoming_instructions = StateInstructions::default();
10211 add_end_of_turn_instructions(
10212 &mut state,
10213 &mut incoming_instructions,
10214 &SideReference::SideOne,
10215 );
10216
10217 let expected_instructions = StateInstructions {
10218 percentage: 100.0,
10219 instruction_list: vec![Instruction::ChangeSideCondition(
10220 ChangeSideConditionInstruction {
10221 side_ref: SideReference::SideOne,
10222 side_condition: PokemonSideCondition::Protect,
10223 amount: -2,
10224 },
10225 )],
10226 };
10227
10228 assert_eq!(expected_instructions, incoming_instructions)
10229 }
10230
10231 #[test]
10232 fn test_roost_vs_removal() {
10233 let mut state = State::default();
10234 state
10235 .side_one
10236 .volatile_statuses
10237 .insert(PokemonVolatileStatus::ROOST);
10238
10239 let mut incoming_instructions = StateInstructions::default();
10240 add_end_of_turn_instructions(
10241 &mut state,
10242 &mut incoming_instructions,
10243 &SideReference::SideOne,
10244 );
10245
10246 let expected_instructions = StateInstructions {
10247 percentage: 100.0,
10248 instruction_list: vec![Instruction::RemoveVolatileStatus(
10249 RemoveVolatileStatusInstruction {
10250 side_ref: SideReference::SideOne,
10251 volatile_status: PokemonVolatileStatus::ROOST,
10252 },
10253 )],
10254 };
10255
10256 assert_eq!(expected_instructions, incoming_instructions)
10257 }
10258
10259 #[test]
10260 fn test_partiallytrapped_damage() {
10261 let mut state = State::default();
10262 state
10263 .side_one
10264 .volatile_statuses
10265 .insert(PokemonVolatileStatus::PARTIALLYTRAPPED);
10266
10267 let mut incoming_instructions = StateInstructions::default();
10268 add_end_of_turn_instructions(
10269 &mut state,
10270 &mut incoming_instructions,
10271 &SideReference::SideOne,
10272 );
10273
10274 #[cfg(any(feature = "gen3", feature = "gen4", feature = "gen5"))]
10275 let expected_instructions = StateInstructions {
10276 percentage: 100.0,
10277 instruction_list: vec![Instruction::Damage(DamageInstruction {
10278 side_ref: SideReference::SideOne,
10279 damage_amount: 6,
10280 })],
10281 };
10282
10283 #[cfg(any(feature = "gen6", feature = "gen7", feature = "gen8", feature = "gen9"))]
10284 let expected_instructions = StateInstructions {
10285 percentage: 100.0,
10286 instruction_list: vec![Instruction::Damage(DamageInstruction {
10287 side_ref: SideReference::SideOne,
10288 damage_amount: 12,
10289 })],
10290 };
10291
10292 assert_eq!(expected_instructions, incoming_instructions)
10293 }
10294
10295 #[test]
10296 fn test_saltcure_on_water_type_damage() {
10297 let mut state = State::default();
10298 state.side_one.get_active().types.0 = PokemonType::WATER;
10299 state
10300 .side_one
10301 .volatile_statuses
10302 .insert(PokemonVolatileStatus::SALTCURE);
10303
10304 let mut incoming_instructions = StateInstructions::default();
10305 add_end_of_turn_instructions(
10306 &mut state,
10307 &mut incoming_instructions,
10308 &SideReference::SideOne,
10309 );
10310
10311 let expected_instructions = StateInstructions {
10312 percentage: 100.0,
10313 instruction_list: vec![Instruction::Damage(DamageInstruction {
10314 side_ref: SideReference::SideOne,
10315 damage_amount: 25,
10316 })],
10317 };
10318
10319 assert_eq!(expected_instructions, incoming_instructions)
10320 }
10321
10322 #[test]
10323 fn test_chance_to_wake_up_with_no_turns_asleep_is_0() {
10324 assert_eq!(0.0, chance_to_wake_up(0));
10325 }
10326
10327 #[test]
10328 #[cfg(any(feature = "gen4"))]
10329 fn test_gen4_25_percent_to_wake_after_1_sleep_turn() {
10330 assert_eq!(0.25, chance_to_wake_up(1));
10331 }
10332
10333 #[test]
10334 #[cfg(any(feature = "gen4"))]
10335 fn test_gen4_100_percent_to_wake_after_4_sleep_turn() {
10336 assert_eq!(1.0, chance_to_wake_up(4));
10337 }
10338}