From a901d227ccc90b73c8e000cc5ecf8926fa71d5c8 Mon Sep 17 00:00:00 2001 From: ghostyboyy97 <106448956+ghostyboyy97@users.noreply.github.com> Date: Fri, 19 Dec 2025 12:40:46 -0500 Subject: [PATCH] fix (scoring): AI_IsMoveEffectInPlus - AI should not see secondary effect of Sheer Force boosted moves as beneficial (#8579) Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com> --- include/battle_util.h | 2 +- src/battle_ai_main.c | 9 +++------ src/battle_ai_util.c | 3 +++ src/battle_script_commands.c | 6 +++--- src/battle_util.c | 4 ++-- test/battle/ai/ai_calc_best_move_score.c | 13 +++++++++++++ 6 files changed, 25 insertions(+), 12 deletions(-) diff --git a/include/battle_util.h b/include/battle_util.h index 8ea72866e5..9ebc837952 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -343,7 +343,7 @@ bool32 IsPartnerMonFromSameTrainer(u32 battler); enum DamageCategory GetCategoryBasedOnStats(u32 battler); void SetShellSideArmCategory(void); bool32 MoveIsAffectedBySheerForce(u32 move); -bool32 TestIfSheerForceAffected(u32 battler, u16 move); +bool32 IsSheerForceAffected(u16 move, enum Ability ability); void TryRestoreHeldItems(void); bool32 CanStealItem(u32 battlerStealing, u32 battlerItem, u16 item); void TrySaveExchangedItem(u32 battler, u16 stolenItem); diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 795efa9825..8dde9c4a97 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -5697,17 +5697,14 @@ static s32 AI_CalcAdditionalEffectScore(u32 battlerAtk, u32 battlerDef, u32 move u32 i; u32 additionalEffectCount = GetMoveAdditionalEffectCount(move); + if (IsSheerForceAffected(move, aiData->abilities[battlerAtk])) + return score; + // 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; diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index a9c4b61284..20ebfdb27f 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -1030,6 +1030,9 @@ static bool32 AI_IsMoveEffectInPlus(u32 battlerAtk, u32 battlerDef, u32 move, s3 enum Ability abilityDef = gAiLogicData->abilities[battlerDef]; enum Ability abilityAtk = gAiLogicData->abilities[battlerAtk]; + if (IsSheerForceAffected(move, abilityAtk)) + return FALSE; + switch (GetMoveEffect(move)) { case EFFECT_HIT_ESCAPE: diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 516e817be5..5b7ddd9da9 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -3049,7 +3049,7 @@ void SetMoveEffect(u32 battler, u32 effectBattler, enum MoveEffect moveEffect, c if (!primary && !affectsUser && IsMoveEffectBlockedByTarget(battlerAbility)) moveEffect = MOVE_EFFECT_NONE; else if (!primary - && TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove) + && IsSheerForceAffected(gCurrentMove, GetBattlerAbility(battler)) && !(GetMoveEffect(gCurrentMove) == EFFECT_ORDER_UP && gBattleStruct->battlerState[gBattlerAttacker].commanderSpecies != SPECIES_NONE)) moveEffect = MOVE_EFFECT_NONE; else if (!IsBattlerAlive(gEffectBattler) && !activateAfterFaint) @@ -6400,7 +6400,7 @@ static void Cmd_moveend(void) && gBattlerTarget != gBattlerAttacker && !IsBattlerAlly(gBattlerTarget, gBattlerAttacker) && gProtectStructs[gBattlerTarget].physicalBattlerId == gBattlerAttacker - && !TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove)) + && !IsSheerForceAffected(gCurrentMove, GetBattlerAbility(gBattlerAttacker))) { gProtectStructs[gBattlerTarget].shellTrap = TRUE; // Change move order in double battles, so the hit mon with shell trap moves immediately after being hit. @@ -6660,7 +6660,7 @@ static void Cmd_moveend(void) gBattleScripting.moveendState++; break; case MOVEEND_SHEER_FORCE: - if (TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove)) + if (IsSheerForceAffected(gCurrentMove, GetBattlerAbility(gBattlerAttacker))) gBattleScripting.moveendState = MOVEEND_EJECT_PACK; else gBattleScripting.moveendState++; diff --git a/src/battle_util.c b/src/battle_util.c index 3205529cb3..11a5ef583c 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -9892,9 +9892,9 @@ bool32 IsBattlerAffectedByHazards(u32 battler, bool32 toxicSpikes) return ret; } -bool32 TestIfSheerForceAffected(u32 battler, u16 move) +bool32 IsSheerForceAffected(u16 move, enum Ability ability) { - return GetBattlerAbility(battler) == ABILITY_SHEER_FORCE && MoveIsAffectedBySheerForce(move); + return ability == ABILITY_SHEER_FORCE && MoveIsAffectedBySheerForce(move); } // This function is the body of "jumpifstat", but can be used dynamically in a function diff --git a/test/battle/ai/ai_calc_best_move_score.c b/test/battle/ai/ai_calc_best_move_score.c index 88525249dc..7fcf0ced24 100644 --- a/test/battle/ai/ai_calc_best_move_score.c +++ b/test/battle/ai/ai_calc_best_move_score.c @@ -133,3 +133,16 @@ AI_SINGLE_BATTLE_TEST("HasMoveThatChangesKOThreshold - AI should not see self-ta TURN { MOVE(player, MOVE_HAMMER_ARM); EXPECT_MOVE(opponent, move == MOVE_EARTHQUAKE ? MOVE_NASTY_PLOT : MOVE_AURA_SPHERE); } } } + +AI_SINGLE_BATTLE_TEST("AI_IsMoveEffectInPlus - AI should not see secondary effect of Sheer Force boosted moves as beneficial") +{ + GIVEN { + ASSUME(GetMovePower(MOVE_PSYCHIC) == 90); + ASSUME(MoveHasAdditionalEffect(MOVE_PSYCHIC, MOVE_EFFECT_SP_DEF_MINUS_1) == TRUE); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_OMNISCIENT); + PLAYER(SPECIES_STEELIX) { Level(100); Nature(NATURE_SASSY); Item(ITEM_STEELIXITE); Ability(ABILITY_STURDY); Speed(58); Moves(MOVE_GYRO_BALL); } + OPPONENT(SPECIES_BRAVIARY_HISUI) { Level(100); Nature(NATURE_TIMID); Ability(ABILITY_SHEER_FORCE); Speed(251); Moves(MOVE_PSYCHIC, MOVE_NIGHT_SHADE); } + } WHEN { + TURN { MOVE(player, MOVE_GYRO_BALL); SCORE_EQ_VAL(opponent, MOVE_PSYCHIC, 101); SCORE_EQ_VAL(opponent, MOVE_NIGHT_SHADE, 101); } + } +}