diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 12b32c3f1..ae92aba7c 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -7683,11 +7683,14 @@ BattleScript_GrudgeTakesPp:: printstring STRINGID_PKMNLOSTPPGRUDGE waitmessage B_WAIT_TIME_LONG return - + BattleScript_MagicCoatBounce:: attackstring ppreduce pause B_WAIT_TIME_SHORT + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, 0, BattleScript_MagicCoatBounce_Print + call BattleScript_AbilityPopUp +BattleScript_MagicCoatBounce_Print: printfromtable gMagicCoatBounceStringIds waitmessage B_WAIT_TIME_LONG orword gHitMarker, HITMARKER_ATTACKSTRING_PRINTED | HITMARKER_NO_PPDEDUCT | HITMARKER_ALLOW_NO_PP diff --git a/include/battle_util.h b/include/battle_util.h index 1c3f486cd..bf3943a9d 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -64,6 +64,35 @@ struct TypePower u16 effect; }; +enum +{ + CANCELLER_FLAGS, + CANCELLER_SKY_DROP, + CANCELLER_ASLEEP, + CANCELLER_FROZEN, + CANCELLER_TRUANT, + CANCELLER_RECHARGE, + CANCELLER_FLINCH, + CANCELLER_DISABLED, + CANCELLER_GRAVITY, + CANCELLER_HEAL_BLOCKED, + CANCELLER_TAUNTED, + CANCELLER_IMPRISONED, + CANCELLER_CONFUSED, + CANCELLER_PARALYSED, + CANCELLER_IN_LOVE, + CANCELLER_BIDE, + CANCELLER_THAW, + CANCELLER_POWDER_MOVE, + CANCELLER_POWDER_STATUS, + CANCELLER_THROAT_CHOP, + CANCELLER_MULTIHIT_MOVES, + CANCELLER_Z_MOVES, + CANCELLER_END, + CANCELLER_PSYCHIC_TERRAIN, + CANCELLER_END2, +}; + extern const struct TypePower gNaturalGiftTable[]; void HandleAction_ThrowBall(void); diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 57311e671..b5629c2cc 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1530,6 +1530,7 @@ static void Cmd_attackcanceler(void) PressurePPLose(gBattlerAttacker, gBattlerTarget, MOVE_MAGIC_COAT); gProtectStructs[gBattlerTarget].usesBouncedMove = TRUE; gBattleCommunication[MULTISTRING_CHOOSER] = 0; + gBattleStruct->atkCancellerTracker = CANCELLER_POWDER_MOVE; // Edge case for bouncing a powder move against a grass type pokemon. if (BlocksPrankster(gCurrentMove, gBattlerTarget, gBattlerAttacker, TRUE)) { // Opponent used a prankster'd magic coat -> reflected status move should fail against a dark-type attacker @@ -1547,11 +1548,12 @@ static void Cmd_attackcanceler(void) && gBattleMoves[gCurrentMove].flags & FLAG_MAGIC_COAT_AFFECTED && !gProtectStructs[gBattlerAttacker].usesBouncedMove) { - RecordAbilityBattle(gBattlerTarget, ABILITY_MAGIC_BOUNCE); gProtectStructs[gBattlerTarget].usesBouncedMove = TRUE; gBattleCommunication[MULTISTRING_CHOOSER] = 1; + gBattleStruct->atkCancellerTracker = CANCELLER_POWDER_MOVE; // Edge case for bouncing a powder move against a grass type pokemon. BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_MagicCoatBounce; + gBattlerAbility = gBattlerTarget; return; } diff --git a/src/battle_util.c b/src/battle_util.c index 4bded150b..46f8d3a3d 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -3410,35 +3410,6 @@ void TryClearRageAndFuryCutter(void) } } -enum -{ - CANCELLER_FLAGS, - CANCELLER_SKY_DROP, - CANCELLER_ASLEEP, - CANCELLER_FROZEN, - CANCELLER_TRUANT, - CANCELLER_RECHARGE, - CANCELLER_FLINCH, - CANCELLER_DISABLED, - CANCELLER_GRAVITY, - CANCELLER_HEAL_BLOCKED, - CANCELLER_TAUNTED, - CANCELLER_IMPRISONED, - CANCELLER_CONFUSED, - CANCELLER_PARALYSED, - CANCELLER_IN_LOVE, - CANCELLER_BIDE, - CANCELLER_THAW, - CANCELLER_POWDER_MOVE, - CANCELLER_POWDER_STATUS, - CANCELLER_THROAT_CHOP, - CANCELLER_MULTIHIT_MOVES, - CANCELLER_Z_MOVES, - CANCELLER_END, - CANCELLER_PSYCHIC_TERRAIN, - CANCELLER_END2, -}; - u8 AtkCanceller_UnableToUseMove(void) { u8 effect = 0; @@ -3753,7 +3724,10 @@ u8 AtkCanceller_UnableToUseMove(void) if (effect != 0) gBattlescriptCurrInstr = BattleScript_PowderMoveNoEffect; } - gBattleStruct->atkCancellerTracker++; + if (gProtectStructs[gBattlerAttacker].usesBouncedMove) // Edge case for bouncing a powder move against a grass type pokemon. + gBattleStruct->atkCancellerTracker = CANCELLER_END; + else + gBattleStruct->atkCancellerTracker++; break; case CANCELLER_POWDER_STATUS: if (gBattleMons[gBattlerAttacker].status2 & STATUS2_POWDER) diff --git a/test/ability_magic_bounce.c b/test/ability_magic_bounce.c new file mode 100644 index 000000000..d52b93c4a --- /dev/null +++ b/test/ability_magic_bounce.c @@ -0,0 +1,84 @@ +#include "global.h" +#include "test_battle.h" + + +SINGLE_BATTLE_TEST("Magic Bounce bounces back status moves") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_TOXIC].effect == EFFECT_TOXIC); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_ESPEON) { Ability(ABILITY_MAGIC_BOUNCE);}; + } WHEN { + TURN { MOVE(player, MOVE_TOXIC); } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_MAGIC_BOUNCE); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, player); + MESSAGE("Wynaut's Toxic was bounced back by Foe Espeon's Magic Bounce!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, opponent); + STATUS_ICON(player, badPoison: TRUE); + } +} + +SINGLE_BATTLE_TEST("Magic Bounce bounces back powder moves") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_STUN_SPORE].flags & FLAG_POWDER); + ASSUME(gBattleMoves[MOVE_STUN_SPORE].effect == EFFECT_PARALYZE); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_ESPEON) { Ability(ABILITY_MAGIC_BOUNCE);}; + } WHEN { + TURN { MOVE(player, MOVE_STUN_SPORE); } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_MAGIC_BOUNCE); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, player); + MESSAGE("Wynaut's Stun Spore was bounced back by Foe Espeon's Magic Bounce!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, opponent); + STATUS_ICON(player, paralysis: TRUE); + } +} + +SINGLE_BATTLE_TEST("Magic Bounce cannot bounce back powder moves against Grass Types") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_STUN_SPORE].flags & FLAG_POWDER); + ASSUME(gSpeciesInfo[SPECIES_ODDISH].types[0] == TYPE_GRASS); + PLAYER(SPECIES_ODDISH); + OPPONENT(SPECIES_ESPEON) { Ability(ABILITY_MAGIC_BOUNCE);}; + } WHEN { + TURN { MOVE(player, MOVE_STUN_SPORE); } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_MAGIC_BOUNCE); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player); + MESSAGE("Oddish's Stun Spore was bounced back by Foe Espeon's Magic Bounce!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, opponent); + MESSAGE("It doesn't affect Oddish…"); + NOT STATUS_ICON(player, paralysis: TRUE); + } +} + + +DOUBLE_BATTLE_TEST("Magic Bounce bounces back moves hitting both foes at two foes") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_LEER].effect == EFFECT_DEFENSE_DOWN); + ASSUME(gBattleMoves[MOVE_LEER].target == MOVE_TARGET_BOTH); + PLAYER(SPECIES_ABRA); + PLAYER(SPECIES_KADABRA); + OPPONENT(SPECIES_ESPEON) { Ability(ABILITY_MAGIC_BOUNCE);}; + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(playerLeft, MOVE_LEER); } + } SCENE { + ABILITY_POPUP(opponentLeft, ABILITY_MAGIC_BOUNCE); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_LEER, playerLeft); + MESSAGE("Abra's Leer was bounced back by Foe Espeon's Magic Bounce!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_LEER, opponentLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + MESSAGE("Abra's defense fell!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); + MESSAGE("Kadabra's defense fell!"); + // Also check if second original target gets hit by Leer as this was once bugged + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight); + MESSAGE("Foe Wynaut's defense fell!"); + } +} diff --git a/test/powder_moves.c b/test/powder_moves.c new file mode 100644 index 000000000..cb0aee528 --- /dev/null +++ b/test/powder_moves.c @@ -0,0 +1,47 @@ +#include "global.h" +#include "test_battle.h" + +SINGLE_BATTLE_TEST("Powder Moves are blocked by Grass Types") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_STUN_SPORE].flags & FLAG_POWDER); + ASSUME(gSpeciesInfo[SPECIES_ODDISH].types[0] == TYPE_GRASS); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_ODDISH); + } WHEN { + TURN { MOVE(player, MOVE_STUN_SPORE); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player); + MESSAGE("It doesn't affect Foe Oddish…"); + } +} + +SINGLE_BATTLE_TEST("Powder Moves are blocked by Ability Overcoat") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_STUN_SPORE].flags & FLAG_POWDER); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_PINECO) { Ability(ABILITY_OVERCOAT);}; + } WHEN { + TURN { MOVE(player, MOVE_STUN_SPORE); } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_OVERCOAT); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player); + MESSAGE("It doesn't affect Foe Pineco…"); + } +} + +SINGLE_BATTLE_TEST("Powder Moves are blocked by Item Safety Goggles") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_STUN_SPORE].flags & FLAG_POWDER); + ASSUME(gItems[ITEM_SAFETY_GOGGLES].holdEffect == HOLD_EFFECT_SAFETY_GOGGLES); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_ABRA) { Item(ITEM_SAFETY_GOGGLES);}; + } WHEN { + TURN { MOVE(player, MOVE_STUN_SPORE); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player); + MESSAGE("Foe Abra is not affected thanks to its SafetyGoggles!"); + } +}