diff --git a/include/battle.h b/include/battle.h index 4e43cbcfbd..4f7d59a458 100644 --- a/include/battle.h +++ b/include/battle.h @@ -249,6 +249,7 @@ struct AI_ThinkingStruct u8 aiLogicId; u8 simulatedRNG[4]; struct AI_SavedBattleMon saved[4]; + bool8 switchMon; // Because all available moves have no/little effect. }; struct UsedMoves diff --git a/include/battle_ai_script_commands.h b/include/battle_ai_script_commands.h index 47c6bda9c4..eb5d046d89 100644 --- a/include/battle_ai_script_commands.h +++ b/include/battle_ai_script_commands.h @@ -5,6 +5,7 @@ // 0 - 3 are move idx #define AI_CHOICE_FLEE 4 #define AI_CHOICE_WATCH 5 +#define AI_CHOICE_SWITCH 7 s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef); s32 AI_CalcPartyMonDamage(u16 move, u8 battlerAtk, u8 battlerDef, struct Pokemon *mon); diff --git a/src/battle_ai_script_commands.c b/src/battle_ai_script_commands.c index d907eeba60..75fa2f63e0 100644 --- a/src/battle_ai_script_commands.c +++ b/src/battle_ai_script_commands.c @@ -54,6 +54,7 @@ static void RecordLastUsedMoveByTarget(void); static void BattleAI_DoAIProcessing(void); static void AIStackPushVar(const u8 *); static bool8 AIStackPop(void); +static s32 CountUsablePartyMons(u8 battlerId); static void BattleAICmd_if_random_less_than(void); static void BattleAICmd_if_random_greater_than(void); @@ -448,6 +449,28 @@ static u8 ChooseMoveOrAction_Singles(void) if (AI_THINKING_STRUCT->aiAction & AI_ACTION_WATCH) return AI_CHOICE_WATCH; + // Consider switching if all moves are worthless to use. + if (AI_THINKING_STRUCT->aiFlags & (AI_SCRIPT_CHECK_VIABILITY | AI_SCRIPT_CHECK_BAD_MOVE | AI_SCRIPT_TRY_TO_FAINT | AI_SCRIPT_PREFER_BATON_PASS) + && CountUsablePartyMons(sBattler_AI) >= 1 + && gBattleMons[sBattler_AI].hp >= gBattleMons[sBattler_AI].maxHP / 2 + && !(gBattleTypeFlags & BATTLE_TYPE_PALACE)) + { + s32 cap = AI_THINKING_STRUCT->aiFlags & (AI_SCRIPT_CHECK_VIABILITY) ? 95 : 93; + + for (i = 0; i < MAX_MON_MOVES; i++) + { + if (AI_THINKING_STRUCT->score[i] > cap) + break; + } + + gActiveBattler = sBattler_AI; + if (i == MAX_MON_MOVES && GetMostSuitableMonToSwitchInto() != PARTY_SIZE) + { + AI_THINKING_STRUCT->switchMon = TRUE; + return AI_CHOICE_SWITCH; + } + } + numOfBestMoves = 1; currentMoveArray[0] = AI_THINKING_STRUCT->score[0]; consideredMoveArray[0] = 0; @@ -1422,19 +1445,10 @@ static void BattleAICmd_nullsub_2B(void) { } -static void BattleAICmd_count_usable_party_mons(void) +static s32 CountUsablePartyMons(u8 battlerId) { - u8 battlerId; - u8 battlerOnField1, battlerOnField2; + s32 battlerOnField1, battlerOnField2, i, ret; struct Pokemon *party; - s32 i; - - AI_THINKING_STRUCT->funcResult = 0; - - if (gAIScriptPtr[1] == AI_USER) - battlerId = sBattler_AI; - else - battlerId = gBattlerTarget; if (GetBattlerSide(battlerId) == B_SIDE_PLAYER) party = gPlayerParty; @@ -1443,10 +1457,8 @@ static void BattleAICmd_count_usable_party_mons(void) if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) { - u32 position; battlerOnField1 = gBattlerPartyIndexes[battlerId]; - position = GetBattlerPosition(battlerId) ^ BIT_FLANK; - battlerOnField2 = gBattlerPartyIndexes[GetBattlerAtPosition(position)]; + battlerOnField2 = gBattlerPartyIndexes[GetBattlerAtPosition(GetBattlerPosition(battlerId) ^ BIT_FLANK)]; } else // In singles there's only one battlerId by side. { @@ -1454,6 +1466,7 @@ static void BattleAICmd_count_usable_party_mons(void) battlerOnField2 = gBattlerPartyIndexes[battlerId]; } + ret = 0; for (i = 0; i < PARTY_SIZE; i++) { if (i != battlerOnField1 && i != battlerOnField2 @@ -1461,10 +1474,16 @@ static void BattleAICmd_count_usable_party_mons(void) && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_NONE && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_EGG) { - AI_THINKING_STRUCT->funcResult++; + ret++; } } + return ret; +} + +static void BattleAICmd_count_usable_party_mons(void) +{ + AI_THINKING_STRUCT->funcResult = CountUsablePartyMons(BattleAI_GetWantedBattler(gAIScriptPtr[1])); gAIScriptPtr += 2; } diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index a45356502d..02f524a24d 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -37,6 +37,21 @@ void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId) } } +static bool8 ShouldSwitchIfAllBadMoves(void) +{ + if (gBattleResources->ai->switchMon) + { + gBattleResources->ai->switchMon = 0; + *(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = PARTY_SIZE; + BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0); + return TRUE; + } + else + { + return FALSE; + } +} + static bool8 ShouldSwitchIfPerishSong(void) { if (gStatuses3[gActiveBattler] & STATUS3_PERISH_SONG @@ -474,6 +489,8 @@ static bool8 ShouldSwitch(void) if (availableToSwitch == 0) return FALSE; + if (ShouldSwitchIfAllBadMoves()) + return TRUE; if (ShouldSwitchIfPerishSong()) return TRUE; if (ShouldSwitchIfWonderGuard()) diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index 68e6de91e6..d94b47423a 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -1551,6 +1551,9 @@ static void OpponentHandleChooseMove(void) case AI_CHOICE_FLEE: BtlController_EmitTwoReturnValues(1, B_ACTION_RUN, 0); break; + case AI_CHOICE_SWITCH: + BtlController_EmitTwoReturnValues(1, 10, 0xFFFF); + break; case 6: BtlController_EmitTwoReturnValues(1, 15, gBattlerTarget); break;