diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index bd1ebe3a36..fb0e40bfd3 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -3990,9 +3990,8 @@ static void AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef) } } -static u32 AI_CalcHoldEffectMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) +static s32 AI_CalcHoldEffectMoveScore(u32 battlerAtk, u32 battlerDef, u32 move, struct AiLogicData *aiData) { - struct AiLogicData *aiData = gAiLogicData; enum ItemHoldEffect holdEffect = aiData->holdEffects[battlerAtk]; s32 score = 0; @@ -4020,17 +4019,16 @@ static u32 AI_CalcHoldEffectMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) return score; } -static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) +static s32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move, struct AiLogicData *aiData) { // move data enum BattleMoveEffects moveEffect = GetMoveEffect(move); - struct AiLogicData *aiData = gAiLogicData; u32 movesetIndex = gAiThinkingStruct->movesetIndex; uq4_12_t effectiveness = aiData->effectiveness[battlerAtk][battlerDef][movesetIndex]; s32 score = 0; - u32 predictedMove = GetIncomingMove(battlerAtk, battlerDef, gAiLogicData); - u32 predictedMoveSpeedCheck = GetIncomingMoveSpeedCheck(battlerAtk, battlerDef, gAiLogicData); + u32 predictedMove = GetIncomingMove(battlerAtk, battlerDef, aiData); + u32 predictedMoveSpeedCheck = GetIncomingMoveSpeedCheck(battlerAtk, battlerDef, aiData); u32 predictedType = GetMoveType(predictedMove); u32 predictedMoveSlot = GetMoveSlot(GetMovesArray(battlerDef), predictedMove); bool32 isBattle1v1 = IsBattle1v1(); @@ -4052,7 +4050,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) ADJUST_SCORE(10); // check burn / frostbite - if (gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_SMART_SWITCHING && gAiLogicData->abilities[battlerAtk] == ABILITY_NATURAL_CURE) + if (gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_SMART_SWITCHING && aiData->abilities[battlerAtk] == ABILITY_NATURAL_CURE) { if ((gBattleMons[battlerAtk].status1 & STATUS1_BURN && HasOnlyMovesWithCategory(battlerAtk, DAMAGE_CATEGORY_PHYSICAL, TRUE)) || (gBattleMons[battlerAtk].status1 & STATUS1_FROSTBITE && HasOnlyMovesWithCategory(battlerAtk, DAMAGE_CATEGORY_SPECIAL, TRUE))) @@ -4478,7 +4476,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) } break; case EFFECT_BATON_PASS: - if ((gAiLogicData->shouldSwitch & (1u << battlerAtk)) && (gBattleMons[battlerAtk].volatiles.substitute + if ((aiData->shouldSwitch & (1u << battlerAtk)) && (gBattleMons[battlerAtk].volatiles.substitute || gBattleMons[battlerAtk].volatiles.powerTrick || gBattleMons[battlerAtk].volatiles.magnetRise || gBattleMons[battlerAtk].volatiles.aquaRing @@ -5482,9 +5480,9 @@ case EFFECT_GUARD_SPLIT: if (GetFirstFaintedPartyIndex(battlerAtk) != PARTY_SIZE) { ADJUST_SCORE(DECENT_EFFECT); - if (gAiLogicData->shouldSwitch & (1u << battlerAtk)) // Bad matchup + if (aiData->shouldSwitch & (1u << battlerAtk)) // Bad matchup ADJUST_SCORE(WEAK_EFFECT); - if (gAiLogicData->mostSuitableMonId[battlerAtk] != PARTY_SIZE) // Good mon to send in after + if (aiData->mostSuitableMonId[battlerAtk] != PARTY_SIZE) // Good mon to send in after ADJUST_SCORE(WEAK_EFFECT); } break; @@ -5590,11 +5588,30 @@ case EFFECT_GUARD_SPLIT: break; } // move effect checks + return score; +} + +static s32 AI_CalcAdditionalEffectScore(u32 battlerAtk, u32 battlerDef, u32 move, struct AiLogicData *aiData) +{ + // move data + s32 score = 0; + + u32 predictedMove = GetIncomingMove(battlerAtk, battlerDef, aiData); + bool32 hasPartner = HasPartner(battlerAtk); + u32 i; u32 additionalEffectCount = GetMoveAdditionalEffectCount(move); + // check move additional effects that are likely to happen for (i = 0; i < additionalEffectCount; i++) { const struct AdditionalEffect *additionalEffect = GetMoveAdditionalEffectById(move, i); + + if (aiData->abilities[battlerAtk] == ABILITY_SHEER_FORCE) + { + if ((additionalEffect->chance > 0) != additionalEffect->sheerForceOverride) + continue; + } + // Only consider effects with a guaranteed chance to happen if (!MoveEffectIsGuaranteed(battlerAtk, aiData->abilities[battlerAtk], additionalEffect)) continue; @@ -5753,8 +5770,12 @@ case EFFECT_GUARD_SPLIT: IncreasePoisonScore(battlerAtk, battlerDef, move, &score); break; case MOVE_EFFECT_CLEAR_SMOG: + { + bool32 moveTargetsBothOpponents = HasTwoOpponents(battlerAtk) && (gMovesInfo[move].target & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY | MOVE_TARGET_ALL_BATTLERS)); + score += AI_TryToClearStats(battlerAtk, battlerDef, moveTargetsBothOpponents); break; + } case MOVE_EFFECT_BUG_BITE: // And pluck if (gBattleMons[battlerDef].volatiles.substitute || aiData->abilities[battlerDef] == ABILITY_STICKY_HOLD) break; @@ -5783,6 +5804,8 @@ case EFFECT_GUARD_SPLIT: case MOVE_EFFECT_THROAT_CHOP: if (IsSoundMove(GetBestDmgMoveFromBattler(battlerDef, battlerAtk, AI_DEFENDING))) { + u32 predictedMoveSpeedCheck = GetIncomingMoveSpeedCheck(battlerAtk, battlerDef, aiData); + if (AI_IsFaster(battlerAtk, battlerDef, move, predictedMoveSpeedCheck, CONSIDER_PRIORITY)) ADJUST_SCORE(GOOD_EFFECT); else @@ -5874,13 +5897,14 @@ case EFFECT_GUARD_SPLIT: } } } - return score; } // AI_FLAG_CHECK_VIABILITY - Chooses best possible move to hit player static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) { + struct AiLogicData *aiData = gAiLogicData; + // Targeting partner, check benefits of doing that instead if (IsTargetingPartner(battlerAtk, battlerDef)) return score; @@ -5897,8 +5921,9 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score } } - ADJUST_SCORE(AI_CalcMoveEffectScore(battlerAtk, battlerDef, move)); - ADJUST_SCORE(AI_CalcHoldEffectMoveScore(battlerAtk, battlerDef, move)); + ADJUST_SCORE(AI_CalcMoveEffectScore(battlerAtk, battlerDef, move, aiData)); + ADJUST_SCORE(AI_CalcAdditionalEffectScore(battlerAtk, battlerDef, move, aiData)); + ADJUST_SCORE(AI_CalcHoldEffectMoveScore(battlerAtk, battlerDef, move, aiData)); return score; } diff --git a/test/battle/ability/sheer_force.c b/test/battle/ability/sheer_force.c index d14d53571d..c6e84694ba 100644 --- a/test/battle/ability/sheer_force.c +++ b/test/battle/ability/sheer_force.c @@ -1358,3 +1358,19 @@ DOUBLE_BATTLE_TEST("Sheer Force only boosts the damage of moves it's supposed to EXPECT_EQ(damage2, damage1); } } + +AI_SINGLE_BATTLE_TEST("AI sees Sheer Force skips additional effects") +{ + u16 ability, expectedMove; + + PARAMETRIZE { ability = ABILITY_NONE; expectedMove = MOVE_POWER_UP_PUNCH; } + PARAMETRIZE { ability = ABILITY_SHEER_FORCE; expectedMove = MOVE_KARATE_CHOP; } + + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Ability(ability); Moves(MOVE_POWER_UP_PUNCH, MOVE_KARATE_CHOP); } + } WHEN { + TURN { EXPECT_MOVE(opponent, expectedMove); } + } +}