diff --git a/include/config/battle.h b/include/config/battle.h index f38be8949c..db3e6dcc77 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -154,6 +154,7 @@ #define B_ABILITY_TRIGGER_CHANCE GEN_LATEST // In Gen3, Shed Skin, Cute Charm, Flame Body, Static and Poison Point have a 1/3 chance to trigger. In Gen 4+ it's 30%. // In Gen3, Effect Spore has a 10% chance to sleep, poison or paralyze, with an equal chance. // In Gen4, it's 30%. In Gen5+ it has 11% to sleep, 9% chance to poison and 10% chance to paralyze. +#define B_PICKUP_WILD GEN_LATEST // In Gen9+, Pickup allows its user to pickup its own used item at the end of the turn in wild battles. // Item settings #define B_HP_BERRIES GEN_LATEST // In Gen4+, berries which restore HP activate immediately after HP drops to half. In Gen3, the effect occurs at the end of the turn. @@ -163,6 +164,7 @@ #define B_MENTAL_HERB GEN_LATEST // In Gen5+, the Mental Herb cures Taunt, Encore, Torment, Heal Block, and Disable in addition to Infatuation from before. #define B_TRAINERS_KNOCK_OFF_ITEMS TRUE // If TRUE, trainers can steal/swap your items (non-berries are restored after battle). In vanilla games trainers cannot steal items. #define B_RETURN_STOLEN_NPC_ITEMS GEN_LATEST // In Gen5+, Thief and Covet no longer steal items from NPCs. +#define B_STEAL_WILD_ITEMS GEN_LATEST // In Gen9, Thief and Covet steal a wild pokemon's item and send it to the bag. Before Gen9, the stolen item would be held by the Thief/Covet user. #define B_RESTORE_HELD_BATTLE_ITEMS GEN_LATEST // In Gen9, all non-berry items are restored after battle. #define B_SOUL_DEW_BOOST GEN_LATEST // In Gens3-6, Soul Dew boosts Latis' Sp. Atk and Sp. Def. In Gen7+ it boosts the power of their Psychic and Dragon type moves instead. #define B_NET_BALL_MODIFIER GEN_LATEST // In Gen7+, Net Ball's catch multiplier is x5 instead of x3. diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 7d342b0b73..1bbef29586 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -2852,17 +2852,26 @@ static void CheckSetUnburden(u8 battler) void StealTargetItem(u8 battlerStealer, u8 battlerItem) { gLastUsedItem = gBattleMons[battlerItem].item; - gBattleMons[battlerItem].item = 0; + gBattleMons[battlerItem].item = ITEM_NONE; - RecordItemEffectBattle(battlerItem, 0); - RecordItemEffectBattle(battlerStealer, ItemId_GetHoldEffect(gLastUsedItem)); - gBattleMons[battlerStealer].item = gLastUsedItem; + if (B_STEAL_WILD_ITEMS >= GEN_9 + && !(gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_PALACE)) + && MoveHasAdditionalEffect(gCurrentMove, MOVE_EFFECT_STEAL_ITEM) + && battlerStealer == gBattlerAttacker) // ensure that Pickpocket isn't activating this + { + AddBagItem(gLastUsedItem, 1); + } + else + { + RecordItemEffectBattle(battlerStealer, ItemId_GetHoldEffect(gLastUsedItem)); + gBattleMons[battlerStealer].item = gLastUsedItem; + gBattleResources->flags->flags[battlerStealer] &= ~RESOURCE_FLAG_UNBURDEN; + BtlController_EmitSetMonData(battlerStealer, BUFFER_A, REQUEST_HELDITEM_BATTLE, 0, sizeof(gLastUsedItem), &gLastUsedItem); // set attacker item + MarkBattlerForControllerExec(battlerStealer); + } + RecordItemEffectBattle(battlerItem, ITEM_NONE); CheckSetUnburden(battlerItem); - gBattleResources->flags->flags[battlerStealer] &= ~RESOURCE_FLAG_UNBURDEN; - - BtlController_EmitSetMonData(battlerStealer, BUFFER_A, REQUEST_HELDITEM_BATTLE, 0, sizeof(gLastUsedItem), &gLastUsedItem); // set attacker item - MarkBattlerForControllerExec(battlerStealer); BtlController_EmitSetMonData(battlerItem, BUFFER_A, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].item), &gBattleMons[battlerItem].item); // remove target item MarkBattlerForControllerExec(battlerItem); @@ -3544,8 +3553,13 @@ void SetMoveEffect(bool32 primary, bool32 certain) else { StealTargetItem(gBattlerAttacker, gBattlerTarget); // Attacker steals target item - gBattleMons[gBattlerAttacker].item = ITEM_NONE; // Item assigned later on with thief (see MOVEEND_CHANGED_ITEMS) - gBattleStruct->changedItems[gBattlerAttacker] = gLastUsedItem; // Stolen item to be assigned later + + if (!(B_STEAL_WILD_ITEMS >= GEN_9 + && !(gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_PALACE)))) + { + gBattleMons[gBattlerAttacker].item = ITEM_NONE; // Item assigned later on with thief (see MOVEEND_CHANGED_ITEMS) + gBattleStruct->changedItems[gBattlerAttacker] = gLastUsedItem; // Stolen item to be assigned later + } BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_ItemSteal; } diff --git a/src/battle_util.c b/src/battle_util.c index d2f5314742..def2742a1b 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -11557,7 +11557,7 @@ u16 GetUsedHeldItem(u32 battler) bool32 CantPickupItem(u32 battler) { // Used by RandomUniformExcept() for RNG_PICKUP - if (battler == gBattlerAttacker && gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_LINK)) + if (battler == gBattlerAttacker && (B_PICKUP_WILD < GEN_9 || gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_LINK))) return TRUE; return !(IsBattlerAlive(battler) && GetUsedHeldItem(battler) && gBattleStruct->canPickupItem & (1u << battler)); } diff --git a/test/battle/ability/pickup.c b/test/battle/ability/pickup.c index 927e1b3468..a6dabb66cc 100644 --- a/test/battle/ability/pickup.c +++ b/test/battle/ability/pickup.c @@ -23,7 +23,24 @@ SINGLE_BATTLE_TEST("Pickup grants an item used by another Pokémon") } } -SINGLE_BATTLE_TEST("Pickup doesn't grant the user their item") +WILD_BATTLE_TEST("Pickup grants an item used by itself in wild battles (Gen 9)") +{ + GIVEN { + ASSUME(B_PICKUP_WILD >= GEN_9); + PLAYER(SPECIES_ZIGZAGOON) { Ability(ABILITY_PICKUP); MaxHP(100); HP(51); Item(ITEM_SITRUS_BERRY); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + ABILITY_POPUP(player, ABILITY_PICKUP); + MESSAGE("Zigzagoon found one Sitrus Berry!"); + } THEN { + EXPECT_EQ(player->item, ITEM_SITRUS_BERRY); + } +} + +SINGLE_BATTLE_TEST("Pickup doesn't grant the user their item outside wild battles") { GIVEN { PLAYER(SPECIES_WOBBUFFET); diff --git a/test/battle/move_effect_secondary/steal_item.c b/test/battle/move_effect_secondary/steal_item.c new file mode 100644 index 0000000000..8a4ae931d2 --- /dev/null +++ b/test/battle/move_effect_secondary/steal_item.c @@ -0,0 +1,129 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(MoveHasAdditionalEffect(MOVE_THIEF, MOVE_EFFECT_STEAL_ITEM) == TRUE); + ASSUME(MoveHasAdditionalEffect(MOVE_COVET, MOVE_EFFECT_STEAL_ITEM) == TRUE); +} + +SINGLE_BATTLE_TEST("Thief and Covet steal target's held item") +{ + u32 move; + PARAMETRIZE { move = MOVE_THIEF; } + PARAMETRIZE { move = MOVE_COVET; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_HYPER_POTION); } + } WHEN { + TURN { MOVE(player, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_STEAL, opponent); + } THEN { + EXPECT_EQ(player->item, ITEM_HYPER_POTION); + EXPECT_EQ(opponent->item, ITEM_NONE); + } +} + +SINGLE_BATTLE_TEST("Thief and Covet steal player's held item if opponent is a trainer") +{ + u32 move; + PARAMETRIZE { move = MOVE_THIEF; } + PARAMETRIZE { move = MOVE_COVET; } + GIVEN { + ASSUME(B_TRAINERS_KNOCK_OFF_ITEMS == TRUE); + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_HYPER_POTION); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + HP_BAR(player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_STEAL, player); + } THEN { + EXPECT_EQ(opponent->item, ITEM_HYPER_POTION); + EXPECT_EQ(player->item, ITEM_NONE); + } +} + +WILD_BATTLE_TEST("Thief and Covet don't steal player's held item if opponent is a wild mon") +{ + u32 move; + PARAMETRIZE { move = MOVE_THIEF; } + PARAMETRIZE { move = MOVE_COVET; } + GIVEN { + ASSUME(B_TRAINERS_KNOCK_OFF_ITEMS == TRUE); + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_HYPER_POTION); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + HP_BAR(player); + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_STEAL, player); + } THEN { + EXPECT_EQ(player->item, ITEM_HYPER_POTION); + EXPECT_EQ(opponent->item, ITEM_NONE); + } +} + +SINGLE_BATTLE_TEST("Thief and Covet don't steal target's held item if user is holding an item") +{ + u32 move; + PARAMETRIZE { move = MOVE_THIEF; } + PARAMETRIZE { move = MOVE_COVET; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_POTION); } + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_HYPER_POTION); } + } WHEN { + TURN { MOVE(player, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent); + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_STEAL, opponent); + } THEN { + EXPECT_EQ(player->item, ITEM_POTION); + EXPECT_EQ(opponent->item, ITEM_HYPER_POTION); + } +} + +SINGLE_BATTLE_TEST("Thief and Covet don't steal target's held item if target has no item") +{ + u32 move; + PARAMETRIZE { move = MOVE_THIEF; } + PARAMETRIZE { move = MOVE_COVET; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent); + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_STEAL, opponent); + } +} + +// Test can't currently verify if the item is sent to Bag +WILD_BATTLE_TEST("Thief and Covet steal target's held item and it's added to Bag in wild battles (Gen 9)") +{ + u32 move; + PARAMETRIZE { move = MOVE_THIEF; } + PARAMETRIZE { move = MOVE_COVET; } + GIVEN { + ASSUME(B_STEAL_WILD_ITEMS >= GEN_9); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_HYPER_POTION); } + } WHEN { + TURN { MOVE(player, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_STEAL, opponent); + } THEN { + EXPECT_EQ(player->item, ITEM_NONE); + EXPECT_EQ(opponent->item, ITEM_NONE); + } +}