From df5312c0bd7070fe31a2db61f829bb82763210da Mon Sep 17 00:00:00 2001 From: ghoulslash <41651341+ghoulslash@users.noreply.github.com> Date: Tue, 14 Oct 2025 15:34:06 -0400 Subject: [PATCH] Fix Knock Off not being restored and Wild Battles (#7952) Co-authored-by: ghoulslash --- src/battle_script_commands.c | 13 ++++++++----- test/battle/move_effect/knock_off.c | 27 +++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index b198d4b589..5280797a41 100755 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -5694,26 +5694,29 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect) return FALSE; u32 effect = FALSE; + u32 side = GetBattlerSide(gBattlerTarget); switch (moveEffect) { case EFFECT_KNOCK_OFF: if (gBattleStruct->battlerState[gBattlerTarget].itemCanBeKnockedOff && gBattleMons[gBattlerTarget].item != ITEM_NONE && IsBattlerTurnDamaged(gBattlerTarget) - && IsBattlerAlive(gBattlerAttacker)) + && IsBattlerAlive(gBattlerAttacker) + && !(B_KNOCK_OFF_REMOVAL >= GEN_5 && side == B_SIDE_PLAYER && !(gBattleTypeFlags & BATTLE_TYPE_TRAINER))) { - u32 side = GetBattlerSide(gBattlerTarget); gLastUsedItem = gBattleMons[gBattlerTarget].item; gBattleMons[gBattlerTarget].item = 0; if (gBattleMons[gBattlerTarget].ability != ABILITY_GORILLA_TACTICS) gBattleStruct->choicedMove[gBattlerTarget] = MOVE_NONE; CheckSetUnburden(gBattlerTarget); - // In Gen 5+, Knock Off removes the target's item rather than rendering it unusable. + // In Gen 5+, Knock Off removes the target's item rather than rendering it unusable if (B_KNOCK_OFF_REMOVAL >= GEN_5) { BtlController_EmitSetMonData(gBattlerTarget, B_COMM_TO_CONTROLLER, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].item), &gBattleMons[gBattlerTarget].item); MarkBattlerForControllerExec(gBattlerTarget); + // Mark item as stolen so it will be restored after battle + gBattleStruct->itemLost[side][gBattlerPartyIndexes[gBattlerTarget]].stolen = TRUE; } else { @@ -5875,7 +5878,7 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect) } break; case EFFECT_STONE_AXE: - if (!IsHazardOnSide(GetBattlerSide(gBattlerTarget), HAZARDS_STEALTH_ROCK) + if (!IsHazardOnSide(side, HAZARDS_STEALTH_ROCK) && IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerAttacker)) { @@ -5886,7 +5889,7 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect) } break; case EFFECT_CEASELESS_EDGE: - if (gSideTimers[GetBattlerSide(gBattlerTarget)].spikesAmount < 3 + if (gSideTimers[side].spikesAmount < 3 && IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerAttacker)) { diff --git a/test/battle/move_effect/knock_off.c b/test/battle/move_effect/knock_off.c index 3fdeadcecc..eb0874d7bf 100644 --- a/test/battle/move_effect/knock_off.c +++ b/test/battle/move_effect/knock_off.c @@ -6,6 +6,33 @@ ASSUMPTIONS ASSUME(GetMoveEffect(MOVE_KNOCK_OFF) == EFFECT_KNOCK_OFF); } +WILD_BATTLE_TEST("Knock Off does not remove item when used by Wild Pokemon (Gen 5+)") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_LEFTOVERS); } + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_EVIOLITE); } + } WHEN { + TURN { MOVE(opponent, MOVE_KNOCK_OFF); } + TURN { MOVE(player, MOVE_KNOCK_OFF); } + } SCENE { + // Turn 1 + ANIMATION(ANIM_TYPE_MOVE, MOVE_KNOCK_OFF, opponent); + if (B_KNOCK_OFF_REMOVAL >= GEN_5) + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_KNOCKOFF, player); + else + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_KNOCKOFF, player); + // Turn 2 + ANIMATION(ANIM_TYPE_MOVE, MOVE_KNOCK_OFF, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_KNOCKOFF, opponent); + } THEN { + EXPECT(player->item == ITEM_LEFTOVERS); + if (B_KNOCK_OFF_REMOVAL >= GEN_5) + EXPECT(opponent->item == ITEM_NONE); + else + EXPECT(opponent->item == ITEM_EVIOLITE); + } +} + SINGLE_BATTLE_TEST("Knock Off knocks a healing berry before it has the chance to activate") { GIVEN {