diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index e4a560ca6e..29b58ed9e6 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -2414,6 +2414,9 @@ u32 GetMostSuitableMonToSwitchInto(u32 battler, enum SwitchType switchType) if (bestMonId != PARTY_SIZE) return bestMonId; + if (aceMonId != PARTY_SIZE && aliveCount == 0) + return aceMonId; + bestMonId = GetBestMonTypeMatchup(party, firstId, lastId, invalidMons, battler, opposingBattler); if (bestMonId != PARTY_SIZE) return bestMonId; diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index 19aeef3f4f..1f1953ea61 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -512,13 +512,57 @@ static inline bool32 IsAcePokemon(u32 chosenMonId, u32 pokemonInBattle, u32 batt && CountAIAliveNonEggMonsExcept(PARTY_SIZE) != pokemonInBattle; } +static inline bool32 IsDoubleAceSlot(u32 battler, u32 partyId) +{ + u32 partyCountEnd; + + if (!(gAiThinkingStruct->aiFlags[battler] & AI_FLAG_DOUBLE_ACE_POKEMON)) + return FALSE; + + partyCountEnd = CalculateEnemyPartyCountInSide(battler); + if (partyCountEnd == 0) + return FALSE; + + if (partyId == partyCountEnd - 1) + return TRUE; + if (partyCountEnd > 1 && partyId == partyCountEnd - 2) + return TRUE; + + return FALSE; +} + static inline bool32 IsDoubleAcePokemon(u32 chosenMonId, u32 pokemonInBattle, u32 battler) { - return gAiThinkingStruct->aiFlags[battler] & AI_FLAG_DOUBLE_ACE_POKEMON - && (chosenMonId == CalculateEnemyPartyCountInSide(battler) - 1) - && (chosenMonId == CalculateEnemyPartyCountInSide(battler) - 2) - && CountAIAliveNonEggMonsExcept(PARTY_SIZE) != pokemonInBattle - && CountAIAliveNonEggMonsExcept(PARTY_SIZE-1) != pokemonInBattle; + s32 battler1, battler2, firstId, lastId; + s32 i; + + if (!IsDoubleAceSlot(battler, chosenMonId)) + return FALSE; + + if (!IsDoubleBattle()) + { + battler2 = battler1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); + } + else + { + battler1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); + battler2 = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); + } + + GetAIPartyIndexes(battler, &firstId, &lastId); + for (i = firstId; i < lastId; i++) + { + if (!IsValidForBattle(&gEnemyParty[i]) + || i == gBattlerPartyIndexes[battler1] + || i == gBattlerPartyIndexes[battler2] + || i == chosenMonId) + continue; + + if (!IsAcePokemon(i, pokemonInBattle, battler) && !IsDoubleAceSlot(battler, i)) + return TRUE; + } + + return FALSE; } static void OpponentHandleChoosePokemon(u32 battler) diff --git a/test/battle/ai/ai_double_ace.c b/test/battle/ai/ai_double_ace.c index 3fa9ce77e0..38b7d1dcd8 100644 --- a/test/battle/ai/ai_double_ace.c +++ b/test/battle/ai/ai_double_ace.c @@ -94,3 +94,35 @@ AI_DOUBLE_BATTLE_TEST("AI_FLAG_DOUBLE_ACE_POKEMON: Ace mons won't be switched in TURN { EXPECT_SWITCH(opponentLeft, 2); } } } + +AI_DOUBLE_BATTLE_TEST("AI_FLAG_DOUBLE_ACE_POKEMON: sends out Ace mons when no other options remain mid-battle") +{ + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_SMART_SWITCHING | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_DOUBLE_ACE_POKEMON); + + PLAYER(SPECIES_WOBBUFFET) { Level(50); Speed(200); Moves(MOVE_THUNDERBOLT, MOVE_CELEBRATE); SpAttack(200); } + PLAYER(SPECIES_WOBBUFFET) { Level(50); Speed(150); Moves(MOVE_THUNDERBOLT, MOVE_CELEBRATE); SpAttack(200); } + + OPPONENT(SPECIES_ZIGZAGOON) { Level(5); HP(1); Speed(1); Moves(MOVE_SPLASH); } + OPPONENT(SPECIES_POOCHYENA) { Level(5); HP(1); Speed(1); Moves(MOVE_SPLASH); } + + // Aces + OPPONENT(SPECIES_MIGHTYENA) { Level(50); Speed(10); Moves(MOVE_CRUNCH); } + OPPONENT(SPECIES_GENGAR) { Level(50); Speed(10); Moves(MOVE_SPLASH); } + } WHEN { + TURN { + MOVE(playerLeft, MOVE_THUNDERBOLT, target: opponentLeft); + MOVE(playerRight, MOVE_CELEBRATE); + EXPECT_MOVE(opponentLeft, MOVE_SPLASH); + EXPECT_MOVE(opponentRight, MOVE_SPLASH); + EXPECT_SEND_OUT(opponentLeft, 3); + } + TURN { + MOVE(playerLeft, MOVE_CELEBRATE); + MOVE(playerRight, MOVE_THUNDERBOLT, target: opponentRight); + EXPECT_MOVE(opponentLeft, MOVE_SPLASH); + EXPECT_MOVE(opponentRight, MOVE_SPLASH); + EXPECT_SEND_OUT(opponentRight, 2); + } + } +}