diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 7e9e0549c0..7007410e15 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -2357,6 +2357,9 @@ BattleScript_TryTailwindAbilitiesLoop_WindPower: BattleScript_EffectMiracleEye:: attackcanceler accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE + jumpifgenconfiglowerthan CONFIG_MIRACLE_EYE_FAIL, GEN_5, BattleScript_MiracleEyeSet + jumpifvolatile BS_TARGET, VOLATILE_MIRACLE_EYE, BattleScript_ButItFailed +BattleScript_MiracleEyeSet: setvolatile BS_TARGET, VOLATILE_MIRACLE_EYE goto BattleScript_IdentifiedFoe @@ -3479,7 +3482,11 @@ BattleScript_EffectSpikes:: BattleScript_EffectForesight:: attackcanceler accuracycheck BattleScript_ButItFailed, NO_ACC_CALC_CHECK_LOCK_ON + jumpifgenconfiglowerthan CONFIG_FORESIGHT_FAIL, GEN_3, BattleScript_ForesightFailCheck + jumpifgenconfiglowerthan CONFIG_FORESIGHT_FAIL, GEN_5, BattleScript_ForesightSet +BattleScript_ForesightFailCheck: jumpifvolatile BS_TARGET, VOLATILE_FORESIGHT, BattleScript_ButItFailed +BattleScript_ForesightSet: setvolatile BS_TARGET, VOLATILE_FORESIGHT BattleScript_IdentifiedFoe: attackanimation diff --git a/include/config/battle.h b/include/config/battle.h index 874817a5ab..9d2814cb85 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -128,6 +128,8 @@ #define B_AFTER_YOU_TURN_ORDER GEN_LATEST // In Gen8+, After You doesn't fail if the turn order wouldn't change after use. #define B_QUASH_TURN_ORDER GEN_LATEST // In Gen8+, Quash-affected battlers move according to speed order. Before Gen8, Quash-affected battlers move in the order they were affected by Quash. #define B_DESTINY_BOND_FAIL GEN_LATEST // In Gen7+, Destiny Bond fails if used repeatedly. +#define B_FORESIGHT_FAIL GEN_LATEST // In Gen2 and Gen5+, Foresight fails if used against a target already under its effect. +#define B_MIRACLE_EYE_FAIL GEN_LATEST // In Gen5+, Miracle Eye fails if used against a target already under its effect. #define B_PURSUIT_TARGET GEN_LATEST // In Gen4+, Pursuit attacks a switching opponent even if they weren't targeting them. Before Gen4, Pursuit only attacks a switching opponent that it originally targeted. #define B_SKIP_RECHARGE GEN_LATEST // In Gen1, recharging moves such as Hyper Beam skip the recharge if the target gets KO'd #define B_ENCORE_TARGET GEN_LATEST // In Gen5+, encored moves are allowed to choose a target diff --git a/include/constants/generational_changes.h b/include/constants/generational_changes.h index f8ef227c4a..ad700a6ea2 100644 --- a/include/constants/generational_changes.h +++ b/include/constants/generational_changes.h @@ -119,6 +119,8 @@ F(AFTER_YOU_TURN_ORDER, afterYouTurnOrder, (u32, GEN_COUNT - 1)) \ F(QUASH_TURN_ORDER, quashTurnOrder, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ F(DESTINY_BOND_FAIL, destinyBondFail, (u32, GEN_COUNT - 1)) \ + F(FORESIGHT_FAIL, foresightFail, (u32, GEN_COUNT - 1)) \ + F(MIRACLE_EYE_FAIL, miracleEyeFail, (u32, GEN_COUNT - 1)) \ F(PURSUIT_TARGET, pursuitTarget, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ F(SKIP_RECHARGE, skipRecharge, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ F(ENCORE_TARGET, encoreTarget, (u32, GEN_COUNT - 1)) \ diff --git a/test/battle/move_effect/foresight.c b/test/battle/move_effect/foresight.c index 59209030d0..9879774bab 100644 --- a/test/battle/move_effect/foresight.c +++ b/test/battle/move_effect/foresight.c @@ -1,14 +1,123 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("Foresight removes Ghost's type immunity to Normal and Fighting types") +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_FORESIGHT) == EFFECT_FORESIGHT); +} + +SINGLE_BATTLE_TEST("Foresight removes Ghost's type immunity to Normal and Fighting types") +{ + GIVEN { + ASSUME(GetMoveType(MOVE_SCRATCH) == TYPE_NORMAL); + ASSUME(GetMoveType(MOVE_LOW_KICK) == TYPE_FIGHTING); + ASSUME(GetSpeciesType(SPECIES_GENGAR, 0) == TYPE_GHOST); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_FORESIGHT, MOVE_SCRATCH, MOVE_LOW_KICK); } + OPPONENT(SPECIES_GENGAR) { Moves(MOVE_SPLASH); } + } WHEN { + TURN { MOVE(player, MOVE_FORESIGHT); MOVE(opponent, MOVE_SPLASH); } + TURN { MOVE(player, MOVE_SCRATCH); MOVE(opponent, MOVE_SPLASH); } + TURN { MOVE(player, MOVE_LOW_KICK); MOVE(opponent, MOVE_SPLASH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); + HP_BAR(opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_LOW_KICK, player); + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Foresight always hits unless the target is semi-invulnerable") +{ + bool32 semiInvulnerable = FALSE; + PARAMETRIZE { semiInvulnerable = FALSE; } + PARAMETRIZE { semiInvulnerable = TRUE; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_DOUBLE_TEAM) == EFFECT_EVASION_UP); + ASSUME(GetMoveEffect(MOVE_FLY) == EFFECT_SEMI_INVULNERABLE); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_FORESIGHT, MOVE_SPLASH); Speed(10); } + OPPONENT(SPECIES_SQUAWKABILLY) { Moves(MOVE_DOUBLE_TEAM, MOVE_FLY); Speed(20); } + } WHEN { + if (semiInvulnerable) + TURN { MOVE(player, MOVE_FORESIGHT); MOVE(opponent, MOVE_FLY); } + else + TURN { MOVE(player, MOVE_FORESIGHT); MOVE(opponent, MOVE_DOUBLE_TEAM); } + + if (semiInvulnerable) + TURN { MOVE(player, MOVE_SPLASH); SKIP_TURN(opponent); } + } SCENE { + if (semiInvulnerable) { + MESSAGE("The opposing Squawkabilly avoided the attack!"); + } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_TEAM, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player); + } + } +} + +SINGLE_BATTLE_TEST("Foresight causes moves against the target to ignore positive evasion stat stages") +{ + PASSES_RANDOMLY(100, 100, RNG_ACCURACY); + GIVEN { + ASSUME(GetMoveEffect(MOVE_DOUBLE_TEAM) == EFFECT_EVASION_UP); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_FORESIGHT, MOVE_SCRATCH); Speed(10); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_DOUBLE_TEAM, MOVE_SPLASH); Speed(20); } + } WHEN { + TURN { MOVE(player, MOVE_FORESIGHT); MOVE(opponent, MOVE_DOUBLE_TEAM); } + TURN { MOVE(player, MOVE_SCRATCH); MOVE(opponent, MOVE_SPLASH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_TEAM, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Foresight fails if the target is already under its effect (Gen 2 and Gen5+)") +{ + u32 genConfig = GEN_2; + PARAMETRIZE { genConfig = GEN_2; } + PARAMETRIZE { genConfig = GEN_5; } + GIVEN { + WITH_CONFIG(CONFIG_FORESIGHT_FAIL, genConfig); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_FORESIGHT); } + TURN { MOVE(player, MOVE_FORESIGHT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player); + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("Foresight doesn't fail if the target is already under its effect (Gen 3-4)") +{ + u32 genConfig = GEN_3; + PARAMETRIZE { genConfig = GEN_3; } + PARAMETRIZE { genConfig = GEN_4; } + GIVEN { + WITH_CONFIG(CONFIG_FORESIGHT_FAIL, genConfig); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_FORESIGHT); } + TURN { MOVE(player, MOVE_FORESIGHT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player); + NOT MESSAGE("But it failed!"); + } +} + TO_DO_BATTLE_TEST("Foresight causes accuracy/evasion stat changes only between the user/target when the user's accuracy stage is less than the target's evasion stage (Gen 2)") TO_DO_BATTLE_TEST("Foresight causes all moves against the target to ignore evasion stat changes (Gen 3)") TO_DO_BATTLE_TEST("Foresight causes all moves against the target to ignore only positive evasion stat changes (Gen 4+)") // Eg. Doesn't ignore Sweet Scent TO_DO_BATTLE_TEST("Foresight doesn't cause moves used against the target to always hit (Gen 2-3)") TO_DO_BATTLE_TEST("Foresight causes moves used against the target to always hit (Gen 4+)") -TO_DO_BATTLE_TEST("Foresight does not make moves hit semi-invulnerable targets") -TO_DO_BATTLE_TEST("Foresight fails if the target is already under its effect (Gen 2 and Gen5+)") -TO_DO_BATTLE_TEST("Foresight doesn't fail if the target is already under its effect (Gen 3-4)") TO_DO_BATTLE_TEST("Baton Pass passes Foresight's effect (Gen 2)"); TO_DO_BATTLE_TEST("Baton Pass doesn't pass Foresight's effect (Gen 3+)"); diff --git a/test/battle/move_effect/miracle_eye.c b/test/battle/move_effect/miracle_eye.c index 784323f1cc..4f819036a5 100644 --- a/test/battle/move_effect/miracle_eye.c +++ b/test/battle/move_effect/miracle_eye.c @@ -1,4 +1,106 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("TODO: Write Miracle Eye (Move Effect) test titles") +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_MIRACLE_EYE) == EFFECT_MIRACLE_EYE); +} + +SINGLE_BATTLE_TEST("Miracle Eye removes Dark-type immunity to Psychic-type moves") +{ + GIVEN { + ASSUME(GetMoveType(MOVE_PSYCHIC) == TYPE_PSYCHIC); + ASSUME(GetSpeciesType(SPECIES_UMBREON, 0) == TYPE_DARK); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_MIRACLE_EYE, MOVE_PSYCHIC); } + OPPONENT(SPECIES_UMBREON) { Moves(MOVE_SPLASH); } + } WHEN { + TURN { MOVE(player, MOVE_PSYCHIC); MOVE(opponent, MOVE_SPLASH); } + TURN { MOVE(player, MOVE_MIRACLE_EYE); MOVE(opponent, MOVE_SPLASH); } + TURN { MOVE(player, MOVE_PSYCHIC); MOVE(opponent, MOVE_SPLASH); } + } SCENE { + NOT HP_BAR(opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player); + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Miracle Eye always hits unless the target is semi-invulnerable") +{ + bool32 semiInvulnerable = FALSE; + PARAMETRIZE { semiInvulnerable = FALSE; } + PARAMETRIZE { semiInvulnerable = TRUE; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_DOUBLE_TEAM) == EFFECT_EVASION_UP); + ASSUME(GetMoveEffect(MOVE_FLY) == EFFECT_SEMI_INVULNERABLE); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_MIRACLE_EYE, MOVE_SPLASH); Speed(10); } + OPPONENT(SPECIES_SQUAWKABILLY) { Moves(MOVE_DOUBLE_TEAM, MOVE_FLY); Speed(20); } + } WHEN { + if (semiInvulnerable) + TURN { MOVE(player, MOVE_MIRACLE_EYE); MOVE(opponent, MOVE_FLY); } + else + TURN { MOVE(player, MOVE_MIRACLE_EYE); MOVE(opponent, MOVE_DOUBLE_TEAM); } + + if (semiInvulnerable) + TURN { MOVE(player, MOVE_SPLASH); SKIP_TURN(opponent); } + } SCENE { + if (semiInvulnerable) { + MESSAGE("Wobbuffet's attack missed!"); + } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_TEAM, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player); + } + } +} + +SINGLE_BATTLE_TEST("Miracle Eye causes moves against the target to ignore positive evasion stat stages") +{ + PASSES_RANDOMLY(100, 100, RNG_ACCURACY); + GIVEN { + ASSUME(GetMoveEffect(MOVE_DOUBLE_TEAM) == EFFECT_EVASION_UP); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_MIRACLE_EYE, MOVE_SCRATCH); Speed(10); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_DOUBLE_TEAM, MOVE_SPLASH); Speed(20); } + } WHEN { + TURN { MOVE(player, MOVE_MIRACLE_EYE); MOVE(opponent, MOVE_DOUBLE_TEAM); } + TURN { MOVE(player, MOVE_SCRATCH); MOVE(opponent, MOVE_SPLASH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_TEAM, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Miracle Eye fails if the target is already affected by Miracle Eye (Gen5+)") +{ + GIVEN { + WITH_CONFIG(CONFIG_MIRACLE_EYE_FAIL, GEN_5); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_MIRACLE_EYE); } + TURN { MOVE(player, MOVE_MIRACLE_EYE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player); + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("Miracle Eye does not fail if the target is already affected by Miracle Eye (Gen4)") +{ + GIVEN { + WITH_CONFIG(CONFIG_MIRACLE_EYE_FAIL, GEN_4); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_MIRACLE_EYE); } + TURN { MOVE(player, MOVE_MIRACLE_EYE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player); + NOT MESSAGE("But it failed!"); + } +}