From c544fee140d5e9c4b3543df7d895e3e7e0d67462 Mon Sep 17 00:00:00 2001 From: Pawkkie <61265402+Pawkkie@users.noreply.github.com> Date: Tue, 22 Jul 2025 14:13:10 -0400 Subject: [PATCH] Improve AI's Sucker Punch handling (#7389) --- include/config/ai.h | 2 ++ include/random.h | 1 + src/battle_ai_main.c | 9 ++++----- test/battle/ai/ai.c | 13 +++++++++++++ 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/include/config/ai.h b/include/config/ai.h index 99283ee90a..f5d0496c20 100644 --- a/include/config/ai.h +++ b/include/config/ai.h @@ -58,6 +58,8 @@ #define BOOST_INTO_HAZE_CHANCE 0 // Chance the AI will use a stat boosting move if the player has used Haze #define SHOULD_RECOVER_CHANCE 50 // Chance the AI will give recovery moves score increase if less than ENABLE_RECOVERY_THRESHOLD and in no immediate danger #define ENABLE_RECOVERY_THRESHOLD 60 // HP percentage beneath which SHOULD_RECOVER_CHANCE is active +#define SUCKER_PUNCH_CHANCE 50 // Chance for the AI to not use Sucker Punch if the player has a status move +#define SUCKER_PUNCH_PREDICTION_CHANCE 50 // Additional chance for the AI to not use Sucker Punch if actively predicting a status move if SUCKER_PUNCH_CHANCE fails // AI damage calc considerations #define RISKY_AI_CRIT_STAGE_THRESHOLD 2 // Stat stages at which Risky will assume it gets a crit diff --git a/include/random.h b/include/random.h index f00ace1f8c..89ccb9779a 100644 --- a/include/random.h +++ b/include/random.h @@ -193,6 +193,7 @@ enum RandomTag RNG_AI_CONSERVE_TERA, RNG_AI_SWITCH_ALL_SCORES_BAD, RNG_AI_PP_STALL_DISREGARD_MOVE, + RNG_AI_SUCKER_PUNCH, RNG_SHELL_SIDE_ARM, RNG_RANDOM_TARGET, RNG_AI_PREDICT_ABILITY, diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index b6ddd0dc91..0f9bf432c9 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -2745,11 +2745,10 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) ADJUST_SCORE(-10); break; case EFFECT_SUCKER_PUNCH: - if (predictedMove != MOVE_NONE) - { - if (IsBattleMoveStatus(predictedMove) || AI_IsSlower(battlerAtk, battlerDef, move)) // Opponent going first - ADJUST_SCORE(-10); - } + if ((HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_STATUS) && RandomPercentage(RNG_AI_SUCKER_PUNCH, SUCKER_PUNCH_CHANCE)) // Player has a status move + || (IsBattleMoveStatus(predictedMove) && RandomPercentage(RNG_AI_SUCKER_PUNCH, SUCKER_PUNCH_PREDICTION_CHANCE) && (gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_PREDICT_MOVE)) // AI actively predicting incoming status move + || AI_IsSlower(battlerAtk, battlerDef, move)) // Opponent going first + ADJUST_SCORE(-10); break; case EFFECT_TAILWIND: if (gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_TAILWIND diff --git a/test/battle/ai/ai.c b/test/battle/ai/ai.c index 4eb99e481e..25999df746 100644 --- a/test/battle/ai/ai.c +++ b/test/battle/ai/ai.c @@ -675,6 +675,19 @@ AI_SINGLE_BATTLE_TEST("AI won't use Sucker Punch if it expects a move of the sam } } +AI_SINGLE_BATTLE_TEST("AI won't use Sucker Punch if it expects a status move a percentage of the time") +{ + PASSES_RANDOMLY(SUCKER_PUNCH_CHANCE, 100, RNG_AI_SUCKER_PUNCH); + GIVEN { + ASSUME(GetMoveEffect(MOVE_SUCKER_PUNCH) == EFFECT_SUCKER_PUNCH); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); + PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_GROWL, MOVE_SCRATCH); } + OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_SUCKER_PUNCH, MOVE_SCRATCH); } + } WHEN { + TURN { MOVE(player, MOVE_SCRATCH); EXPECT_MOVE(opponent, MOVE_SUCKER_PUNCH); } + } +} + AI_SINGLE_BATTLE_TEST("AI won't use thawing moves if target is frozen unless it is super effective or it has no other options") { u32 aiFlags = 0; u32 status = 0; u32 aiMove = 0;