diff --git a/include/battle.h b/include/battle.h index 107d7cde27..393da86ad1 100644 --- a/include/battle.h +++ b/include/battle.h @@ -1124,7 +1124,7 @@ extern u8 gHealthboxSpriteIds[MAX_BATTLERS_COUNT]; extern u8 gMultiUsePlayerCursor; extern u8 gNumberOfMovesToChoose; extern bool8 gHasFetchedBall; -extern u8 gLastUsedBall; +extern u16 gLastUsedBall; extern u16 gLastThrownBall; extern u16 gBallToDisplay; extern bool8 gLastUsedBallMenuPresent; diff --git a/src/battle_main.c b/src/battle_main.c index dc2114fbd9..f51b063ba6 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -236,7 +236,7 @@ EWRAM_DATA u16 gBattleTurnCounter = 0; EWRAM_DATA u8 gBattlerAbility = 0; EWRAM_DATA struct QueuedStatBoost gQueuedStatBoosts[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA bool8 gHasFetchedBall = FALSE; -EWRAM_DATA u8 gLastUsedBall = 0; +EWRAM_DATA u16 gLastUsedBall = 0; EWRAM_DATA u16 gLastThrownBall = 0; EWRAM_DATA u16 gBallToDisplay = 0; EWRAM_DATA bool8 gLastUsedBallMenuPresent = FALSE; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index d74ec3f956..56aeb7fee7 100755 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -13950,6 +13950,13 @@ static void Cmd_handleballthrow(void) BtlController_EmitBallThrowAnim(gBattlerAttacker, B_COMM_TO_CONTROLLER, shakes); MarkBattlerForControllerExec(gBattlerAttacker); + #if TESTING + if (gTestRunnerEnabled) + { + shakes = 0; // Force failure for tests. TODO: make capture RNG flag + } + #endif + if (shakes == maxShakes) // mon caught, copy of the code above { if (IsCriticalCapture()) diff --git a/src/battle_util.c b/src/battle_util.c index d5d848b1bf..f5f40d4496 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -4398,14 +4398,15 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 break; case ABILITY_BALL_FETCH: if (gBattleMons[battler].item == ITEM_NONE - && gBattleResults.catchAttempts[gLastUsedBall - ITEM_ULTRA_BALL] >= 1 + && gBattleResults.catchAttempts[ItemIdToBallId(gLastUsedBall)] >= 1 && !gHasFetchedBall) { + gLastUsedItem = gLastUsedBall; gBattleScripting.battler = battler; - BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_HELDITEM_BATTLE, 0, 2, &gLastUsedBall); + gBattleMons[battler].item = gLastUsedItem; + BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_HELDITEM_BATTLE, 0, 2, &gLastUsedItem); MarkBattlerForControllerExec(battler); gHasFetchedBall = TRUE; - gLastUsedItem = gLastUsedBall; BattleScriptPushCursorAndCallback(BattleScript_BallFetch); effect++; } diff --git a/test/battle/ability/ball_fetch.c b/test/battle/ability/ball_fetch.c index 7411d60a7d..040aa7cb08 100644 --- a/test/battle/ability/ball_fetch.c +++ b/test/battle/ability/ball_fetch.c @@ -1,7 +1,120 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("Ball Fetch causes the Pokémon to pick up the last failed Ball at the end of the turn"); -TO_DO_BATTLE_TEST("Ball Fetch doesn't trigger if the Pokémon is already holding an item"); -TO_DO_BATTLE_TEST("Ball Fetch only picks up the first failed ball, once per battle"); // Bestow can help test this +WILD_BATTLE_TEST("Ball Fetch causes the Pokémon to pick up the last failed Ball at the end of the turn") +{ + u32 item = 0; + + PARAMETRIZE { item = ITEM_POKE_BALL; } + PARAMETRIZE { item = ITEM_GREAT_BALL; } + PARAMETRIZE { item = ITEM_ULTRA_BALL; } + PARAMETRIZE { item = ITEM_STRANGE_BALL; } + PARAMETRIZE { item = ITEM_X_ACCURACY; } + + GIVEN { + PLAYER(SPECIES_YAMPER) { Ability(ABILITY_BALL_FETCH); } + OPPONENT(SPECIES_METAGROSS); + } WHEN { + TURN { USE_ITEM(player, item); } + } SCENE { + if (item != ITEM_X_ACCURACY) + ABILITY_POPUP(player, ABILITY_BALL_FETCH); + else + NOT ABILITY_POPUP(player, ABILITY_BALL_FETCH); + } THEN { + if (item != ITEM_X_ACCURACY) + EXPECT_EQ(player->item, item); + else + EXPECT_EQ(player->item, ITEM_NONE); + } +} + +WILD_BATTLE_TEST("Ball Fetch doesn't trigger if the Pokémon is already holding an item") +{ + u32 item = 0; + + PARAMETRIZE { item = ITEM_NONE; } + PARAMETRIZE { item = ITEM_NUGGET; } + + GIVEN { + PLAYER(SPECIES_YAMPER) { Ability(ABILITY_BALL_FETCH); Item(item); } + OPPONENT(SPECIES_METAGROSS); + } WHEN { + TURN { USE_ITEM(player, ITEM_GREAT_BALL); } + } SCENE { + if (item == ITEM_NONE) + { + MESSAGE("You used Great Ball!"); + ABILITY_POPUP(player, ABILITY_BALL_FETCH); + MESSAGE("Yamper found a Great Ball!"); + } + else + { + NONE_OF + { + ABILITY_POPUP(player, ABILITY_BALL_FETCH); + MESSAGE("Yamper found a Great Ball!"); + } + } + } THEN { + if (item == ITEM_NONE) + EXPECT_EQ(player->item, ITEM_GREAT_BALL); + else + EXPECT_EQ(player->item, item); + } +} + +WILD_BATTLE_TEST("Ball Fetch only picks up the first failed ball, once per battle") +{ + u32 item = 0; + u32 item2 = 0; + + PARAMETRIZE { item = ITEM_GREAT_BALL; item2 = ITEM_X_ACCURACY; } + PARAMETRIZE { item = ITEM_GREAT_BALL; item2 = ITEM_ULTRA_BALL; } + PARAMETRIZE { item = ITEM_GREAT_BALL; item2 = ITEM_FAST_BALL; } + PARAMETRIZE { item = ITEM_GREAT_BALL; item2 = ITEM_STRANGE_BALL; } + + + GIVEN { + PLAYER(SPECIES_YAMPER) { Ability(ABILITY_BALL_FETCH); } + OPPONENT(SPECIES_METAGROSS); + } WHEN { + TURN { USE_ITEM(player, item); } + TURN { MOVE(player, MOVE_BESTOW); } + TURN { USE_ITEM(player, item2); } + } SCENE { + MESSAGE("You used Great Ball!"); + ABILITY_POPUP(player, ABILITY_BALL_FETCH); + MESSAGE("Yamper found a Great Ball!"); + MESSAGE("Yamper used Bestow!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BESTOW, player); + MESSAGE("The wild Metagross received Great Ball from Yamper!"); + NOT ABILITY_POPUP(player, ABILITY_BALL_FETCH); + } THEN { + EXPECT_EQ(player->item, ITEM_NONE); + } +} + +SINGLE_BATTLE_TEST("Ball Fetch doesn't trigger in Trainer Battles") +{ + u32 item = 0; + + PARAMETRIZE { item = ITEM_POKE_BALL; } + PARAMETRIZE { item = ITEM_GREAT_BALL; } + PARAMETRIZE { item = ITEM_ULTRA_BALL; } + PARAMETRIZE { item = ITEM_STRANGE_BALL; } + PARAMETRIZE { item = ITEM_X_ACCURACY; } + + GIVEN { + PLAYER(SPECIES_YAMPER) { Ability(ABILITY_BALL_FETCH); } + OPPONENT(SPECIES_METAGROSS); + } WHEN { + TURN { USE_ITEM(player, item); } + } SCENE { + NOT ABILITY_POPUP(player, ABILITY_BALL_FETCH); + } THEN { + EXPECT_EQ(player->item, ITEM_NONE); + } +} + TO_DO_BATTLE_TEST("Ball Fetch doesn't trigger in Max Raid Battles");