From 6031bb2a78acf0ef99173fd772bb2d497604ac94 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Tue, 18 Mar 2025 17:00:11 +0100 Subject: [PATCH] Split ai function IsBattlerTrapped (#6438) --- include/battle_ai_util.h | 3 ++- src/battle_ai_main.c | 28 ++++++++++++++++++++-------- src/battle_ai_util.c | 39 ++++++++++++++++++++++++++------------- test/battle/ai/ai.c | 12 ++++++++++++ 4 files changed, 60 insertions(+), 22 deletions(-) diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index 6d1f5a9ed4..678688dbc3 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -52,7 +52,8 @@ u32 GetTotalBaseStat(u32 species); bool32 IsTruantMonVulnerable(u32 battlerAI, u32 opposingBattler); bool32 AI_BattlerAtMaxHp(u32 battler); u32 GetHealthPercentage(u32 battler); -bool32 IsBattlerTrapped(u32 battler, bool32 switching); +bool32 AI_CanBattlerEscape(u32 battler); +bool32 IsBattlerTrapped(u32 battlerAtk, u32 battlerDef); s32 AI_WhoStrikesFirst(u32 battlerAI, u32 battler2, u32 moveConsidered); bool32 CanTargetFaintAi(u32 battlerDef, u32 battlerAtk); u32 NoOfHitsForTargetToFaintAI(u32 battlerDef, u32 battlerAtk); diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 133d62aeb7..a764cf14c2 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -1600,7 +1600,9 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) ADJUST_SCORE(-10); // if mon will wake up, is not asleep, or is not comatose break; case EFFECT_MEAN_LOOK: - if (IsBattlerTrapped(battlerDef, TRUE) || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) + if (AI_CanBattlerEscape(battlerDef) + || IsBattlerTrapped(battlerAtk, battlerDef) + || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) ADJUST_SCORE(-10); break; case EFFECT_NIGHTMARE: @@ -2713,7 +2715,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) else if (GetBattleMoveCategory(move) == DAMAGE_CATEGORY_STATUS && (CountUsablePartyMons(battlerAtk) < 1 || AI_DATA->mostSuitableMonId[battlerAtk] == PARTY_SIZE - || IsBattlerTrapped(battlerAtk, TRUE))) + || (!AI_CanBattlerEscape(battlerAtk) && IsBattlerTrapped(battlerDef, battlerAtk)))) ADJUST_SCORE(-30); } @@ -3666,7 +3668,8 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) || aiData->abilities[battlerDef] == ABILITY_MAGIC_GUARD) break; ADJUST_SCORE(GOOD_EFFECT); - if (!HasDamagingMove(battlerDef) || IsBattlerTrapped(battlerDef, FALSE) + if (!HasDamagingMove(battlerDef) + || (!AI_CanBattlerEscape(battlerDef) && IsBattlerTrapped(battlerAtk, battlerDef)) || aiData->holdEffects[battlerAtk] == HOLD_EFFECT_BIG_ROOT) ADJUST_SCORE(DECENT_EFFECT); break; @@ -3761,7 +3764,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) case EFFECT_CURSE: if (IS_BATTLER_OF_TYPE(battlerAtk, TYPE_GHOST)) { - if (IsBattlerTrapped(battlerDef, TRUE)) + if (!AI_CanBattlerEscape(battlerDef) && IsBattlerTrapped(battlerAtk, battlerDef)) ADJUST_SCORE(GOOD_EFFECT); else ADJUST_SCORE(WEAK_EFFECT); @@ -3854,7 +3857,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) ADJUST_SCORE(DECENT_EFFECT); break; case EFFECT_PERISH_SONG: - if (IsBattlerTrapped(battlerDef, TRUE)) + if (!AI_CanBattlerEscape(battlerDef) && IsBattlerTrapped(battlerAtk, battlerDef)) ADJUST_SCORE(GOOD_EFFECT); break; case EFFECT_SANDSTORM: @@ -3995,7 +3998,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) break; // Don't use if the attract won't have a change to activate if (gBattleMons[battlerDef].status1 & STATUS1_ANY || (gBattleMons[battlerDef].status2 & STATUS2_CONFUSION) - || IsBattlerTrapped(battlerDef, TRUE)) + || (!AI_CanBattlerEscape(battlerDef) && IsBattlerTrapped(battlerAtk, battlerDef))) ADJUST_SCORE(GOOD_EFFECT); else ADJUST_SCORE(DECENT_EFFECT); @@ -5091,7 +5094,7 @@ static s32 AI_PreferBatonPass(u32 battlerAtk, u32 battlerDef, u32 move, s32 scor || CountUsablePartyMons(battlerAtk) == 0 || !IsBattleMoveStatus(move) || !HasMoveEffect(battlerAtk, EFFECT_BATON_PASS) - || IsBattlerTrapped(battlerAtk, TRUE)) + || (!AI_CanBattlerEscape(battlerAtk) && IsBattlerTrapped(battlerDef, battlerAtk))) return score; u32 effect = GetMoveEffect(move); @@ -5582,7 +5585,16 @@ static void AI_Watch(void) // Roaming pokemon logic static s32 AI_Roaming(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) { - if (IsBattlerTrapped(battlerAtk, FALSE)) + bool32 roamerCanFlee = FALSE; + + if (AI_CanBattlerEscape(battlerAtk)) + roamerCanFlee = TRUE; + else if (AI_DATA->abilities[battlerAtk] == ABILITY_RUN_AWAY) + roamerCanFlee = TRUE; + else if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_CAN_ALWAYS_RUN) + roamerCanFlee = TRUE; + + if (!roamerCanFlee && IsBattlerTrapped(battlerDef, battlerAtk)) return score; AI_Flee(); diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 6d7c36bd39..1c2d0789cd 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -339,25 +339,35 @@ bool32 AI_BattlerAtMaxHp(u32 battlerId) return FALSE; } -bool32 IsBattlerTrapped(u32 battler, bool32 checkSwitch) + +bool32 AI_CanBattlerEscape(u32 battler) { u32 holdEffect = AI_DATA->holdEffects[battler]; if (B_GHOSTS_ESCAPE >= GEN_6 && IS_BATTLER_OF_TYPE(battler, TYPE_GHOST)) - return FALSE; - if (checkSwitch && holdEffect == HOLD_EFFECT_SHED_SHELL) - return FALSE; - else if (!checkSwitch && AI_DATA->abilities[battler] == ABILITY_RUN_AWAY) - return FALSE; - else if (!checkSwitch && holdEffect == HOLD_EFFECT_CAN_ALWAYS_RUN) - return FALSE; - else if (gBattleMons[battler].status2 & (STATUS2_ESCAPE_PREVENTION | STATUS2_WRAPPED)) return TRUE; - else if (gStatuses3[battler] & (STATUS3_ROOTED | STATUS3_SKY_DROPPED)) + if (holdEffect == HOLD_EFFECT_SHED_SHELL) return TRUE; - else if (gFieldStatuses & STATUS_FIELD_FAIRY_LOCK) + + return FALSE; +} + +bool32 IsBattlerTrapped(u32 battlerAtk, u32 battlerDef) +{ + if (gBattleMons[battlerDef].status2 & (STATUS2_ESCAPE_PREVENTION | STATUS2_WRAPPED)) return TRUE; - else if (IsAbilityPreventingEscape(battler)) + if (gStatuses3[battlerDef] & (STATUS3_ROOTED | STATUS3_SKY_DROPPED)) + return TRUE; + if (gFieldStatuses & STATUS_FIELD_FAIRY_LOCK) + return TRUE; + if (AI_IsAbilityOnSide(battlerAtk, ABILITY_SHADOW_TAG) + && (B_SHADOW_TAG_ESCAPE >= GEN_4 && AI_DATA->abilities[battlerDef] != ABILITY_SHADOW_TAG)) + return TRUE; + if (AI_IsAbilityOnSide(battlerAtk, ABILITY_ARENA_TRAP) + && IsBattlerGrounded(battlerAtk)) + return TRUE; + if (AI_IsAbilityOnSide(battlerAtk, ABILITY_MAGNET_PULL) + && IS_BATTLER_OF_TYPE(battlerAtk, TYPE_STEEL)) return TRUE; return FALSE; @@ -3256,7 +3266,10 @@ u32 ShouldTryToFlinch(u32 battlerAtk, u32 battlerDef, u32 atkAbility, u32 defAbi bool32 ShouldTrap(u32 battlerAtk, u32 battlerDef, u32 move) { - if (IsBattlerTrapped(battlerDef, TRUE)) + if (AI_CanBattlerEscape(battlerDef)) + return FALSE; + + if (IsBattlerTrapped(battlerAtk, battlerDef)) return FALSE; if (BattlerWillFaintFromSecondaryDamage(battlerDef, AI_DATA->abilities[battlerDef])) diff --git a/test/battle/ai/ai.c b/test/battle/ai/ai.c index da77f828e4..3a72508c17 100644 --- a/test/battle/ai/ai.c +++ b/test/battle/ai/ai.c @@ -845,3 +845,15 @@ AI_SINGLE_BATTLE_TEST("AI won't use Sucker Punch if it expects a move of the sam TURN { MOVE(player, MOVE_QUICK_ATTACK); EXPECT_MOVE(opponent, MOVE_TACKLE); } } } + +AI_SINGLE_BATTLE_TEST("AI score for Mean Look will be decreased if target can escape") +{ + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); + PLAYER(SPECIES_BULBASAUR) { Item(ITEM_SHED_SHELL); } + OPPONENT(SPECIES_BULBASAUR) { Moves(MOVE_TACKLE, MOVE_MEAN_LOOK); } + } WHEN { + TURN { SCORE_EQ_VAL(opponent, MOVE_MEAN_LOOK, 90); } + } +} +