diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index b60e4c2c82..9fe1b819ba 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -2057,6 +2057,15 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) ADJUST_SCORE(-20); break; case EFFECT_TRICK: + if ((gBattleMons[battlerAtk].item == ITEM_NONE && aiData->items[battlerDef] == ITEM_NONE) + || !CanBattlerGetOrLoseItem(battlerAtk, gBattleMons[battlerAtk].item) + || !CanBattlerGetOrLoseItem(battlerAtk, aiData->items[battlerDef]) + || !CanBattlerGetOrLoseItem(battlerDef, aiData->items[battlerDef]) + || !CanBattlerGetOrLoseItem(battlerDef, gBattleMons[battlerAtk].item) + || aiData->abilities[battlerAtk] == ABILITY_STICKY_HOLD + || aiData->abilities[battlerDef] == ABILITY_STICKY_HOLD + || DoesSubstituteBlockMove(battlerAtk, battlerDef, move)) + ADJUST_SCORE(-10); case EFFECT_KNOCK_OFF: case EFFECT_CORROSIVE_GAS: if (aiData->abilities[battlerDef] == ABILITY_STICKY_HOLD) @@ -2450,8 +2459,12 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) ADJUST_SCORE(-10); break; case EFFECT_BESTOW: - if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_NONE - || !CanBattlerGetOrLoseItem(battlerAtk, gBattleMons[battlerAtk].item)) // AI knows its own item + if (gBattleMons[battlerAtk].item == ITEM_NONE + || aiData->items[battlerDef] != ITEM_NONE + || !CanBattlerGetOrLoseItem(battlerAtk, gBattleMons[battlerAtk].item) // AI knows its own item + || !CanBattlerGetOrLoseItem(battlerDef, gBattleMons[battlerAtk].item) + || aiData->abilities[battlerAtk] == ABILITY_STICKY_HOLD + || DoesSubstituteBlockMove(battlerAtk, battlerDef, move)) ADJUST_SCORE(-10); break; case EFFECT_WISH: diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 0bbd7c1955..ba546a5c99 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -12698,7 +12698,7 @@ static void Cmd_trysethelpinghand(void) } } -// Trick // TODO: Sticky Hold +// Trick static void Cmd_tryswapitems(void) { CMD_ARGS(const u8 *failInstr); @@ -12743,6 +12743,10 @@ static void Cmd_tryswapitems(void) gBattlescriptCurrInstr = cmd->failInstr; } // check if ability prevents swapping + else if (GetBattlerAbility(gBattlerAttacker) == ABILITY_STICKY_HOLD) + { + gBattlescriptCurrInstr = cmd->failInstr; + } else if (GetBattlerAbility(gBattlerTarget) == ABILITY_STICKY_HOLD) { gBattlescriptCurrInstr = BattleScript_StickyHoldActivates; @@ -17475,6 +17479,7 @@ void BS_TryBestow(void) || gBattleMons[gBattlerTarget].item != ITEM_NONE || !CanBattlerGetOrLoseItem(gBattlerAttacker, gBattleMons[gBattlerAttacker].item) || !CanBattlerGetOrLoseItem(gBattlerTarget, gBattleMons[gBattlerAttacker].item) + || GetBattlerAbility(gBattlerAttacker) == ABILITY_STICKY_HOLD || gWishFutureKnock.knockedOffMons[GetBattlerSide(gBattlerTarget)] & (1u << gBattlerPartyIndexes[gBattlerTarget])) { gBattlescriptCurrInstr = cmd->failInstr; diff --git a/test/battle/ai/ai_doubles.c b/test/battle/ai/ai_doubles.c index 712c811286..94fcfc8ef9 100644 --- a/test/battle/ai/ai_doubles.c +++ b/test/battle/ai/ai_doubles.c @@ -56,6 +56,88 @@ AI_DOUBLE_BATTLE_TEST("AI will not use Helping Hand if partner does not have any } } +AI_DOUBLE_BATTLE_TEST("AI skips Trick/Bestow when items are missing or target already holds one") +{ + u16 move = MOVE_NONE, atkItem = ITEM_NONE, targetItem = ITEM_NONE; + + PARAMETRIZE { move = MOVE_TRICK; atkItem = ITEM_NONE; targetItem = ITEM_NONE; } + PARAMETRIZE { move = MOVE_BESTOW; atkItem = ITEM_NONE; targetItem = ITEM_NONE; } + PARAMETRIZE { move = MOVE_BESTOW; atkItem = ITEM_ORAN_BERRY; targetItem = ITEM_LEFTOVERS; } + + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_WOBBUFFET) { Item(targetItem); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Moves(move, MOVE_SCRATCH); Item(atkItem); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_TACKLE); } + } WHEN { + TURN { NOT_EXPECT_MOVE(opponentLeft, move); } + } +} + +AI_DOUBLE_BATTLE_TEST("AI skips Trick/Bestow with unexchangeable items") +{ + u16 move = MOVE_NONE, atkItem = ITEM_NONE, targetItem = ITEM_NONE; + + PARAMETRIZE { move = MOVE_TRICK; atkItem = ITEM_ORANGE_MAIL; targetItem = ITEM_NONE; } + PARAMETRIZE { move = MOVE_TRICK; atkItem = ITEM_ORAN_BERRY; targetItem = ITEM_ORANGE_MAIL; } + PARAMETRIZE { move = MOVE_BESTOW; atkItem = ITEM_ORANGE_MAIL; targetItem = ITEM_NONE; } + + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_WOBBUFFET) { Item(targetItem); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Moves(move, MOVE_SCRATCH); Item(atkItem); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_TACKLE); } + } WHEN { + TURN { NOT_EXPECT_MOVE(opponentLeft, move); } + } +} + +AI_DOUBLE_BATTLE_TEST("AI skips Trick/Bestow around Sticky Hold") +{ + u16 move = MOVE_NONE, atkItem = ITEM_ORAN_BERRY, targetItem = ITEM_NONE; + enum Ability atkAbility = ABILITY_PRESSURE, targetAbility = ABILITY_PRESSURE; + + PARAMETRIZE { move = MOVE_TRICK; atkAbility = ABILITY_STICKY_HOLD; targetAbility = ABILITY_PRESSURE; targetItem = ITEM_LEFTOVERS; } + PARAMETRIZE { move = MOVE_TRICK; atkAbility = ABILITY_PRESSURE; targetAbility = ABILITY_STICKY_HOLD; targetItem = ITEM_LEFTOVERS; } + PARAMETRIZE { move = MOVE_BESTOW; atkAbility = ABILITY_STICKY_HOLD; targetAbility = ABILITY_PRESSURE; targetItem = ITEM_NONE; } + + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_WOBBUFFET) { Ability(targetAbility); Item(targetItem); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Ability(atkAbility); Item(atkItem); Moves(move, MOVE_SCRATCH); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_TACKLE); } + } WHEN { + TURN { NOT_EXPECT_MOVE(opponentLeft, move); } + } +} + +AI_DOUBLE_BATTLE_TEST("AI skips Trick/Bestow if the target has a Substitute") +{ + ASSUME(GetMoveEffect(MOVE_SUBSTITUTE) == EFFECT_SUBSTITUTE); + + u16 move = MOVE_NONE, atkItem = ITEM_NONE, targetItem = ITEM_NONE; + + PARAMETRIZE { move = MOVE_TRICK; atkItem = ITEM_ORAN_BERRY; targetItem = ITEM_LEFTOVERS; } + PARAMETRIZE { move = MOVE_BESTOW; atkItem = ITEM_ORAN_BERRY; targetItem = ITEM_NONE; } + + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_SUBSTITUTE, MOVE_CELEBRATE); Item(targetItem); Speed(20); } + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE); Speed(20); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(move, MOVE_SCRATCH); Item(atkItem); Speed(1); Attack(1); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE); Speed(1); } + } WHEN { + TURN { + MOVE(playerLeft, MOVE_SUBSTITUTE); + MOVE(playerRight, MOVE_CELEBRATE); + } + TURN { NOT_EXPECT_MOVE(opponentLeft, move); } + } +} + AI_DOUBLE_BATTLE_TEST("AI will not use a status move if partner already chose Helping Hand") { s32 j; diff --git a/test/battle/move_effect/bestow.c b/test/battle/move_effect/bestow.c index 4ca86119b6..9d7bc9ca5c 100644 --- a/test/battle/move_effect/bestow.c +++ b/test/battle/move_effect/bestow.c @@ -92,6 +92,21 @@ SINGLE_BATTLE_TEST("Bestow fails if the user's held item is a Z-Crystal") } } +SINGLE_BATTLE_TEST("Bestow fails if the user has Sticky Hold") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_STICKY_HOLD); Item(ITEM_SITRUS_BERRY); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_BESTOW); } + } SCENE { + MESSAGE("But it failed!"); + } THEN { + EXPECT(player->item == ITEM_SITRUS_BERRY); + EXPECT(opponent->item == ITEM_NONE); + } +} + SINGLE_BATTLE_TEST("Bestow fails if the target is behind a Substitute (Gen 6+)") { GIVEN { @@ -130,4 +145,3 @@ SINGLE_BATTLE_TEST("Bestow fails if the user's held item changes its form") EXPECT(opponent->item == ITEM_NONE); } } - diff --git a/test/battle/move_effect/trick.c b/test/battle/move_effect/trick.c index 3aa26af67a..b7a75cd188 100644 --- a/test/battle/move_effect/trick.c +++ b/test/battle/move_effect/trick.c @@ -1,4 +1,185 @@ #include "global.h" #include "test/battle.h" +#include "mail.h" -TO_DO_BATTLE_TEST("TODO: Write Trick (Move Effect) test titles") +SINGLE_BATTLE_TEST("Trick swaps held items") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_SITRUS_BERRY); } + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_LUM_BERRY); } + } WHEN { + TURN { MOVE(player, MOVE_TRICK); } + } THEN { + EXPECT(player->item == ITEM_LUM_BERRY); + EXPECT(opponent->item == ITEM_SITRUS_BERRY); + } +} + +SINGLE_BATTLE_TEST("Trick succeeds if only the user has an item") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_SITRUS_BERRY); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TRICK); } + } THEN { + EXPECT(player->item == ITEM_NONE); + EXPECT(opponent->item == ITEM_SITRUS_BERRY); + } +} + +SINGLE_BATTLE_TEST("Trick succeeds if only the target has an item") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_LUM_BERRY); } + } WHEN { + TURN { MOVE(player, MOVE_TRICK); } + } THEN { + EXPECT(player->item == ITEM_LUM_BERRY); + EXPECT(opponent->item == ITEM_NONE); + } +} + +SINGLE_BATTLE_TEST("Trick fails if both battlers have no held item") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TRICK); } + } SCENE { + MESSAGE("But it failed!"); + } THEN { + EXPECT(player->item == ITEM_NONE); + EXPECT(opponent->item == ITEM_NONE); + } +} + +SINGLE_BATTLE_TEST("Trick fails if either item is Mail") +{ + u16 atkItem = ITEM_NONE, defItem = ITEM_NONE; + + ASSUME(ItemIsMail(ITEM_ORANGE_MAIL)); + PARAMETRIZE { atkItem = ITEM_ORANGE_MAIL; defItem = ITEM_NONE; } + PARAMETRIZE { atkItem = ITEM_ORAN_BERRY; defItem = ITEM_ORANGE_MAIL; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(atkItem); } + OPPONENT(SPECIES_WOBBUFFET) { Item(defItem); } + } WHEN { + TURN { MOVE(player, MOVE_TRICK); } + } SCENE { + MESSAGE("But it failed!"); + } THEN { + EXPECT(player->item == atkItem); + EXPECT(opponent->item == defItem); + } +} + +SINGLE_BATTLE_TEST("Trick fails if either item is a Z-Crystal") +{ + u16 atkItem = ITEM_NONE, defItem = ITEM_NONE; + + ASSUME(GetItemHoldEffect(ITEM_FIGHTINIUM_Z) == HOLD_EFFECT_Z_CRYSTAL); + PARAMETRIZE { atkItem = ITEM_FIGHTINIUM_Z; defItem = ITEM_NONE; } + PARAMETRIZE { atkItem = ITEM_SITRUS_BERRY; defItem = ITEM_FIGHTINIUM_Z; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(atkItem); } + OPPONENT(SPECIES_WOBBUFFET) { Item(defItem); } + } WHEN { + TURN { MOVE(player, MOVE_TRICK); } + } SCENE { + MESSAGE("But it failed!"); + } THEN { + EXPECT(player->item == atkItem); + EXPECT(opponent->item == defItem); + } +} + +SINGLE_BATTLE_TEST("Trick fails if either battler holds a Mega Stone") +{ + u16 atkItem = ITEM_NONE, defItem = ITEM_NONE; + u16 atkSpecies = SPECIES_WOBBUFFET, defSpecies = SPECIES_WOBBUFFET; + + PARAMETRIZE { atkSpecies = SPECIES_BLAZIKEN; atkItem = ITEM_BLAZIKENITE; defSpecies = SPECIES_WOBBUFFET; defItem = ITEM_SITRUS_BERRY; } + PARAMETRIZE { atkSpecies = SPECIES_WOBBUFFET; atkItem = ITEM_SITRUS_BERRY; defSpecies = SPECIES_BLAZIKEN; defItem = ITEM_BLAZIKENITE; } + + GIVEN { + PLAYER(atkSpecies) { Item(atkItem); } + OPPONENT(defSpecies) { Item(defItem); } + } WHEN { + TURN { MOVE(player, MOVE_TRICK); } + } SCENE { + MESSAGE("But it failed!"); + } THEN { + EXPECT(player->item == atkItem); + EXPECT(opponent->item == defItem); + } +} + +SINGLE_BATTLE_TEST("Trick fails if an item changes the holder's form") +{ + u16 atkItem = ITEM_NONE, defItem = ITEM_NONE; + + PARAMETRIZE { atkItem = ITEM_GRISEOUS_CORE; defItem = ITEM_SITRUS_BERRY; } + PARAMETRIZE { atkItem = ITEM_SITRUS_BERRY; defItem = ITEM_GRISEOUS_CORE; } + + GIVEN { + PLAYER(SPECIES_GIRATINA_ORIGIN) { Item(atkItem); } + OPPONENT(SPECIES_WOBBUFFET) { Item(defItem); } + } WHEN { + TURN { MOVE(player, MOVE_TRICK); } + } SCENE { + MESSAGE("But it failed!"); + } THEN { + EXPECT(player->item == atkItem); + EXPECT(opponent->item == defItem); + } +} + +SINGLE_BATTLE_TEST("Trick fails if the user has Sticky Hold") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_STICKY_HOLD); Item(ITEM_SITRUS_BERRY); } + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_LUM_BERRY); } + } WHEN { + TURN { MOVE(player, MOVE_TRICK); } + } SCENE { + MESSAGE("But it failed!"); + } THEN { + EXPECT(player->item == ITEM_SITRUS_BERRY); + EXPECT(opponent->item == ITEM_LUM_BERRY); + } +} + +SINGLE_BATTLE_TEST("Trick fails against Sticky Hold") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_SITRUS_BERRY); } + OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_STICKY_HOLD); Item(ITEM_LUM_BERRY); } + } WHEN { + TURN { MOVE(player, MOVE_TRICK); } + } SCENE { + MESSAGE("The opposing Wobbuffet's Sticky Hold made Trick ineffective!"); + } THEN { + EXPECT(player->item == ITEM_SITRUS_BERRY); + EXPECT(opponent->item == ITEM_LUM_BERRY); + } +} + +SINGLE_BATTLE_TEST("Trick fails if the target is behind a Substitute") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_SITRUS_BERRY); Speed(50); } + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_LUM_BERRY); Speed(100); } + } WHEN { + TURN { MOVE(opponent, MOVE_SUBSTITUTE); MOVE(player, MOVE_TRICK); } + } SCENE { + MESSAGE("But it failed!"); + } THEN { + EXPECT(player->item == ITEM_SITRUS_BERRY); + EXPECT(opponent->item == ITEM_LUM_BERRY); + } +}