diff --git a/include/battle_ai_switch_items.h b/include/battle_ai_switch_items.h index 28eb318b2fc..d5fbc701a5b 100644 --- a/include/battle_ai_switch_items.h +++ b/include/battle_ai_switch_items.h @@ -1,9 +1,15 @@ #ifndef GUARD_BATTLE_AI_SWITCH_ITEMS_H #define GUARD_BATTLE_AI_SWITCH_ITEMS_H +enum SwitchType +{ + SWITCH_AFTER_KO, + SWITCH_MID_BATTLE, +}; + void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId); void AI_TrySwitchOrUseItem(u32 battler); -u32 GetMostSuitableMonToSwitchInto(u32 battler, bool32 switchAfterMonKOd); +u32 GetMostSuitableMonToSwitchInto(u32 battler, enum SwitchType switchType); bool32 ShouldSwitch(u32 battler); bool32 IsMonGrounded(u16 heldItemEffect, u32 ability, u8 type1, u8 type2); diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index c2f742d2732..6e754121c0c 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -1732,7 +1732,7 @@ static bool32 CanAbilityTrapOpponent(u16 ability, u32 opponent) return FALSE; } -static inline bool32 IsFreeSwitch(bool32 isSwitchAfterKO, u32 battlerSwitchingOut, u32 opposingBattler) +static inline bool32 IsFreeSwitch(enum SwitchType switchType, u32 battlerSwitchingOut, u32 opposingBattler) { bool32 movedSecond = GetBattlerTurnOrderNum(battlerSwitchingOut) > GetBattlerTurnOrderNum(opposingBattler) ? TRUE : FALSE; @@ -1755,7 +1755,7 @@ static inline bool32 IsFreeSwitch(bool32 isSwitchAfterKO, u32 battlerSwitchingOu } // Post KO check has to be last because the GetMostSuitableMonToSwitchInto call in OpponentHandleChoosePokemon assumes a KO rather than a forced switch choice - if (isSwitchAfterKO) + if (switchType == SWITCH_AFTER_KO) return TRUE; else return FALSE; @@ -1777,7 +1777,7 @@ static inline bool32 CanSwitchinWin1v1(u32 hitsToKOAI, u32 hitsToKOPlayer, bool3 // This function splits switching behaviour depending on whether the switch is free. // Everything runs in the same loop to minimize computation time. This makes it harder to read, but hopefully the comments can guide you! -static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId, u32 battler, u32 opposingBattler, u32 battlerIn1, u32 battlerIn2, bool32 isSwitchAfterKO) +static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId, u32 battler, u32 opposingBattler, u32 battlerIn1, u32 battlerIn2, enum SwitchType switchType) { int revengeKillerId = PARTY_SIZE, slowRevengeKillerId = PARTY_SIZE, fastThreatenId = PARTY_SIZE, slowThreatenId = PARTY_SIZE, damageMonId = PARTY_SIZE; int batonPassId = PARTY_SIZE, typeMatchupId = PARTY_SIZE, typeMatchupEffectiveId = PARTY_SIZE, defensiveMonId = PARTY_SIZE, aceMonId = PARTY_SIZE, trapperId = PARTY_SIZE; @@ -1786,7 +1786,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId, s32 playerMonHP = gBattleMons[opposingBattler].hp, maxDamageDealt = 0, damageDealt = 0; u32 aiMove, hitsToKOAI, maxHitsToKO = 0; u16 bestResist = UQ_4_12(1.0), bestResistEffective = UQ_4_12(1.0), typeMatchup; - bool32 isFreeSwitch = IsFreeSwitch(isSwitchAfterKO, battlerIn1, opposingBattler), isSwitchinFirst, canSwitchinWin1v1; + bool32 isFreeSwitch = IsFreeSwitch(switchType, battlerIn1, opposingBattler), isSwitchinFirst, canSwitchinWin1v1; // Iterate through mons for (i = firstId; i < lastId; i++) @@ -1979,7 +1979,7 @@ static u32 GetNextMonInParty(struct Pokemon *party, int firstId, int lastId, u32 return PARTY_SIZE; } -u32 GetMostSuitableMonToSwitchInto(u32 battler, bool32 switchAfterMonKOd) +u32 GetMostSuitableMonToSwitchInto(u32 battler, enum SwitchType switchType) { u32 opposingBattler = 0; u32 bestMonId = PARTY_SIZE; @@ -2024,7 +2024,7 @@ u32 GetMostSuitableMonToSwitchInto(u32 battler, bool32 switchAfterMonKOd) // Only use better mon selection if AI_FLAG_SMART_MON_CHOICES is set for the trainer. if (AI_THINKING_STRUCT->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SMART_MON_CHOICES && !IsDoubleBattle()) // Double Battles aren't included in AI_FLAG_SMART_MON_CHOICE. Defaults to regular switch in logic { - bestMonId = GetBestMonIntegrated(party, firstId, lastId, battler, opposingBattler, battlerIn1, battlerIn2, switchAfterMonKOd); + bestMonId = GetBestMonIntegrated(party, firstId, lastId, battler, opposingBattler, battlerIn1, battlerIn2, switchType); return bestMonId; } diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index adeae79652e..28e572d1a50 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -680,7 +680,7 @@ static void OpponentHandleChoosePokemon(u32 battler) // Switching out else if (gBattleStruct->AI_monToSwitchIntoId[battler] == PARTY_SIZE) { - chosenMonId = GetMostSuitableMonToSwitchInto(battler, TRUE); + chosenMonId = GetMostSuitableMonToSwitchInto(battler, SWITCH_AFTER_KO); if (chosenMonId == PARTY_SIZE) { s32 battler1, battler2, firstId, lastId; diff --git a/src/battle_controller_player_partner.c b/src/battle_controller_player_partner.c index 4c3163dd9ad..a57bb8e2a52 100644 --- a/src/battle_controller_player_partner.c +++ b/src/battle_controller_player_partner.c @@ -387,7 +387,7 @@ static void PlayerPartnerHandleChoosePokemon(u32 battler) // Switching out else if (gBattleStruct->monToSwitchIntoId[battler] >= PARTY_SIZE || !IsValidForBattle(&gPlayerParty[gBattleStruct->monToSwitchIntoId[battler]])) { - chosenMonId = GetMostSuitableMonToSwitchInto(battler, TRUE); + chosenMonId = GetMostSuitableMonToSwitchInto(battler, SWITCH_AFTER_KO); if (chosenMonId == PARTY_SIZE || !IsValidForBattle(&gPlayerParty[chosenMonId])) // just switch to the next mon { diff --git a/src/battle_main.c b/src/battle_main.c index f47c54bcf51..6e51a4d69f0 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -4145,7 +4145,7 @@ enum STATE_SELECTION_SCRIPT_MAY_RUN }; -void SetupAISwitchingData(u32 battler, bool32 isAiRisky) +void SetupAISwitchingData(u32 battler, enum SwitchType switchType) { s32 opposingBattler = GetBattlerAtPosition(BATTLE_OPPOSITE(GetBattlerPosition(battler))); @@ -4154,7 +4154,7 @@ void SetupAISwitchingData(u32 battler, bool32 isAiRisky) { AI_DATA->aiSwitchPredictionInProgress = TRUE; AI_DATA->battlerDoingPrediction = battler; - AI_DATA->mostSuitableMonId[opposingBattler] = GetMostSuitableMonToSwitchInto(opposingBattler, isAiRisky); + AI_DATA->mostSuitableMonId[opposingBattler] = GetMostSuitableMonToSwitchInto(opposingBattler, switchType); if (ShouldSwitch(opposingBattler)) AI_DATA->shouldSwitch |= (1u << opposingBattler); AI_DATA->aiSwitchPredictionInProgress = FALSE; @@ -4164,7 +4164,7 @@ void SetupAISwitchingData(u32 battler, bool32 isAiRisky) } // AI's data - AI_DATA->mostSuitableMonId[battler] = GetMostSuitableMonToSwitchInto(battler, isAiRisky); + AI_DATA->mostSuitableMonId[battler] = GetMostSuitableMonToSwitchInto(battler, switchType); if (ShouldSwitch(battler)) AI_DATA->shouldSwitch |= (1u << battler); } @@ -4182,7 +4182,7 @@ static void HandleTurnActionSelectionState(void) case STATE_TURN_START_RECORD: // Recorded battle related action on start of every turn. RecordedBattle_CopyBattlerMoves(battler); gBattleCommunication[battler] = STATE_BEFORE_ACTION_CHOSEN; - u32 isAiRisky = AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_RISKY; // Risky AI switches aggressively even mid battle + enum SwitchType switchType = (AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_RISKY) ? SWITCH_AFTER_KO : SWITCH_MID_BATTLE; // Risky AI switches aggressively even mid battle // Do AI score computations here so we can use them in AI_TrySwitchOrUseItem if ((gBattleTypeFlags & BATTLE_TYPE_HAS_AI || IsWildMonSmart()) @@ -4192,7 +4192,7 @@ static void HandleTurnActionSelectionState(void) // Setup battler data BattleAI_SetupAIData(0xF, battler); - SetupAISwitchingData(battler, isAiRisky); + SetupAISwitchingData(battler, switchType); // Do scoring gBattleStruct->aiMoveOrAction[battler] = BattleAI_ChooseMoveOrAction(battler);