diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 5fc7e3fcbf..abe8cbf696 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -640,6 +640,7 @@ void SetAiLogicDataForTurn(struct AiLogicData *aiData) u32 battlerAtk, battlersCount, weather; memset(aiData, 0, sizeof(struct AiLogicData)); + gAiBattleData->aiUsingGimmick = 0; if (!(gBattleTypeFlags & BATTLE_TYPE_HAS_AI) && !IsWildMonSmart()) return; diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index 7d150d1a3b..c04b22d417 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -1351,6 +1351,7 @@ void AI_TrySwitchOrUseItem(u32 battler) if (gAiLogicData->shouldSwitch & (1u << battler) && IsSwitchinValid(battler)) { BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, B_ACTION_SWITCH, 0); + SetAIUsingGimmick(battler, NO_GIMMICK); if (gBattleStruct->AI_monToSwitchIntoId[battler] == PARTY_SIZE) { s32 monToSwitchId = gAiLogicData->mostSuitableMonId[battler]; @@ -1397,6 +1398,7 @@ void AI_TrySwitchOrUseItem(u32 battler) } else if (ShouldUseItem(battler)) { + SetAIUsingGimmick(battler, NO_GIMMICK); return; } } diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index e7477f9542..a2d40bc5e1 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -462,12 +462,15 @@ static void OpponentHandleChooseMove(u32 battler) gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); } // If opponent can and should use a gimmick (considering trainer data), do it - if (gBattleStruct->gimmick.usableGimmick[battler] != GIMMICK_NONE && IsAIUsingGimmick(battler)) + enum Gimmick usableGimmick = gBattleStruct->gimmick.usableGimmick[battler]; + if (usableGimmick != GIMMICK_NONE && IsAIUsingGimmick(battler) && !HasTrainerUsedGimmick(battler, usableGimmick)) { + gBattleStruct->gimmick.toActivate |= 1u << battler; BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, B_ACTION_EXEC_SCRIPT, (chosenMoveIndex) | (RET_GIMMICK) | (gBattlerTarget << 8)); } else { + SetAIUsingGimmick(battler, NO_GIMMICK); BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, B_ACTION_EXEC_SCRIPT, (chosenMoveIndex) | (gBattlerTarget << 8)); } } diff --git a/src/battle_controller_player_partner.c b/src/battle_controller_player_partner.c index 6973dbbc28..37af6497df 100644 --- a/src/battle_controller_player_partner.c +++ b/src/battle_controller_player_partner.c @@ -269,12 +269,15 @@ static void PlayerPartnerHandleChooseMove(u32 battler) gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); } // If partner can and should use a gimmick (considering trainer data), do it - if (gBattleStruct->gimmick.usableGimmick[battler] != GIMMICK_NONE && IsAIUsingGimmick(battler)) + enum Gimmick usableGimmick = gBattleStruct->gimmick.usableGimmick[battler]; + if (usableGimmick != GIMMICK_NONE && IsAIUsingGimmick(battler) && !HasTrainerUsedGimmick(battler, usableGimmick)) { + gBattleStruct->gimmick.toActivate |= 1u << battler; BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, B_ACTION_EXEC_SCRIPT, (chosenMoveIndex) | (RET_GIMMICK) | (gBattlerTarget << 8)); } else { + SetAIUsingGimmick(battler, NO_GIMMICK); BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, B_ACTION_EXEC_SCRIPT, (chosenMoveIndex) | (gBattlerTarget << 8)); } diff --git a/src/battle_gimmick.c b/src/battle_gimmick.c index 38d60d4365..3969932faf 100644 --- a/src/battle_gimmick.c +++ b/src/battle_gimmick.c @@ -91,20 +91,15 @@ bool32 ShouldTrainerBattlerUseGimmick(u32 battler, enum Gimmick gimmick) // Returns whether a trainer has used a gimmick during a battle. bool32 HasTrainerUsedGimmick(u32 battler, enum Gimmick gimmick) { - // Check whether partner battler has used gimmick or plans to during turn. - if (IsDoubleBattle() - && IsPartnerMonFromSameTrainer(battler) - && (gBattleStruct->gimmick.activated[BATTLE_PARTNER(battler)][gimmick] - || ((gBattleStruct->gimmick.toActivate & (1u << BATTLE_PARTNER(battler)) - && gBattleStruct->gimmick.usableGimmick[BATTLE_PARTNER(battler)] == gimmick)))) + if (IsDoubleBattle() && IsPartnerMonFromSameTrainer(battler)) { - return TRUE; - } - // Otherwise, return whether current battler has used gimmick. - else - { - return gBattleStruct->gimmick.activated[battler][gimmick]; + u32 partner = BATTLE_PARTNER(battler); + if (gBattleStruct->gimmick.activated[partner][gimmick] + || ((gBattleStruct->gimmick.toActivate & (1u << partner)) && gBattleStruct->gimmick.usableGimmick[partner] == gimmick)) + return TRUE; } + + return gBattleStruct->gimmick.activated[battler][gimmick]; } // Sets a gimmick as used by a trainer with checks for Multi Battles. diff --git a/src/battle_util.c b/src/battle_util.c index 95514cd82a..68e89fc93b 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -8938,14 +8938,10 @@ s32 GetStealthHazardDamage(enum TypeSideHazard hazardType, u32 battler) bool32 IsPartnerMonFromSameTrainer(u32 battler) { - if (!IsOnPlayerSide(battler) && gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) - return FALSE; - else if (IsOnPlayerSide(battler) && gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) - return FALSE; - else if (gBattleTypeFlags & BATTLE_TYPE_MULTI) - return FALSE; + if (!IsOnPlayerSide(battler)) + return !(gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS); else - return TRUE; + return !(gBattleTypeFlags & BATTLE_TYPE_MULTI); } bool32 DoesSpeciesUseHoldItemToChangeForm(u16 species, u16 heldItemId) diff --git a/test/battle/ai/gimmick_dynamax.c b/test/battle/ai/gimmick_dynamax.c index 7e99a883c4..99e6157c94 100644 --- a/test/battle/ai/gimmick_dynamax.c +++ b/test/battle/ai/gimmick_dynamax.c @@ -37,5 +37,29 @@ AI_SINGLE_BATTLE_TEST("AI uses Dynamax -- AI does not dynamax before using a uti } } +AI_TWO_VS_ONE_BATTLE_TEST("AI only Dynamaxes once per trainer in 2v1 multi battles") +{ + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); + MULTI_PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_SPLASH); } + MULTI_PARTNER(SPECIES_WOBBUFFET) { Moves(MOVE_SPLASH); } + MULTI_OPPONENT_A(SPECIES_WOBBUFFET) { Moves(MOVE_SPLASH); DynamaxLevel(10); } + MULTI_OPPONENT_A(SPECIES_WOBBUFFET) { Moves(MOVE_SPLASH); DynamaxLevel(10); } + } WHEN { + TURN { + MOVE(playerLeft, MOVE_SPLASH); + MOVE(playerRight, MOVE_SPLASH); + EXPECT_MOVE(opponentLeft, MOVE_SPLASH, gimmick: GIMMICK_DYNAMAX); + EXPECT_MOVE(opponentRight, MOVE_SPLASH, gimmick: GIMMICK_NONE); + } + TURN { + MOVE(playerLeft, MOVE_SPLASH); + MOVE(playerRight, MOVE_SPLASH); + EXPECT_MOVE(opponentLeft, MOVE_SPLASH, gimmick: GIMMICK_NONE); + EXPECT_MOVE(opponentRight, MOVE_SPLASH, gimmick: GIMMICK_NONE); + } + } +} + // Copycatting an ally's Max Guard rendition of Trick Room was a notable strategy. TO_DO_BATTLE_TEST("TODO: AI uses Dynamax -- AI uses Copycat against a Dynamaxed Pokemon intelligently")