From 0ea1ebe8de03b5e3495050fe9193e0c37d1333bc Mon Sep 17 00:00:00 2001 From: Pawkkie <61265402+Pawkkie@users.noreply.github.com> Date: Thu, 15 May 2025 13:53:51 -0400 Subject: [PATCH] Fix AI_FLAG_PREDICT_MOVES scoring bug (#6867) --- src/battle_ai_main.c | 18 +++++++++--------- src/battle_ai_util.c | 2 +- test/battle/ai/ai_flag_predict_move.c | 13 +++++++++++++ 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 0a43bc81ed..cbf4e9f6c2 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -358,7 +358,6 @@ void SetupAIPredictionData(u32 battler, enum SwitchType switchType) if (gAiThinkingStruct->aiFlags[battler] & AI_FLAG_PREDICT_MOVE) { gAiLogicData->predictedMove[opposingBattler] = gBattleMons[opposingBattler].moves[BattleAI_ChooseMoveIndex(opposingBattler)]; - DebugPrintf("Predicted move: %d", gAiLogicData->predictedMove[opposingBattler]); ModifySwitchAfterMoveScoring(opposingBattler); // Determine whether AI will use predictions this turn @@ -678,16 +677,17 @@ static u32 ChooseMoveOrAction_Singles(u32 battler) u32 numOfBestMoves; s32 i; u64 flags = gAiThinkingStruct->aiFlags[GetThinkingBattler(battler)]; + u32 opposingBattler = GetOppositeBattler(battler); gAiLogicData->partnerMove = 0; // no ally while (flags != 0) { if (flags & 1) { - if (IsBattlerPredictedToSwitch(gBattlerTarget) && (gAiThinkingStruct->aiFlags[battler] & AI_FLAG_PREDICT_INCOMING_MON)) - BattleAI_DoAIProcessing_PredictedSwitchin(gAiThinkingStruct, gAiLogicData, battler, gBattlerTarget); + if (IsBattlerPredictedToSwitch(opposingBattler) && (gAiThinkingStruct->aiFlags[battler] & AI_FLAG_PREDICT_INCOMING_MON)) + BattleAI_DoAIProcessing_PredictedSwitchin(gAiThinkingStruct, gAiLogicData, battler, opposingBattler); else - BattleAI_DoAIProcessing(gAiThinkingStruct, battler, gBattlerTarget); + BattleAI_DoAIProcessing(gAiThinkingStruct, battler, opposingBattler); } flags >>= (u64)1; gAiThinkingStruct->aiLogicId++; @@ -695,7 +695,7 @@ static u32 ChooseMoveOrAction_Singles(u32 battler) for (i = 0; i < MAX_MON_MOVES; i++) { - gAiBattleData->finalScore[battler][gBattlerTarget][i] = gAiThinkingStruct->score[i]; + gAiBattleData->finalScore[battler][opposingBattler][i] = gAiThinkingStruct->score[i]; } numOfBestMoves = 1; @@ -1003,7 +1003,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) bool32 isDoubleBattle = IsValidDoubleBattle(battlerAtk); u32 i; u32 weather; - u32 predictedMove = ((gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_PREDICT_MOVE) && aiData->predictingMove) ? gAiLogicData->predictedMove[battlerDef] : aiData->lastUsedMove[battlerDef]; + u32 predictedMove = GetIncomingMove(battlerAtk, battlerDef, gAiLogicData); u32 abilityAtk = aiData->abilities[battlerAtk]; u32 abilityDef = aiData->abilities[battlerDef]; s32 atkPriority = GetBattleMovePriority(battlerAtk, abilityAtk, move); @@ -2932,7 +2932,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) bool32 partnerProtecting = (partnerEffect == EFFECT_PROTECT); bool32 attackerHasBadAbility = (gAbilitiesInfo[aiData->abilities[battlerAtk]].aiRating < 0); bool32 partnerHasBadAbility = (gAbilitiesInfo[atkPartnerAbility].aiRating < 0); - u32 predictedMove = ((gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_PREDICT_MOVE) && aiData->predictingMove) ? gAiLogicData->predictedMove[battlerDef] : aiData->lastUsedMove[battlerDef]; + u32 predictedMove = GetIncomingMove(battlerAtk, battlerDef, gAiLogicData); SetTypeBeforeUsingMove(move, battlerAtk); moveType = GetBattleMoveType(move); @@ -3503,7 +3503,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) uq4_12_t effectiveness = aiData->effectiveness[battlerAtk][battlerDef][movesetIndex]; s32 score = 0; - u32 predictedMove = ((gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_PREDICT_MOVE) && aiData->predictingMove) ? gAiLogicData->predictedMove[battlerDef] : aiData->lastUsedMove[battlerDef]; + u32 predictedMove = GetIncomingMove(battlerAtk, battlerDef, gAiLogicData); u32 predictedType = GetMoveType(predictedMove); u32 predictedMoveSlot = GetMoveSlot(GetMovesArray(battlerDef), predictedMove); bool32 isDoubleBattle = IsValidDoubleBattle(battlerAtk); @@ -5611,7 +5611,7 @@ static s32 AI_PredictSwitch(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) enum BattleMoveEffects moveEffect = GetMoveEffect(move); struct AiLogicData *aiData = gAiLogicData; uq4_12_t effectiveness = aiData->effectiveness[battlerAtk][battlerDef][gAiThinkingStruct->movesetIndex]; - u32 predictedMove = ((gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_PREDICT_MOVE) && aiData->predictingMove) ? aiData->predictedMove[battlerDef] : aiData->lastUsedMove[battlerDef]; + u32 predictedMove = GetIncomingMove(battlerAtk, battlerDef, gAiLogicData); // Switch benefit switch (moveEffect) diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 0b532b865c..c080ee5b43 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -1142,7 +1142,7 @@ s32 AI_WhoStrikesFirst(u32 battlerAI, u32 battler, u32 moveConsidered) u32 abilityAI = gAiLogicData->abilities[battlerAI]; u32 abilityPlayer = gAiLogicData->abilities[battler]; - u32 predictedMove = ((gAiThinkingStruct->aiFlags[battlerAI] & AI_FLAG_PREDICT_MOVE) && gAiLogicData->predictingMove) ? gAiLogicData->predictedMove[battler] : gAiLogicData->lastUsedMove[battler]; + u32 predictedMove = GetIncomingMove(battlerAI, battler, gAiLogicData); s8 aiPriority = GetBattleMovePriority(battlerAI, abilityAI, moveConsidered); s8 playerPriority = GetBattleMovePriority(battler, abilityPlayer, predictedMove); diff --git a/test/battle/ai/ai_flag_predict_move.c b/test/battle/ai/ai_flag_predict_move.c index 362783f731..97a7c12afa 100644 --- a/test/battle/ai/ai_flag_predict_move.c +++ b/test/battle/ai/ai_flag_predict_move.c @@ -14,3 +14,16 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_PREDICT_MOVE: AI will predict player's move") TURN { MOVE(player, MOVE_SURF); EXPECT_SWITCH(opponent, 1); } } } + +AI_SINGLE_BATTLE_TEST("AI_FLAG_PREDICT_MOVE: AI will still attack you when it should") +{ + PASSES_RANDOMLY(PREDICT_MOVE_CHANCE, 100, RNG_AI_PREDICT_MOVE); + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_PREDICT_MOVE); + PLAYER(SPECIES_VAPOREON) { Ability(ABILITY_WATER_ABSORB); Moves(MOVE_SURF, MOVE_TACKLE); } + OPPONENT(SPECIES_SCEPTILE) { Moves(MOVE_LEAF_BLADE); } + OPPONENT(SPECIES_RHYDON) { Ability(ABILITY_ROCK_HEAD); Moves(MOVE_EARTHQUAKE); } + } WHEN { + TURN { MOVE(player, MOVE_SURF); EXPECT_MOVE(opponent, MOVE_LEAF_BLADE); } + } +}