From 775ea3b564af2ad5455e92af8fa7ab33bb7f563f Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Fri, 6 Dec 2024 18:14:46 +0000 Subject: [PATCH] Pursuit refactor (#5707) --- data/battle_scripts_1.s | 55 +- include/battle.h | 3 + include/battle_scripts.h | 2 + include/config/battle.h | 1 + include/constants/battle_script_commands.h | 1 + src/battle_main.c | 58 ++- src/battle_script_commands.c | 94 ++-- src/battle_util.c | 8 +- test/battle/move_effect/pursuit.c | 566 ++++++++++++++++++++- 9 files changed, 694 insertions(+), 94 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index c7d9a35bb7..d1d139b800 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -298,12 +298,20 @@ BattleScript_CheckPrimalWeather: jumpifhalfword CMP_COMMON_BITS, gBattleWeather, B_WEATHER_STRONG_WINDS, BattleScript_MysteriousAirCurrentBlowsOn return +BattleScript_MoveSwitchPursuit: + jumpifbattletype BATTLE_TYPE_ARENA, BattleScript_MoveSwitchEnd + jumpifcantswitch SWITCH_IGNORE_ESCAPE_PREVENTION | BS_ATTACKER, BattleScript_MoveSwitchEnd + printstring STRINGID_PKMNWENTBACK + waitmessage B_WAIT_TIME_SHORT + jumpifnopursuitswitchdmg BattleScript_MoveSwitchOpenPartyScreen + end + BattleScript_MoveSwitch: jumpifbattletype BATTLE_TYPE_ARENA, BattleScript_MoveSwitchEnd jumpifcantswitch SWITCH_IGNORE_ESCAPE_PREVENTION | BS_ATTACKER, BattleScript_MoveSwitchEnd printstring STRINGID_PKMNWENTBACK waitmessage B_WAIT_TIME_SHORT -BattleScript_MoveSwitchOpenPartyScreen: +BattleScript_MoveSwitchOpenPartyScreen:: openpartyscreen BS_ATTACKER, BattleScript_MoveSwitchEnd switchoutabilities BS_ATTACKER waitstate @@ -1343,7 +1351,7 @@ BattleScript_EffectPartingShotTrySpAtk: waitmessage B_WAIT_TIME_LONG BattleScript_EffectPartingShotSwitch: moveendall - goto BattleScript_MoveSwitch + goto BattleScript_MoveSwitchPursuit BattleScript_EffectPowder:: attackcanceler @@ -2785,7 +2793,7 @@ BattleScript_EffectHitEscape:: jumpifbattleend BattleScript_HitEscapeEnd jumpifbyte CMP_NOT_EQUAL, gBattleOutcome, 0, BattleScript_HitEscapeEnd jumpifemergencyexited BS_TARGET, BattleScript_HitEscapeEnd - goto BattleScript_MoveSwitch + goto BattleScript_MoveSwitchPursuit BattleScript_HitEscapeEnd: end @@ -5767,19 +5775,10 @@ BattleScript_PrintFullBox:: BattleScript_ActionSwitch:: hpthresholds2 BS_ATTACKER printstring STRINGID_RETURNMON - jumpifbattletype BATTLE_TYPE_DOUBLE, BattleScript_PursuitSwitchDmgSetMultihit - setmultihit 1 - goto BattleScript_PursuitSwitchDmgLoop -BattleScript_PursuitSwitchDmgSetMultihit:: - setmultihit 2 -BattleScript_PursuitSwitchDmgLoop:: jumpifnopursuitswitchdmg BattleScript_DoSwitchOut - swapattackerwithtarget - trysetdestinybondtohappen - call BattleScript_PursuitDmgOnSwitchOut - swapattackerwithtarget + end2 + BattleScript_DoSwitchOut:: - decrementmultihit BattleScript_PursuitSwitchDmgLoop switchoutabilities BS_ATTACKER updatedynamax waitstate @@ -5801,34 +5800,6 @@ BattleScript_DoSwitchOut:: moveendcase MOVEEND_MIRROR_MOVE end2 -BattleScript_PursuitDmgOnSwitchOut:: - pause B_WAIT_TIME_SHORT - orword gHitMarker, HITMARKER_OBEYS - attackstring - ppreduce - critcalc - damagecalc - adjustdamage - attackanimation - waitanimation - effectivenesssound - hitanimation BS_TARGET - waitstate - healthbarupdate BS_TARGET - datahpupdate BS_TARGET - critmessage - waitmessage B_WAIT_TIME_LONG - resultmessage - waitmessage B_WAIT_TIME_LONG - tryfaintmon BS_TARGET - moveendfromto MOVEEND_ABILITIES, MOVEEND_ATTACKER_INVISIBLE @ MOVEEND_CHOICE_MOVE has to be included - jumpiffainted BS_TARGET, FALSE, BattleScript_PursuitDmgOnSwitchOutRet - setbyte sGIVEEXP_STATE, 0 - getexp BS_TARGET -BattleScript_PursuitDmgOnSwitchOutRet: - bicword gHitMarker, HITMARKER_OBEYS - return - BattleScript_Pausex20:: pause B_WAIT_TIME_SHORT return diff --git a/include/battle.h b/include/battle.h index fc2d73e62d..aa21a88a12 100644 --- a/include/battle.h +++ b/include/battle.h @@ -836,6 +836,9 @@ struct BattleStruct u8 monCausingSleepClause[NUM_BATTLE_SIDES]; // Stores which pokemon on a given side is causing Sleep Clause to be active as the mon's index in the party u8 sleepClauseEffectExempt:4; // Stores whether effect should be exempt from triggering Sleep Clause (Effect Spore) u8 usedMicleBerry:4; + u8 pursuitTarget:4; // Each battler as a bit. + u8 pursuitSwitchByMove:1; + u8 pursuitStoredSwitch; // Stored id for the Pursuit target's switch s32 battlerExpReward; // Simultaneous hp reduction for spread moves diff --git a/include/battle_scripts.h b/include/battle_scripts.h index bf4b34509f..cac3ce990f 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -46,6 +46,8 @@ extern const u8 BattleScript_PrintFailedToRunString[]; extern const u8 BattleScript_PrintCantEscapeFromBattle[]; extern const u8 BattleScript_PrintFullBox[]; extern const u8 BattleScript_ActionSwitch[]; +extern const u8 BattleScript_DoSwitchOut[]; +extern const u8 BattleScript_MoveSwitchOpenPartyScreen[]; extern const u8 BattleScript_Pausex20[]; extern const u8 BattleScript_LevelUp[]; extern const u8 BattleScript_RainContinuesOrEnds[]; diff --git a/include/config/battle.h b/include/config/battle.h index 781df413a0..b15a32a664 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -126,6 +126,7 @@ #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_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. // Ability settings #define B_ABILITY_WEATHER GEN_LATEST // In Gen6+, ability-induced weather lasts 5 turns. Before, it lasted until the battle ended or until it was changed by a move or a different weather-affecting ability. diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index 609090b12e..3b3faa5790 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -302,6 +302,7 @@ enum MoveEndEffects MOVEEND_SAME_MOVE_TURNS, MOVEEND_SET_EVOLUTION_TRACKER, MOVEEND_CLEAR_BITS, + MOVEEND_PURSUIT_NEXT_ACTION, MOVEEND_COUNT, }; diff --git a/src/battle_main.c b/src/battle_main.c index c7c72564f0..25f69d44b0 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3114,6 +3114,9 @@ static void BattleStartClearSetData(void) gBattleStruct->swapDamageCategory = FALSE; // Photon Geyser, Shell Side Arm, Light That Burns the Sky gBattleStruct->categoryOverride = FALSE; // used for Z-Moves and Max Moves + gBattleStruct->pursuitTarget = 0; + gBattleStruct->pursuitSwitchByMove = FALSE; + gBattleStruct->pursuitStoredSwitch = 0; gSelectedMonPartyId = PARTY_SIZE; // Revival Blessing gCategoryIconSpriteId = 0xFF; @@ -3355,6 +3358,9 @@ const u8* FaintClearSetData(u32 battler) gBattleStruct->lastTakenMoveFrom[battler][1] = 0; gBattleStruct->lastTakenMoveFrom[battler][2] = 0; gBattleStruct->lastTakenMoveFrom[battler][3] = 0; + gBattleStruct->pursuitTarget = 0; + gBattleStruct->pursuitSwitchByMove = FALSE; + gBattleStruct->pursuitStoredSwitch = 0; gBattleStruct->palaceFlags &= ~(1u << battler); gBattleStruct->boosterEnergyActivates &= ~(1u << battler); @@ -5164,6 +5170,9 @@ static void TurnValuesCleanUp(bool8 var0) gSideTimers[B_SIDE_OPPONENT].followmeTimer = 0; gBattleStruct->usedEjectItem = 0; + gBattleStruct->pursuitTarget = 0; + gBattleStruct->pursuitSwitchByMove = FALSE; + gBattleStruct->pursuitStoredSwitch = 0; gBattleStruct->pledgeMove = FALSE; // combined pledge move may not have been used due to a canceller ClearDamageCalcResults(); } @@ -5180,11 +5189,26 @@ static void PopulateArrayWithBattlers(u8 *battlers) battlers[i] = i; } +static bool32 TryActivateGimmick(u32 battler) +{ + if ((gBattleStruct->gimmick.toActivate & (1u << battler)) && !(gProtectStructs[battler].noValidMoves)) + { + gBattlerAttacker = gBattleScripting.battler = battler; + gBattleStruct->gimmick.toActivate &= ~(1u << battler); + if (gGimmicksInfo[gBattleStruct->gimmick.usableGimmick[battler]].ActivateGimmick != NULL) + { + gGimmicksInfo[gBattleStruct->gimmick.usableGimmick[battler]].ActivateGimmick(battler); + return TRUE; + } + } + return FALSE; +} + static bool32 TryDoGimmicksBeforeMoves(void) { if (!(gHitMarker & HITMARKER_RUN) && gBattleStruct->gimmick.toActivate) { - u32 i, battler; + u32 i; u8 order[MAX_BATTLERS_COUNT]; PopulateArrayWithBattlers(order); @@ -5192,16 +5216,8 @@ static bool32 TryDoGimmicksBeforeMoves(void) for (i = 0; i < gBattlersCount; i++) { // Search through each battler and activate their gimmick if they have one prepared. - if ((gBattleStruct->gimmick.toActivate & (1u << order[i])) && !(gProtectStructs[order[i]].noValidMoves)) - { - battler = gBattlerAttacker = gBattleScripting.battler = order[i]; - gBattleStruct->gimmick.toActivate &= ~(1u << battler); - if (gGimmicksInfo[gBattleStruct->gimmick.usableGimmick[battler]].ActivateGimmick != NULL) - { - gGimmicksInfo[gBattleStruct->gimmick.usableGimmick[battler]].ActivateGimmick(battler); - return TRUE; - } - } + if (TryActivateGimmick(order[i])) + return TRUE; } } @@ -5251,7 +5267,7 @@ static bool32 TryDoMoveEffectsBeforeMoves(void) static void TryChangeTurnOrder(void) { u32 i, j; - for (i = 0; i < gBattlersCount - 1; i++) + for (i = gCurrentTurnActionNumber; i < gBattlersCount - 1; i++) { for (j = i + 1; j < gBattlersCount; j++) { @@ -5369,11 +5385,19 @@ static void RunTurnActionsFunctions(void) // Mega Evolve / Focus Punch-like moves after switching, items, running, but before using a move. if (gCurrentActionFuncId == B_ACTION_USE_MOVE && !gBattleStruct->effectsBeforeUsingMoveDone) { - if (TryDoGimmicksBeforeMoves()) - return; - else if (TryDoMoveEffectsBeforeMoves()) - return; - gBattleStruct->effectsBeforeUsingMoveDone = TRUE; + if (!gBattleStruct->pursuitTarget) + { + if (TryDoGimmicksBeforeMoves()) + return; + else if (TryDoMoveEffectsBeforeMoves()) + return; + gBattleStruct->effectsBeforeUsingMoveDone = TRUE; + } + else + { + if (TryActivateGimmick(gBattlerByTurnOrder[gCurrentTurnActionNumber])) + return; + } } *(&gBattleStruct->savedTurnActionNumber) = gCurrentTurnActionNumber; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 791ea1f68f..6e95788859 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -329,6 +329,7 @@ static void BestowItem(u32 battlerAtk, u32 battlerDef); static bool8 IsFinalStrikeEffect(u32 moveEffect); static void TryUpdateRoundTurnOrder(void); static bool32 ChangeOrderTargetAfterAttacker(void); +static bool32 SetTargetToNextPursuiter(u32 battlerDef); void ApplyExperienceMultipliers(s32 *expAmount, u8 expGetterMonId, u8 faintedBattler); static void RemoveAllWeather(void); static void RemoveAllTerrains(void); @@ -1487,6 +1488,10 @@ static bool32 AccuracyCalcHelper(u32 move, u32 battler) { effect = TRUE; } + else if (gBattleStruct->pursuitTarget & (1u << battler)) + { + effect = TRUE; + } else if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !(gStatuses3[battler] & STATUS3_SEMI_INVULNERABLE)) { effect = TRUE; @@ -6947,6 +6952,32 @@ static void Cmd_moveend(void) } } + gBattleScripting.moveendState++; + break; + case MOVEEND_PURSUIT_NEXT_ACTION: + if (gBattleStruct->pursuitTarget & (1u << gBattlerTarget)) + { + u32 storedTarget = gBattlerTarget; + if (SetTargetToNextPursuiter(gBattlerTarget)) + { + ChangeOrderTargetAfterAttacker(); + *(gBattleStruct->moveTarget + gBattlerTarget) = storedTarget; + gBattlerTarget = storedTarget; + } + else if (IsBattlerAlive(gBattlerTarget)) + { + gBattlerAttacker = gBattlerTarget; + if (gBattleStruct->pursuitSwitchByMove) + gBattlescriptCurrInstr = BattleScript_MoveSwitchOpenPartyScreen; + else + gBattlescriptCurrInstr = BattleScript_DoSwitchOut; + *(gBattleStruct->monToSwitchIntoId + gBattlerTarget) = gBattleStruct->pursuitStoredSwitch; + gBattleStruct->pursuitTarget = 0; + gBattleStruct->pursuitSwitchByMove = FALSE; + gBattleStruct->pursuitStoredSwitch = 0; + effect = TRUE; + } + } gBattleScripting.moveendState++; break; case MOVEEND_COUNT: @@ -13967,45 +13998,44 @@ static void Cmd_magnitudedamagecalculation(void) gBattlescriptCurrInstr = cmd->nextInstr; } +static bool32 SetTargetToNextPursuiter(u32 battlerDef) +{ + u32 i; + for (i = gCurrentTurnActionNumber + 1; i < gBattlersCount; i++) + { + u32 battler = gBattlerByTurnOrder[i]; + if (gChosenActionByBattler[battler] == B_ACTION_USE_MOVE + && gMovesInfo[gChosenMoveByBattler[battler]].effect == EFFECT_PURSUIT + && IsBattlerAlive(battlerDef) + && IsBattlerAlive(battler) + && GetBattlerSide(battler) != GetBattlerSide(battlerDef) + && (B_PURSUIT_TARGET >= GEN_4 || *(gBattleStruct->moveTarget + battler) == battlerDef) + && !IsGimmickSelected(battler, GIMMICK_Z_MOVE) + && !IsGimmickSelected(battler, GIMMICK_DYNAMAX) + && GetActiveGimmick(battler) != GIMMICK_DYNAMAX) + { + gBattlerTarget = battler; + return TRUE; + } + } + return FALSE; +} + static void Cmd_jumpifnopursuitswitchdmg(void) { CMD_ARGS(const u8 *jumpInstr); - if (gMultiHitCounter == 1) - { - if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) - gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); - else - gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); - } - else - { - if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) - gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); - else - gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); - } + u32 savedTarget = gBattlerTarget; - if (gChosenActionByBattler[gBattlerTarget] == B_ACTION_USE_MOVE - && gBattlerAttacker == *(gBattleStruct->moveTarget + gBattlerTarget) - && !(gBattleMons[gBattlerTarget].status1 & (STATUS1_SLEEP | STATUS1_FREEZE)) - && gBattleMons[gBattlerAttacker].hp - && !gDisableStructs[gBattlerTarget].truantCounter - && gMovesInfo[gChosenMoveByBattler[gBattlerTarget]].effect == EFFECT_PURSUIT) + if (SetTargetToNextPursuiter(gBattlerAttacker)) { - s32 i; - - for (i = 0; i < gBattlersCount; i++) - { - if (gBattlerByTurnOrder[i] == gBattlerTarget) - gActionsByTurnOrder[i] = B_ACTION_TRY_FINISH; - } - - gCurrentMove = gChosenMove = gChosenMoveByBattler[gBattlerTarget]; - gCurrMovePos = gChosenMovePos = *(gBattleStruct->chosenMovePositions + gBattlerTarget); + ChangeOrderTargetAfterAttacker(); + gBattleStruct->pursuitTarget = 1u << gBattlerAttacker; + gBattleStruct->pursuitSwitchByMove = gActionsByTurnOrder[gCurrentTurnActionNumber] == B_ACTION_USE_MOVE; + gBattleStruct->pursuitStoredSwitch = gBattleStruct->monToSwitchIntoId[gBattlerAttacker]; + *(gBattleStruct->moveTarget + gBattlerTarget) = gBattlerAttacker; + gBattlerTarget = savedTarget; gBattlescriptCurrInstr = cmd->nextInstr; - gBattleScripting.animTurn = 1; - gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED; } else { diff --git a/src/battle_util.c b/src/battle_util.c index aa2f36882c..3848a31212 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -119,6 +119,9 @@ bool32 IsAffectedByFollowMe(u32 battlerAtk, u32 defSide, u32 move) || ability == ABILITY_PROPELLER_TAIL || ability == ABILITY_STALWART) return FALSE; + if (gMovesInfo[move].effect == EFFECT_PURSUIT && gBattleStruct->pursuitTarget) + return FALSE; + if (gSideTimers[defSide].followmePowder && !IsAffectedByPowder(battlerAtk, ability, GetBattlerHoldEffect(battlerAtk, TRUE))) return FALSE; @@ -226,6 +229,7 @@ void HandleAction_UseMove(void) } else if (IsDoubleBattle() && gSideTimers[side].followmeTimer == 0 + && !(gBattleStruct->pursuitTarget & (1u << *(gBattleStruct->moveTarget + gBattlerAttacker))) && (gMovesInfo[gCurrentMove].power != 0 || (moveTarget != MOVE_TARGET_USER && moveTarget != MOVE_TARGET_ALL_BATTLERS)) && ((GetBattlerAbility(*(gBattleStruct->moveTarget + gBattlerAttacker)) != ABILITY_LIGHTNING_ROD && moveType == TYPE_ELECTRIC) || (GetBattlerAbility(*(gBattleStruct->moveTarget + gBattlerAttacker)) != ABILITY_STORM_DRAIN && moveType == TYPE_WATER))) @@ -705,7 +709,7 @@ void HandleAction_ActionFinished(void) gBattleScripting.multihitMoveEffect = 0; gBattleResources->battleScriptsStack->size = 0; - if (B_RECALC_TURN_AFTER_ACTIONS >= GEN_8 && !afterYouActive && !gBattleStruct->pledgeMove) + if (B_RECALC_TURN_AFTER_ACTIONS >= GEN_8 && !afterYouActive && !gBattleStruct->pledgeMove && !gBattleStruct->pursuitTarget) { // i starts at `gCurrentTurnActionNumber` because we don't want to recalculate turn order for mon that have already // taken action. It's been previously increased, which we want in order to not recalculate the turn of the mon that just finished its action @@ -8961,7 +8965,7 @@ static inline u32 CalcMoveBasePower(struct DamageCalculationData *damageCalcData basePower *= 2; break; case EFFECT_PURSUIT: - if (gActionsByTurnOrder[GetBattlerTurnOrderNum(battlerDef)] == B_ACTION_SWITCH) + if (gBattleStruct->pursuitTarget & (1u << battlerDef)) basePower *= 2; break; case EFFECT_NATURAL_GIFT: diff --git a/test/battle/move_effect/pursuit.c b/test/battle/move_effect/pursuit.c index 3a1db03d06..5dfa3f8e33 100644 --- a/test/battle/move_effect/pursuit.c +++ b/test/battle/move_effect/pursuit.c @@ -6,10 +6,462 @@ ASSUMPTIONS ASSUME(gMovesInfo[MOVE_PURSUIT].effect == EFFECT_PURSUIT); } +SINGLE_BATTLE_TEST("Pursuit attacks a switching foe") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_ZIGZAGOON); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { SWITCH(player, 1); MOVE(opponent, MOVE_PURSUIT); } + } SCENE { + SWITCH_OUT_MESSAGE("Wobbuffet"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponent); + HP_BAR(player); + SEND_IN_MESSAGE("Zigzagoon"); + } +} + +SINGLE_BATTLE_TEST("Pursuit attacks a foe using Volt Switch / U-Turn / Parting Shot to switch out") +{ + u32 move; + PARAMETRIZE { move = MOVE_VOLT_SWITCH; } + PARAMETRIZE { move = MOVE_U_TURN; } + PARAMETRIZE { move = MOVE_PARTING_SHOT; } + GIVEN { + ASSUME(gMovesInfo[MOVE_VOLT_SWITCH].effect == EFFECT_HIT_ESCAPE); + ASSUME(gMovesInfo[MOVE_U_TURN].effect == EFFECT_HIT_ESCAPE); + ASSUME(gMovesInfo[MOVE_PARTING_SHOT].effect == EFFECT_PARTING_SHOT); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_ZIGZAGOON); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(player, move); MOVE(opponent, MOVE_PURSUIT); SEND_OUT(player, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, player); + MESSAGE("Wobbuffet went back to 1!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponent); + SEND_IN_MESSAGE("Zigzagoon"); + } +} + +DOUBLE_BATTLE_TEST("Pursuit doesn't attack a foe using Teleport / Baton Pass to switch out") +{ + u32 move; + PARAMETRIZE { move = MOVE_TELEPORT; } + PARAMETRIZE { move = MOVE_BATON_PASS; } + GIVEN { + ASSUME(gMovesInfo[MOVE_QUASH].effect == EFFECT_QUASH); + ASSUME(gMovesInfo[MOVE_TELEPORT].effect == EFFECT_TELEPORT); + ASSUME(gMovesInfo[MOVE_BATON_PASS].effect == EFFECT_BATON_PASS); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_NIDOKING); + PLAYER(SPECIES_ZIGZAGOON); + OPPONENT(SPECIES_WYNAUT); + OPPONENT(SPECIES_LINOONE); + } WHEN { + TURN { MOVE(playerRight, MOVE_QUASH, target: opponentLeft); MOVE(playerLeft, move); MOVE(opponentLeft, MOVE_PURSUIT, target: playerLeft); SEND_OUT(playerLeft, 2); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_QUASH, playerRight); + ANIMATION(ANIM_TYPE_MOVE, move, playerLeft); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponentLeft); + SEND_IN_MESSAGE("Zigzagoon"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponentLeft); + } +} + +SINGLE_BATTLE_TEST("Pursuit doesn't attack switching foe if user already acted that turn") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_ZIGZAGOON); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(opponent, MOVE_PURSUIT); MOVE(player, MOVE_VOLT_SWITCH); SEND_OUT(player, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_VOLT_SWITCH, player); + MESSAGE("Wobbuffet went back to 1!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponent); + SEND_IN_MESSAGE("Zigzagoon"); + } +} + +SINGLE_BATTLE_TEST("Pursuit doubles in power if attacking while target switches out", s16 damage) +{ + u32 speed; + PARAMETRIZE { speed = 5; } + PARAMETRIZE { speed = 3; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Speed(4); } + PLAYER(SPECIES_ZIGZAGOON) { Speed(2); } + OPPONENT(SPECIES_WYNAUT) { Speed(speed); } + } WHEN { + TURN { MOVE(opponent, MOVE_PURSUIT); MOVE(player, MOVE_VOLT_SWITCH); SEND_OUT(player, 1); } + } SCENE { + if (speed == 3) + ANIMATION(ANIM_TYPE_MOVE, MOVE_VOLT_SWITCH, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponent); + HP_BAR(player, captureDamage: &results[i].damage); + if (speed == 5) + ANIMATION(ANIM_TYPE_MOVE, MOVE_VOLT_SWITCH, player); + SEND_IN_MESSAGE("Zigzagoon"); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Pursuit ignores accuracy checks when attacking a switching target") +{ + PASSES_RANDOMLY(100, 100, RNG_ACCURACY); + GIVEN { + ASSUME(gMovesInfo[MOVE_SAND_ATTACK].effect == EFFECT_ACCURACY_DOWN); + ASSUME(gMovesInfo[MOVE_HAIL].effect == EFFECT_HAIL); + PLAYER(SPECIES_GLACEON) { Ability(ABILITY_SNOW_CLOAK); } + PLAYER(SPECIES_ZIGZAGOON); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SAND_ATTACK); MOVE(opponent, MOVE_HAIL); } + TURN { SWITCH(player, 1); MOVE(opponent, MOVE_PURSUIT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SAND_ATTACK, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HAIL, opponent); + SWITCH_OUT_MESSAGE("Glaceon"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponent); + SEND_IN_MESSAGE("Zigzagoon"); + } +} + +DOUBLE_BATTLE_TEST("Pursuit attacks switching foes even if not targetting them (Gen 4+)") +{ + GIVEN { + ASSUME(B_PURSUIT_TARGET >= GEN_4); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_ZIGZAGOON); + PLAYER(SPECIES_GRIMER); + OPPONENT(SPECIES_WYNAUT); + OPPONENT(SPECIES_LINOONE); + } WHEN { + TURN { SWITCH(playerLeft, 2); MOVE(opponentLeft, MOVE_PURSUIT, target: playerRight); MOVE(opponentRight, MOVE_PURSUIT, target: playerRight); } + } SCENE { + SWITCH_OUT_MESSAGE("Wobbuffet"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponentLeft); + HP_BAR(playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponentRight); + HP_BAR(playerLeft); + SEND_IN_MESSAGE("Grimer"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerRight); + } +} + +DOUBLE_BATTLE_TEST("Pursuit attacks a switching foe from fastest to slowest") +{ + u32 speedLeft, speedRight; + PARAMETRIZE { speedLeft = 5; speedRight = 3; } + PARAMETRIZE { speedLeft = 3; speedRight = 5; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Speed(1); } + PLAYER(SPECIES_ZIGZAGOON) { Speed(4); } + PLAYER(SPECIES_GRIMER) { Speed(2); } + OPPONENT(SPECIES_WYNAUT) { Speed(speedLeft); } + OPPONENT(SPECIES_LINOONE) { Speed(speedRight); } + } WHEN { + TURN { SWITCH(playerLeft, 2); MOVE(opponentLeft, MOVE_PURSUIT, target: playerLeft); MOVE(opponentRight, MOVE_PURSUIT, target: playerLeft); } + } SCENE { + SWITCH_OUT_MESSAGE("Wobbuffet"); + if (speedLeft > speedRight) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponentLeft); + HP_BAR(playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponentRight); + HP_BAR(playerLeft); + } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponentRight); + HP_BAR(playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponentLeft); + HP_BAR(playerLeft); + } + SEND_IN_MESSAGE("Grimer"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerRight); + } +} + +DOUBLE_BATTLE_TEST("Pursuit attacks a switching foe but not switching allies") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_ZIGZAGOON); + PLAYER(SPECIES_GRIMER); + OPPONENT(SPECIES_WYNAUT); + OPPONENT(SPECIES_LINOONE); + OPPONENT(SPECIES_ABRA); + } WHEN { + TURN { SWITCH(playerLeft, 2); SWITCH(opponentRight, 2); MOVE(playerRight, MOVE_PURSUIT, target: opponentRight); MOVE(opponentLeft, MOVE_PURSUIT, target: playerLeft); } + } SCENE { + SWITCH_OUT_MESSAGE("Wobbuffet"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, playerRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponentLeft); + SEND_IN_MESSAGE("Grimer"); + MESSAGE("2 withdrew Linoone!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, playerRight); + MESSAGE("2 sent out Abra!"); + } +} + +DOUBLE_BATTLE_TEST("Pursuit only attacks the first switching foe") +{ + // This test does not make sense for B_PURSUIT_TARGET < GEN_4 + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_ZIGZAGOON); + PLAYER(SPECIES_GRIMER); + PLAYER(SPECIES_SUNKERN); + OPPONENT(SPECIES_WYNAUT); + OPPONENT(SPECIES_LINOONE); + } WHEN { + TURN { SWITCH(playerLeft, 2); SWITCH(playerRight, 3); MOVE(opponentLeft, MOVE_PURSUIT, target: playerLeft); MOVE(opponentRight, MOVE_PURSUIT, target: playerLeft); } + } SCENE { + SWITCH_OUT_MESSAGE("Wobbuffet"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponentLeft); + HP_BAR(playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponentRight); + HP_BAR(playerLeft); + SEND_IN_MESSAGE("Grimer"); + SWITCH_OUT_MESSAGE("Zigzagoon"); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponentLeft); + HP_BAR(playerRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponentRight); + HP_BAR(playerRight); + } + SEND_IN_MESSAGE("Sunkern"); + } +} + +DOUBLE_BATTLE_TEST("Pursuit only attacks a switching foe if foe is alive") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { HP(1); } + PLAYER(SPECIES_ZIGZAGOON); + PLAYER(SPECIES_GRIMER); + PLAYER(SPECIES_SUNKERN); + OPPONENT(SPECIES_WYNAUT); + OPPONENT(SPECIES_LINOONE); + } WHEN { + TURN { SWITCH(playerLeft, 2); MOVE(opponentLeft, MOVE_PURSUIT, target: playerLeft); MOVE(opponentRight, MOVE_PURSUIT, target: playerLeft); SEND_OUT(playerLeft, 2); } + } SCENE { + SWITCH_OUT_MESSAGE("Wobbuffet"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponentLeft); + HP_BAR(playerLeft); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponentRight); + MESSAGE("Wobbuffet fainted!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponentRight); + SEND_IN_MESSAGE("Grimer"); + } +} + +DOUBLE_BATTLE_TEST("Pursuit attacks the second switching foe if the first faints from pursuit") +{ + // This test does not make sense for B_PURSUIT_TARGET < GEN_4 + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { HP(1); } + PLAYER(SPECIES_ZIGZAGOON); + PLAYER(SPECIES_GRIMER); + PLAYER(SPECIES_SUNKERN); + OPPONENT(SPECIES_WYNAUT); + OPPONENT(SPECIES_LINOONE); + } WHEN { + TURN { SWITCH(playerLeft, 2); SWITCH(playerRight, 3); MOVE(opponentLeft, MOVE_PURSUIT, target: playerLeft); MOVE(opponentRight, MOVE_PURSUIT, target: playerRight); SEND_OUT(playerLeft, 2); } + } SCENE { + SWITCH_OUT_MESSAGE("Wobbuffet"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponentLeft); + HP_BAR(playerLeft); + MESSAGE("Wobbuffet fainted!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponentRight); + SWITCH_OUT_MESSAGE("Zigzagoon"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponentRight); + HP_BAR(playerRight); + SEND_IN_MESSAGE("Sunkern"); + SEND_IN_MESSAGE("Grimer"); + } +} + +DOUBLE_BATTLE_TEST("Pursuit only attacks a switching foe if user is alive") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_ZIGZAGOON); + PLAYER(SPECIES_GRIMER); + OPPONENT(SPECIES_WYNAUT) { HP(1); } + OPPONENT(SPECIES_LINOONE); + OPPONENT(SPECIES_SUNKERN); + } WHEN { + TURN { MOVE(playerLeft, MOVE_VOLT_SWITCH, target: opponentLeft); MOVE(opponentLeft, MOVE_PURSUIT, target: playerLeft); SEND_OUT(playerLeft, 2); SEND_OUT(opponentLeft, 2); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_VOLT_SWITCH, playerLeft); + MESSAGE("The opposing Wynaut fainted!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponentLeft); + SEND_IN_MESSAGE("Grimer"); + } +} + +SINGLE_BATTLE_TEST("Pursuit attacks a switching foe but fails if user is asleep") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_ZIGZAGOON); + OPPONENT(SPECIES_WYNAUT) { Status1(STATUS1_SLEEP_TURN(2)); } + } WHEN { + TURN { SWITCH(player, 1); MOVE(opponent, MOVE_PURSUIT); } + } SCENE { + SWITCH_OUT_MESSAGE("Wobbuffet"); + MESSAGE("The opposing Wynaut is fast asleep."); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponent); + SEND_IN_MESSAGE("Zigzagoon"); + } +} + +SINGLE_BATTLE_TEST("Pursuit attacks a switching foe and takes Life Orb damage") +{ + GIVEN { + ASSUME(gItemsInfo[ITEM_LIFE_ORB].holdEffect == HOLD_EFFECT_LIFE_ORB); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_ZIGZAGOON); + OPPONENT(SPECIES_WYNAUT) { Item(ITEM_LIFE_ORB); } + } WHEN { + TURN { SWITCH(player, 1); MOVE(opponent, MOVE_PURSUIT); } + } SCENE { + SWITCH_OUT_MESSAGE("Wobbuffet"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponent); + HP_BAR(opponent); + SEND_IN_MESSAGE("Zigzagoon"); + } +} + +DOUBLE_BATTLE_TEST("Pursuit attacks a switching foe but isn't affected by Follow Me") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_FOLLOW_ME].effect == EFFECT_FOLLOW_ME); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_CLEFABLE); + PLAYER(SPECIES_ZIGZAGOON); + OPPONENT(SPECIES_WYNAUT); + OPPONENT(SPECIES_LINOONE); + } WHEN { + TURN { MOVE(playerRight, MOVE_FOLLOW_ME); MOVE(playerLeft, MOVE_VOLT_SWITCH, target: opponentLeft); MOVE(opponentLeft, MOVE_PURSUIT, target: playerLeft); SEND_OUT(playerLeft, 2); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FOLLOW_ME, playerRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_VOLT_SWITCH, playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponentLeft); + SEND_IN_MESSAGE("Zigzagoon"); + } +} + +SINGLE_BATTLE_TEST("Pursuit user mega evolves before attacking a switching foe and hits twice if user has Parental Bond") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_ZIGZAGOON); + OPPONENT(SPECIES_KANGASKHAN) { Item(ITEM_KANGASKHANITE); } + } WHEN { + TURN { SWITCH(player, 1); MOVE(opponent, MOVE_PURSUIT, gimmick: GIMMICK_MEGA); } + } SCENE { + SWITCH_OUT_MESSAGE("Wobbuffet"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponent); + HP_BAR(player); + HP_BAR(player); + SEND_IN_MESSAGE("Zigzagoon"); + } +} + +DOUBLE_BATTLE_TEST("Pursuit user mega evolves before attacking a switching foe and others mega evolve after switch") +{ + GIVEN { + PLAYER(SPECIES_CHARIZARD) { Item(ITEM_CHARIZARDITE_X); } + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_ZIGZAGOON); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_KANGASKHAN) { Item(ITEM_KANGASKHANITE); } + } WHEN { + TURN { SWITCH(playerRight, 2); MOVE(opponentRight, MOVE_PURSUIT, gimmick: GIMMICK_MEGA, target: playerRight); MOVE(playerLeft, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); } + } SCENE { + SWITCH_OUT_MESSAGE("Wobbuffet"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponentRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponentRight); + HP_BAR(playerRight); + HP_BAR(playerRight); + SEND_IN_MESSAGE("Zigzagoon"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerLeft); + } +} + +SINGLE_BATTLE_TEST("Pursuit user terastalizes before attacking a switching foe and gets the damage boost from the tera type", s16 damage) +{ + u32 tera; + PARAMETRIZE { tera = GIMMICK_NONE; } + PARAMETRIZE { tera = GIMMICK_TERA; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_ZIGZAGOON); + OPPONENT(SPECIES_KANGASKHAN) { TeraType(TYPE_DARK); } + } WHEN { + TURN { SWITCH(player, 1); MOVE(opponent, MOVE_PURSUIT, gimmick: tera); } + } SCENE { + SWITCH_OUT_MESSAGE("Wobbuffet"); + if (tera == GIMMICK_TERA) + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_TERA_ACTIVATE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponent); + HP_BAR(player, captureDamage: &results[i].damage); + SEND_IN_MESSAGE("Zigzagoon"); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + } +} + +DOUBLE_BATTLE_TEST("Pursuit affected by Electrify fails against immune target") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_ELECTRIFY].effect == EFFECT_ELECTRIFY); + PLAYER(SPECIES_DONPHAN); + PLAYER(SPECIES_HELIOLISK); + PLAYER(SPECIES_ZIGZAGOON); + OPPONENT(SPECIES_WYNAUT); + OPPONENT(SPECIES_LINOONE); + } WHEN { + TURN { MOVE(playerRight, MOVE_ELECTRIFY, target: opponentLeft); MOVE(playerLeft, MOVE_VOLT_SWITCH, target: opponentLeft); MOVE(opponentLeft, MOVE_PURSUIT, target: playerLeft); SEND_OUT(playerLeft, 2); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRIFY, playerRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_VOLT_SWITCH, playerLeft); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponentLeft); + SEND_IN_MESSAGE("Zigzagoon"); + } +} + +DOUBLE_BATTLE_TEST("Pursuit affected by Electrify fails against target with Volt Absorb") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_ELECTRIFY].effect == EFFECT_ELECTRIFY); + PLAYER(SPECIES_LANTURN) { Ability(ABILITY_VOLT_ABSORB); } + PLAYER(SPECIES_HELIOLISK); + PLAYER(SPECIES_ZIGZAGOON); + OPPONENT(SPECIES_WYNAUT); + OPPONENT(SPECIES_LINOONE); + } WHEN { + TURN { MOVE(playerRight, MOVE_ELECTRIFY, target: opponentLeft); MOVE(playerLeft, MOVE_VOLT_SWITCH, target: opponentLeft); MOVE(opponentLeft, MOVE_PURSUIT, target: playerLeft); SEND_OUT(playerLeft, 2); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRIFY, playerRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_VOLT_SWITCH, playerLeft); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponentLeft); + ABILITY_POPUP(playerLeft, ABILITY_VOLT_ABSORB); + SEND_IN_MESSAGE("Zigzagoon"); + } +} + SINGLE_BATTLE_TEST("Pursuited mon correctly switches out after it got hit and activated ability Tangling Hair") { GIVEN { - PLAYER(SPECIES_DUGTRIO) { Ability(ABILITY_TANGLING_HAIR); } + PLAYER(SPECIES_DUGTRIO_ALOLA) { Ability(ABILITY_TANGLING_HAIR); } PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WYNAUT); OPPONENT(SPECIES_WOBBUFFET); @@ -25,6 +477,80 @@ SINGLE_BATTLE_TEST("Pursuited mon correctly switches out after it got hit and ac } } +DOUBLE_BATTLE_TEST("Pursuited mon correctly switches out after it got hit and activated ability Tangling Hair - Doubles") +{ + GIVEN { + PLAYER(SPECIES_DUGTRIO_ALOLA) { Ability(ABILITY_TANGLING_HAIR); } + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { SWITCH(playerLeft, 2); MOVE(opponentLeft, MOVE_PURSUIT, target: playerLeft); MOVE(opponentRight, MOVE_PURSUIT, target: playerLeft); } + } SCENE { + SWITCH_OUT_MESSAGE("Dugtrio"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponentLeft); + ABILITY_POPUP(playerLeft, ABILITY_TANGLING_HAIR); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); + MESSAGE("The opposing Wynaut's Speed fell!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponentRight); + ABILITY_POPUP(playerLeft, ABILITY_TANGLING_HAIR); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight); + MESSAGE("The opposing Wobbuffet's Speed fell!"); + SEND_IN_MESSAGE("Wobbuffet"); + } +} + +SINGLE_BATTLE_TEST("Pursuited mon correctly switches out after it got hit and activated ability Tangling Hair - Mirror Armor") +{ + GIVEN { + PLAYER(SPECIES_DUGTRIO_ALOLA) { Ability(ABILITY_TANGLING_HAIR); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { SWITCH(player, 1); MOVE(opponent, MOVE_PURSUIT); } + } SCENE { + SWITCH_OUT_MESSAGE("Dugtrio"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponent); + ABILITY_POPUP(player, ABILITY_TANGLING_HAIR); + ABILITY_POPUP(opponent, ABILITY_MIRROR_ARMOR); + SEND_IN_MESSAGE("Wobbuffet"); + } +} + +DOUBLE_BATTLE_TEST("Pursuited mon correctly switches out after it got hit and activated ability Cotton Down") +{ + GIVEN { + PLAYER(SPECIES_ELDEGOSS) { Ability(ABILITY_COTTON_DOWN); } + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { SWITCH(playerLeft, 2); MOVE(opponentLeft, MOVE_PURSUIT, target: playerLeft); MOVE(opponentRight, MOVE_PURSUIT, target: playerLeft); } + } SCENE { + SWITCH_OUT_MESSAGE("Eldegoss"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponentLeft); + ABILITY_POPUP(playerLeft, ABILITY_COTTON_DOWN); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); + MESSAGE("The opposing Wynaut's Speed fell!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); + MESSAGE("Wobbuffet's Speed fell!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight); + MESSAGE("The opposing Wobbuffet's Speed fell!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponentRight); + ABILITY_POPUP(playerLeft, ABILITY_COTTON_DOWN); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); + MESSAGE("The opposing Wynaut's Speed fell!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); + MESSAGE("Wobbuffet's Speed fell!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight); + MESSAGE("The opposing Wobbuffet's Speed fell!"); + SEND_IN_MESSAGE("Wobbuffet"); + } +} + // Checked so that Pursuit has only 1 PP and it forces the player to use Struggle. SINGLE_BATTLE_TEST("Pursuit becomes a locked move after being used on switch-out while holding a Choice Item") { @@ -46,4 +572,42 @@ SINGLE_BATTLE_TEST("Pursuit becomes a locked move after being used on switch-out } } +SINGLE_BATTLE_TEST("Pursuit attacks a switching foe and switchin is correctly stored") +{ + u32 switchin; + PARAMETRIZE { switchin = 1; } + PARAMETRIZE { switchin = 2; } + PARAMETRIZE { switchin = 3; } + PARAMETRIZE { switchin = 4; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_ZIGZAGOON); + PLAYER(SPECIES_AIPOM); + PLAYER(SPECIES_ABRA); + PLAYER(SPECIES_VENIPEDE); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { SWITCH(player, switchin); MOVE(opponent, MOVE_PURSUIT); } + } SCENE { + SWITCH_OUT_MESSAGE("Wobbuffet"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, opponent); + switch (switchin) + { + case 1: + SEND_IN_MESSAGE("Zigzagoon"); + break; + case 2: + SEND_IN_MESSAGE("Aipom"); + break; + case 3: + SEND_IN_MESSAGE("Abra"); + break; + case 4: + SEND_IN_MESSAGE("Venipede"); + break; + } + } +} + TO_DO_BATTLE_TEST("Baton Pass doesn't cause Pursuit to increase its power or priority");