diff --git a/include/battle.h b/include/battle.h index 4fa543053b..56ea4f254b 100644 --- a/include/battle.h +++ b/include/battle.h @@ -333,7 +333,7 @@ struct AiLogicData u8 ejectPackSwitch:1; // Tracks whether current switch out was from Eject Pack u8 predictingSwitch:1; // Determines whether AI will use switch predictions this turn or not u8 predictingMove:1; // Determines whether AI will use move predictions this turn or not - u8 aiSwitchPredictionInProgress:1; // Tracks whether the AI is in the middle of running prediction calculations + u8 aiPredictionInProgress:1; // Tracks whether the AI is in the middle of running prediction calculations u8 padding:2; u8 shouldSwitch; // Stores result of ShouldSwitch, which decides whether a mon should be switched out u8 aiCalcInProgress:1; diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index 17adb67712..ca3f7c1d1e 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -240,5 +240,6 @@ bool32 IsBattlerItemEnabled(u32 battler); bool32 IsBattlerPredictedToSwitch(u32 battler); bool32 HasLowAccuracyMove(u32 battlerAtk, u32 battlerDef); bool32 HasBattlerSideAbility(u32 battlerDef, u32 ability, struct AiLogicData *aiData); +u32 GetThinkingBattler(u32 battler); #endif //GUARD_BATTLE_AI_UTIL_H diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 2af6e3067d..f07ed8a03b 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -32,11 +32,10 @@ #define AI_ACTION_WATCH (1 << 2) #define AI_ACTION_DO_NOT_ATTACK (1 << 3) -static u32 ChooseMoveOrAction_Singles(u32 battlerAi); -static u32 ChooseMoveOrAction_Doubles(u32 battlerAi); -static inline void BattleAI_DoAIProcessing(struct AI_ThinkingStruct *aiThink, u32 battlerAi, u32 battlerDef); -static inline void BattleAI_DoAIProcessing_PredictMove(struct AI_ThinkingStruct *aiThink, u32 battler, u32 opposingBattler); -static inline void BattleAI_DoAIProcessing_PredictedSwitchin(struct AI_ThinkingStruct *aiThink, struct AiLogicData *aiData, u32 battlerAi, u32 battlerDef); +static u32 ChooseMoveOrAction_Singles(u32 battler); +static u32 ChooseMoveOrAction_Doubles(u32 battler); +static inline void BattleAI_DoAIProcessing(struct AI_ThinkingStruct *aiThink, u32 battler, u32 battlerDef); +static inline void BattleAI_DoAIProcessing_PredictedSwitchin(struct AI_ThinkingStruct *aiThink, struct AiLogicData *aiData, u32 battlerAtk, u32 battlerDef); static bool32 IsPinchBerryItemEffect(enum ItemHoldEffect holdEffect); // ewram @@ -308,16 +307,15 @@ bool32 BattlerChoseNonMoveAction(void) void SetupAIPredictionData(u32 battler, enum SwitchType switchType) { s32 opposingBattler = GetOppositeBattler(battler); - + AI_DATA->aiPredictionInProgress = TRUE; + AI_DATA->battlerDoingPrediction = battler; + // Switch prediction if ((AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_PREDICT_SWITCH)) { - AI_DATA->aiSwitchPredictionInProgress = TRUE; - AI_DATA->battlerDoingPrediction = battler; AI_DATA->mostSuitableMonId[opposingBattler] = GetMostSuitableMonToSwitchInto(opposingBattler, switchType); if (ShouldSwitch(opposingBattler)) AI_DATA->shouldSwitch |= (1u << opposingBattler); - AI_DATA->aiSwitchPredictionInProgress = FALSE; gBattleStruct->prevTurnSpecies[opposingBattler] = gBattleMons[opposingBattler].species; // Determine whether AI will use predictions this turn @@ -327,13 +325,14 @@ void SetupAIPredictionData(u32 battler, enum SwitchType switchType) // Move prediction if (AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_PREDICT_MOVES) { - AI_DATA->predictedMove[opposingBattler] = gBattleMons[opposingBattler].moves[BattleAI_PredictMove(battler, opposingBattler)]; + AI_DATA->predictedMove[opposingBattler] = gBattleMons[opposingBattler].moves[BattleAI_ChooseMoveIndex(opposingBattler)]; DebugPrintf("Predicted move: %d", AI_DATA->predictedMove[opposingBattler]); ModifySwitchAfterMoveScoring(opposingBattler); // Determine whether AI will use predictions this turn AI_DATA->predictingMove = RandomPercentage(RNG_AI_PREDICT_MOVE, PREDICT_MOVE_CHANCE); } + AI_DATA->aiPredictionInProgress = FALSE; } void ComputeBattlerDecisions(u32 battler) @@ -640,77 +639,23 @@ static u32 PpStallReduction(u32 move, u32 battlerAtk) return returnValue; } -u32 BattleAI_PredictMove(u32 battler, u32 opposingBattler) +static u32 ChooseMoveOrAction_Singles(u32 battler) { u8 currentMoveArray[MAX_MON_MOVES]; u8 consideredMoveArray[MAX_MON_MOVES]; u32 numOfBestMoves; s32 i; - u32 flags = AI_THINKING_STRUCT->aiFlags[battler]; + u64 flags = AI_THINKING_STRUCT->aiFlags[GetThinkingBattler(battler)]; AI_DATA->partnerMove = 0; // no ally while (flags != 0) { if (flags & 1) { - BattleAI_DoAIProcessing_PredictMove(AI_THINKING_STRUCT, battler, opposingBattler); - } - flags >>= 1; - AI_THINKING_STRUCT->aiLogicId++; - } - - for (i = 0; i < MAX_MON_MOVES; i++) - { - gAiBattleData->finalScore[opposingBattler][battler][i] = AI_THINKING_STRUCT->score[i]; - DebugPrintf("Final score: %d", gAiBattleData->finalScore[opposingBattler][battler][i]); - } - - numOfBestMoves = 1; - currentMoveArray[0] = AI_THINKING_STRUCT->score[0]; - consideredMoveArray[0] = 0; - - for (i = 1; i < MAX_MON_MOVES; i++) - { - if (gBattleMons[opposingBattler].moves[i] != MOVE_NONE) - { - // In ruby, the order of these if statements is reversed. - if (currentMoveArray[0] == AI_THINKING_STRUCT->score[i]) - { - currentMoveArray[numOfBestMoves] = AI_THINKING_STRUCT->score[i]; - consideredMoveArray[numOfBestMoves++] = i; - } - if (currentMoveArray[0] < AI_THINKING_STRUCT->score[i]) - { - numOfBestMoves = 1; - currentMoveArray[0] = AI_THINKING_STRUCT->score[i]; - consideredMoveArray[0] = i; - } - } - } - DebugPrintf("Number of best moves: %d", numOfBestMoves); - DebugPrintf("Random best move: %d", consideredMoveArray[Random() % numOfBestMoves]); - DebugPrintf("Random best move: %d", consideredMoveArray[Random() % numOfBestMoves]); - DebugPrintf("Random best move: %d", consideredMoveArray[Random() % numOfBestMoves]); - return consideredMoveArray[Random() % numOfBestMoves]; -} - -static u32 ChooseMoveOrAction_Singles(u32 battlerAi) -{ - u8 currentMoveArray[MAX_MON_MOVES]; - u8 consideredMoveArray[MAX_MON_MOVES]; - u32 numOfBestMoves; - s32 i; - u64 flags = AI_THINKING_STRUCT->aiFlags[battlerAi]; - - AI_DATA->partnerMove = 0; // no ally - while (flags != 0) - { - if (flags & 1) - { - if (IsBattlerPredictedToSwitch(gBattlerTarget) && (AI_THINKING_STRUCT->aiFlags[battlerAi] & AI_FLAG_PREDICT_INCOMING_MON)) - BattleAI_DoAIProcessing_PredictedSwitchin(AI_THINKING_STRUCT, AI_DATA, battlerAi, gBattlerTarget); + if (IsBattlerPredictedToSwitch(gBattlerTarget) && (AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_PREDICT_INCOMING_MON)) + BattleAI_DoAIProcessing_PredictedSwitchin(AI_THINKING_STRUCT, AI_DATA, battler, gBattlerTarget); else - BattleAI_DoAIProcessing(AI_THINKING_STRUCT, battlerAi, gBattlerTarget); + BattleAI_DoAIProcessing(AI_THINKING_STRUCT, battler, gBattlerTarget); } flags >>= (u64)1; AI_THINKING_STRUCT->aiLogicId++; @@ -718,7 +663,7 @@ static u32 ChooseMoveOrAction_Singles(u32 battlerAi) for (i = 0; i < MAX_MON_MOVES; i++) { - gAiBattleData->finalScore[battlerAi][gBattlerTarget][i] = AI_THINKING_STRUCT->score[i]; + gAiBattleData->finalScore[battler][gBattlerTarget][i] = AI_THINKING_STRUCT->score[i]; } numOfBestMoves = 1; @@ -727,7 +672,7 @@ static u32 ChooseMoveOrAction_Singles(u32 battlerAi) for (i = 1; i < MAX_MON_MOVES; i++) { - if (gBattleMons[battlerAi].moves[i] != MOVE_NONE) + if (gBattleMons[battler].moves[i] != MOVE_NONE) { // In ruby, the order of these if statements is reversed. if (currentMoveArray[0] == AI_THINKING_STRUCT->score[i]) @@ -746,7 +691,7 @@ static u32 ChooseMoveOrAction_Singles(u32 battlerAi) return consideredMoveArray[Random() % numOfBestMoves]; } -static u32 ChooseMoveOrAction_Doubles(u32 battlerAi) +static u32 ChooseMoveOrAction_Doubles(u32 battler) { s32 i, j; u64 flags; @@ -761,7 +706,7 @@ static u32 ChooseMoveOrAction_Doubles(u32 battlerAi) for (i = 0; i < MAX_BATTLERS_COUNT; i++) { - if (i == battlerAi || gBattleMons[i].hp == 0) + if (i == battler || gBattleMons[i].hp == 0) { actionOrMoveIndex[i] = 0xFF; bestMovePointsForTarget[i] = -1; @@ -769,25 +714,25 @@ static u32 ChooseMoveOrAction_Doubles(u32 battlerAi) else { if (gBattleTypeFlags & BATTLE_TYPE_PALACE) - BattleAI_SetupAIData(gBattleStruct->palaceFlags >> 4, battlerAi); + BattleAI_SetupAIData(gBattleStruct->palaceFlags >> 4, battler); else - BattleAI_SetupAIData(0xF, battlerAi); + BattleAI_SetupAIData(0xF, battler); gBattlerTarget = i; - AI_DATA->partnerMove = GetAllyChosenMove(battlerAi); + AI_DATA->partnerMove = GetAllyChosenMove(battler); AI_THINKING_STRUCT->aiLogicId = 0; AI_THINKING_STRUCT->movesetIndex = 0; - flags = AI_THINKING_STRUCT->aiFlags[battlerAi]; + flags = AI_THINKING_STRUCT->aiFlags[GetThinkingBattler(battler)]; while (flags != 0) { if (flags & 1) { - if (IsBattlerPredictedToSwitch(gBattlerTarget) && (AI_THINKING_STRUCT->aiFlags[battlerAi] & AI_FLAG_PREDICT_INCOMING_MON)) - BattleAI_DoAIProcessing_PredictedSwitchin(AI_THINKING_STRUCT, AI_DATA, battlerAi, gBattlerTarget); + if (IsBattlerPredictedToSwitch(gBattlerTarget) && (AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_PREDICT_INCOMING_MON)) + BattleAI_DoAIProcessing_PredictedSwitchin(AI_THINKING_STRUCT, AI_DATA, battler, gBattlerTarget); else - BattleAI_DoAIProcessing(AI_THINKING_STRUCT, battlerAi, gBattlerTarget); + BattleAI_DoAIProcessing(AI_THINKING_STRUCT, battler, gBattlerTarget); } flags >>= (u64)1; AI_THINKING_STRUCT->aiLogicId++; @@ -798,9 +743,9 @@ static u32 ChooseMoveOrAction_Doubles(u32 battlerAi) mostViableMovesNo = 1; for (j = 1; j < MAX_MON_MOVES; j++) { - if (gBattleMons[battlerAi].moves[j] != 0) + if (gBattleMons[battler].moves[j] != 0) { - if (!CanTargetBattler(battlerAi, i, gBattleMons[battlerAi].moves[j])) + if (!CanTargetBattler(battler, i, gBattleMons[battler].moves[j])) continue; if (mostViableMovesScores[0] == AI_THINKING_STRUCT->score[j]) @@ -821,14 +766,14 @@ static u32 ChooseMoveOrAction_Doubles(u32 battlerAi) bestMovePointsForTarget[i] = mostViableMovesScores[0]; // Don't use a move against ally if it has less than 100 points. - if (i == BATTLE_PARTNER(battlerAi) && bestMovePointsForTarget[i] < AI_SCORE_DEFAULT) + if (i == BATTLE_PARTNER(battler) && bestMovePointsForTarget[i] < AI_SCORE_DEFAULT) { bestMovePointsForTarget[i] = -1; } for (j = 0; j < MAX_MON_MOVES; j++) { - gAiBattleData->finalScore[battlerAi][gBattlerTarget][j] = AI_THINKING_STRUCT->score[j]; + gAiBattleData->finalScore[battler][gBattlerTarget][j] = AI_THINKING_STRUCT->score[j]; } } } @@ -853,7 +798,7 @@ static u32 ChooseMoveOrAction_Doubles(u32 battlerAi) } gBattlerTarget = mostViableTargetsArray[Random() % mostViableTargetsNo]; - gAiBattleData->chosenTarget[battlerAi] = gBattlerTarget; + gAiBattleData->chosenTarget[battler] = gBattlerTarget; return actionOrMoveIndex[gBattlerTarget]; } @@ -868,64 +813,26 @@ static inline bool32 ShouldConsiderMoveForBattler(u32 battlerAi, u32 battlerDef, return TRUE; } -static inline void BattleAI_DoAIProcessing_PredictMove(struct AI_ThinkingStruct *aiThink, u32 battler, u32 opposingBattler) +static inline void BattleAI_DoAIProcessing(struct AI_ThinkingStruct *aiThink, u32 battler, u32 battlerDef) { do { - if (gBattleMons[opposingBattler].pp[aiThink->movesetIndex] == 0) + if (gBattleMons[battler].pp[aiThink->movesetIndex] == 0) aiThink->moveConsidered = MOVE_NONE; else - aiThink->moveConsidered = gBattleMons[opposingBattler].moves[aiThink->movesetIndex]; - - DebugPrintf("Move considered: %d", aiThink->moveConsidered); + aiThink->moveConsidered = gBattleMons[battler].moves[aiThink->movesetIndex]; // There is no point in calculating scores for all 3 battlers(2 opponents + 1 ally) with certain moves. if (aiThink->moveConsidered != MOVE_NONE && aiThink->score[aiThink->movesetIndex] > 0 - && ShouldConsiderMoveForBattler(opposingBattler, battler, aiThink->moveConsidered)) + && ShouldConsiderMoveForBattler(battler, battlerDef, aiThink->moveConsidered)) { if (aiThink->aiLogicId < ARRAY_COUNT(sBattleAiFuncTable) && sBattleAiFuncTable[aiThink->aiLogicId] != NULL) { // Call AI function aiThink->score[aiThink->movesetIndex] = - sBattleAiFuncTable[aiThink->aiLogicId](opposingBattler, - battler, - aiThink->moveConsidered, - aiThink->score[aiThink->movesetIndex]); - DebugPrintf("Current score: %d", aiThink->score[aiThink->movesetIndex]); - } - } - else - { - aiThink->score[aiThink->movesetIndex] = 0; - } - aiThink->movesetIndex++; - } while (aiThink->movesetIndex < MAX_MON_MOVES); - - aiThink->movesetIndex = 0; -} - -static inline void BattleAI_DoAIProcessing(struct AI_ThinkingStruct *aiThink, u32 battlerAi, u32 battlerDef) -{ - do - { - if (gBattleMons[battlerAi].pp[aiThink->movesetIndex] == 0) - aiThink->moveConsidered = MOVE_NONE; - else - aiThink->moveConsidered = gBattleMons[battlerAi].moves[aiThink->movesetIndex]; - - // There is no point in calculating scores for all 3 battlers(2 opponents + 1 ally) with certain moves. - if (aiThink->moveConsidered != MOVE_NONE - && aiThink->score[aiThink->movesetIndex] > 0 - && ShouldConsiderMoveForBattler(battlerAi, battlerDef, aiThink->moveConsidered)) - { - if (aiThink->aiLogicId < ARRAY_COUNT(sBattleAiFuncTable) - && sBattleAiFuncTable[aiThink->aiLogicId] != NULL) - { - // Call AI function - aiThink->score[aiThink->movesetIndex] = - sBattleAiFuncTable[aiThink->aiLogicId](battlerAi, + sBattleAiFuncTable[aiThink->aiLogicId](battler, battlerDef, aiThink->moveConsidered, aiThink->score[aiThink->movesetIndex]); diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index d1a489580c..56e6482fd1 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -108,13 +108,6 @@ u32 GetSwitchChance(enum ShouldSwitchScenario shouldSwitchScenario) } } -u32 GetThinkingBattler(u32 battler) -{ - if (AI_DATA->aiSwitchPredictionInProgress) - return AI_DATA->battlerDoingPrediction; - return battler; -} - static bool32 IsAceMon(u32 battler, u32 monPartyId) { if (AI_THINKING_STRUCT->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_ACE_POKEMON @@ -295,7 +288,7 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler) && gBattleMons[battler].hp >= gBattleMons[battler].maxHP / 4))) { // 50% chance to stay in regardless - if (RandomPercentage(RNG_AI_SWITCH_HASBADODDS, (100 - GetSwitchChance(SHOULD_SWITCH_HASBADODDS))) && !AI_DATA->aiSwitchPredictionInProgress) + if (RandomPercentage(RNG_AI_SWITCH_HASBADODDS, (100 - GetSwitchChance(SHOULD_SWITCH_HASBADODDS))) && !AI_DATA->aiPredictionInProgress) return FALSE; // Switch mon out @@ -315,7 +308,7 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler) return FALSE; // 50% chance to stay in regardless - if (RandomPercentage(RNG_AI_SWITCH_HASBADODDS, (100 - GetSwitchChance(SHOULD_SWITCH_HASBADODDS))) && !AI_DATA->aiSwitchPredictionInProgress) + if (RandomPercentage(RNG_AI_SWITCH_HASBADODDS, (100 - GetSwitchChance(SHOULD_SWITCH_HASBADODDS))) && !AI_DATA->aiPredictionInProgress) return FALSE; // Switch mon out @@ -465,7 +458,7 @@ static bool32 FindMonThatAbsorbsOpponentsMove(u32 battler) return FALSE; if (gBattleStruct->prevTurnSpecies[battler] != gBattleMons[battler].species && !(AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_PREDICT_MOVES)) // AI mon has changed, player's behaviour no longer reliable; note to override this if using AI_FLAG_PREDICT_MOVE return FALSE; - if (HasSuperEffectiveMoveAgainstOpponents(battler, TRUE) && (RandomPercentage(RNG_AI_SWITCH_ABSORBING_STAY_IN, STAY_IN_ABSORBING_PERCENTAGE) || AI_DATA->aiSwitchPredictionInProgress)) + if (HasSuperEffectiveMoveAgainstOpponents(battler, TRUE) && (RandomPercentage(RNG_AI_SWITCH_ABSORBING_STAY_IN, STAY_IN_ABSORBING_PERCENTAGE) || AI_DATA->aiPredictionInProgress)) return FALSE; if (AreStatsRaised(battler)) return FALSE; @@ -898,7 +891,7 @@ static bool32 FindMonWithFlagsAndSuperEffective(u32 battler, u16 flags, u32 perc if (move == 0) continue; - if (AI_GetMoveEffectiveness(move, battler, battlerIn1) >= UQ_4_12(2.0) && (RandomPercentage(RNG_AI_SWITCH_SE_DEFENSIVE, percentChance) || AI_DATA->aiSwitchPredictionInProgress)) + if (AI_GetMoveEffectiveness(move, battler, battlerIn1) >= UQ_4_12(2.0) && (RandomPercentage(RNG_AI_SWITCH_SE_DEFENSIVE, percentChance) || AI_DATA->aiPredictionInProgress)) return SetSwitchinAndSwitch(battler, i); } } @@ -990,7 +983,7 @@ static bool32 ShouldSwitchIfEncored(u32 battler) return FALSE; // Switch out 50% of the time otherwise - else if ((RandomPercentage(RNG_AI_SWITCH_ENCORE, GetSwitchChance(SHOULD_SWITCH_ENCORE_DAMAGE)) || AI_DATA->aiSwitchPredictionInProgress) && AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE) + else if ((RandomPercentage(RNG_AI_SWITCH_ENCORE, GetSwitchChance(SHOULD_SWITCH_ENCORE_DAMAGE)) || AI_DATA->aiPredictionInProgress) && AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE) return SetSwitchinAndSwitch(battler, PARTY_SIZE); return FALSE; @@ -1036,7 +1029,7 @@ static bool32 ShouldSwitchIfAttackingStatsLowered(u32 battler) // 50% chance if attack at -2 and have a good candidate mon else if (attackingStage == DEFAULT_STAT_STAGE - 2) { - if (AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE && (RandomPercentage(RNG_AI_SWITCH_STATS_LOWERED, GetSwitchChance(SHOULD_SWITCH_ATTACKING_STAT_MINUS_TWO)) || AI_DATA->aiSwitchPredictionInProgress)) + if (AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE && (RandomPercentage(RNG_AI_SWITCH_STATS_LOWERED, GetSwitchChance(SHOULD_SWITCH_ATTACKING_STAT_MINUS_TWO)) || AI_DATA->aiPredictionInProgress)) return SetSwitchinAndSwitch(battler, PARTY_SIZE); } // If at -3 or worse, switch out regardless @@ -1053,7 +1046,7 @@ static bool32 ShouldSwitchIfAttackingStatsLowered(u32 battler) // 50% chance if attack at -2 and have a good candidate mon else if (spAttackingStage == DEFAULT_STAT_STAGE - 2) { - if (AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE && (RandomPercentage(RNG_AI_SWITCH_STATS_LOWERED, GetSwitchChance(SHOULD_SWITCH_ATTACKING_STAT_MINUS_TWO)) || AI_DATA->aiSwitchPredictionInProgress)) + if (AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE && (RandomPercentage(RNG_AI_SWITCH_STATS_LOWERED, GetSwitchChance(SHOULD_SWITCH_ATTACKING_STAT_MINUS_TWO)) || AI_DATA->aiPredictionInProgress)) return SetSwitchinAndSwitch(battler, PARTY_SIZE); } // If at -3 or worse, switch out regardless diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 74f0646ed3..55223e2353 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -4517,3 +4517,10 @@ bool32 HasBattlerSideAbility(u32 battler, u32 ability, struct AiLogicData *aiDat return TRUE; return FALSE; } + +u32 GetThinkingBattler(u32 battler) +{ + if (AI_DATA->aiPredictionInProgress) + return AI_DATA->battlerDoingPrediction; + return battler; +}