diff --git a/include/constants/battle_ai.h b/include/constants/battle_ai.h index d48827683a..a7a3e54183 100644 --- a/include/constants/battle_ai.h +++ b/include/constants/battle_ai.h @@ -56,6 +56,7 @@ #define AI_FLAG_STALL (1 << 13) // AI stalls battle and prefers secondary damage/trapping/etc. TODO not finished #define AI_FLAG_SCREENER (1 << 14) // AI prefers screening effects like reflect, mist, etc. TODO unfinished #define AI_FLAG_SMART_SWITCHING (1 << 15) // AI includes a lot more switching checks +#define AI_FLAG_ACE_POKEMON (1 << 16) // AI has Ace Pokemon -- Saves until last // 'other' ai logic flags #define AI_FLAG_ROAMING (1 << 29) diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index 3b00340d27..329d3ebf35 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -199,6 +199,10 @@ static bool8 FindMonThatAbsorbsOpponentsMove(void) continue; if (i == *(gBattleStruct->monToSwitchIntoId + battlerIn2)) continue; + if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_ACE_POKEMON + && i == (CalculateEnemyPartyCount()-1)) + continue; + species = GetMonData(&party[i], MON_DATA_SPECIES); if (GetMonData(&party[i], MON_DATA_ABILITY_NUM) != 0) @@ -276,6 +280,10 @@ static bool8 ShouldSwitchIfGameStatePrompt(void) for (i = firstId; i < lastId; i++) { + if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_ACE_POKEMON + && i == (CalculateEnemyPartyCount()-1)) + break; + //Look for mon in party that is able to be switched into and has ability that sets terrain if (GetMonData(&party[i], MON_DATA_HP) != 0 && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_NONE @@ -562,6 +570,10 @@ static bool8 FindMonWithFlagsAndSuperEffective(u16 flags, u8 moduloPercent) continue; if (i == *(gBattleStruct->monToSwitchIntoId + battlerIn2)) continue; + if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_ACE_POKEMON + && i == (CalculateEnemyPartyCount()-1)) + continue; + species = GetMonData(&party[i], MON_DATA_SPECIES); if (GetMonData(&party[i], MON_DATA_ABILITY_NUM) != 0) @@ -650,6 +662,9 @@ bool32 ShouldSwitch(void) continue; if (i == *(gBattleStruct->monToSwitchIntoId + battlerIn2)) continue; + if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_ACE_POKEMON + && i == (CalculateEnemyPartyCount()-1)) + continue; availableToSwitch++; } @@ -712,7 +727,7 @@ void AI_TrySwitchOrUseItem(void) GetAIPartyIndexes(gActiveBattler, &firstId, &lastId); - for (monToSwitchId = firstId; monToSwitchId < lastId; monToSwitchId++) + for (monToSwitchId = (lastId-1); monToSwitchId >= firstId; monToSwitchId--) { if (GetMonData(&party[monToSwitchId], MON_DATA_HP) == 0) continue; @@ -724,6 +739,9 @@ void AI_TrySwitchOrUseItem(void) continue; if (monToSwitchId == *(gBattleStruct->monToSwitchIntoId + battlerIn2)) continue; + if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_ACE_POKEMON + && monToSwitchId == (CalculateEnemyPartyCount()-1)) + continue; break; } @@ -921,7 +939,9 @@ u8 GetMostSuitableMonToSwitchInto(void) || gBattlerPartyIndexes[battlerIn2] == i || i == *(gBattleStruct->monToSwitchIntoId + battlerIn1) || i == *(gBattleStruct->monToSwitchIntoId + battlerIn2) - || (GetMonAbility(&party[i]) == ABILITY_TRUANT && IsTruantMonVulnerable(gActiveBattler, opposingBattler))) // While not really invalid per say, not really wise to switch into this mon. + || (GetMonAbility(&party[i]) == ABILITY_TRUANT && IsTruantMonVulnerable(gActiveBattler, opposingBattler)) // While not really invalid per say, not really wise to switch into this mon. + || (AI_THINKING_STRUCT->aiFlags & AI_FLAG_ACE_POKEMON + && i == (CalculateEnemyPartyCount()-1))) //Save Ace Pokemon for last invalidMons |= gBitTable[i]; else aliveCount++; diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index 501e31704c..ddc4c44052 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -2,6 +2,7 @@ #include "battle.h" #include "battle_ai_main.h" #include "battle_ai_util.h" +#include "constants/battle_ai.h" #include "battle_anim.h" #include "battle_arena.h" #include "battle_controllers.h" @@ -94,6 +95,7 @@ static void OpponentHandleResetActionMoveSelection(void); static void OpponentHandleEndLinkBattle(void); static void OpponentHandleDebugMenu(void); static void OpponentCmdEnd(void); +static u8 CountAIAliveNonEggMonsExcept(u8 slotToIgnore); static void OpponentBufferRunCommand(void); static void OpponentBufferExecCompleted(void); @@ -1670,6 +1672,7 @@ static void OpponentHandleChooseItem(void) static void OpponentHandleChoosePokemon(void) { s32 chosenMonId; + s32 pokemonInBattle = 1; if (*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) == PARTY_SIZE) { @@ -1687,15 +1690,20 @@ static void OpponentHandleChoosePokemon(void) { battler1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); battler2 = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); + pokemonInBattle = 2; + } GetAIPartyIndexes(gActiveBattler, &firstId, &lastId); - for (chosenMonId = firstId; chosenMonId < lastId; chosenMonId++) + for (chosenMonId = (lastId-1); chosenMonId >= firstId; chosenMonId--) { if (GetMonData(&gEnemyParty[chosenMonId], MON_DATA_HP) != 0 && chosenMonId != gBattlerPartyIndexes[battler1] - && chosenMonId != gBattlerPartyIndexes[battler2]) + && chosenMonId != gBattlerPartyIndexes[battler2] + && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_ACE_POKEMON + && (!(chosenMonId == (CalculateEnemyPartyCount()-1)) + || CountAIAliveNonEggMonsExcept(PARTY_SIZE) == pokemonInBattle))) { break; } @@ -1714,6 +1722,24 @@ static void OpponentHandleChoosePokemon(void) OpponentBufferExecCompleted(); } +static u8 CountAIAliveNonEggMonsExcept(u8 slotToIgnore) +{ + u16 i, count; + + for (i = 0, count = 0; i < PARTY_SIZE; i++) + { + if (i != slotToIgnore + && GetMonData(&gEnemyParty[i], MON_DATA_SPECIES) != SPECIES_NONE + && !GetMonData(&gEnemyParty[i], MON_DATA_IS_EGG) + && GetMonData(&gEnemyParty[i], MON_DATA_HP) != 0) + { + count++; + } + } + + return count; +} + static void OpponentHandleCmd23(void) { OpponentBufferExecCompleted();