diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index a4e818b570..b60e4c2c82 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -2079,7 +2079,8 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) ADJUST_SCORE(-10); break; case EFFECT_REFRESH: - if (!(gBattleMons[battlerAtk].status1 & STATUS1_CAN_MOVE)) + if (!(gBattleMons[battlerAtk].status1 & STATUS1_CAN_MOVE) + || !ShouldCureStatus(battlerAtk, battlerAtk, aiData)) ADJUST_SCORE(-10); break; case EFFECT_PSYCHO_SHIFT: @@ -2904,12 +2905,17 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) ADJUST_SCORE(-10); break; case EFFECT_JUNGLE_HEALING: - if (AI_BattlerAtMaxHp(battlerAtk) - && AI_BattlerAtMaxHp(BATTLE_PARTNER(battlerAtk)) - && !(gBattleMons[battlerAtk].status1 & STATUS1_ANY) - && !(gBattleMons[BATTLE_PARTNER(battlerAtk)].status1 & STATUS1_ANY)) + { + bool32 canCureSelf = (gBattleMons[battlerAtk].status1 & STATUS1_ANY) && ShouldCureStatus(battlerAtk, battlerAtk, aiData); + bool32 canCurePartner = (gBattleMons[BATTLE_PARTNER(battlerAtk)].status1 & STATUS1_ANY) && ShouldCureStatus(battlerAtk, BATTLE_PARTNER(battlerAtk), aiData); + + if (AI_BattlerAtMaxHp(battlerAtk) + && AI_BattlerAtMaxHp(BATTLE_PARTNER(battlerAtk)) + && !canCureSelf + && !canCurePartner) ADJUST_SCORE(-10); break; + } case EFFECT_TAKE_HEART: if ((!(gBattleMons[battlerAtk].status1 & STATUS1_ANY) || PartnerMoveEffectIs(BATTLE_PARTNER(battlerAtk), aiData->partnerMove, EFFECT_JUNGLE_HEALING) @@ -5100,7 +5106,8 @@ static s32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move, stru ADJUST_SCORE(WEAK_EFFECT); break; case EFFECT_REFRESH: - if (gBattleMons[battlerAtk].status1 & STATUS1_CAN_MOVE) + if ((gBattleMons[battlerAtk].status1 & STATUS1_CAN_MOVE) + && ShouldCureStatus(battlerAtk, battlerAtk, aiData)) ADJUST_SCORE(DECENT_EFFECT); break; case EFFECT_TAKE_HEART: @@ -5558,12 +5565,17 @@ static s32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move, stru //case EFFECT_SKY_DROP //break; case EFFECT_JUNGLE_HEALING: + { + bool32 canCureSelf = (gBattleMons[battlerAtk].status1 & STATUS1_ANY) && ShouldCureStatus(battlerAtk, battlerAtk, aiData); + bool32 canCurePartner = (gBattleMons[BATTLE_PARTNER(battlerAtk)].status1 & STATUS1_ANY) && ShouldCureStatus(battlerAtk, BATTLE_PARTNER(battlerAtk), aiData); + if (ShouldRecover(battlerAtk, battlerDef, move, 25) || ShouldRecover(BATTLE_PARTNER(battlerAtk), battlerDef, move, 25) - || gBattleMons[battlerAtk].status1 & STATUS1_ANY - || gBattleMons[BATTLE_PARTNER(battlerAtk)].status1 & STATUS1_ANY) + || canCureSelf + || canCurePartner) ADJUST_SCORE(GOOD_EFFECT); break; + } case EFFECT_RAPID_SPIN: if ((AreAnyHazardsOnSide(GetBattlerSide(battlerAtk)) && CountUsablePartyMons(battlerAtk) != 0) || (gBattleMons[battlerAtk].volatiles.leechSeed || gBattleMons[battlerAtk].volatiles.wrapped)) diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index b5b887f37b..85864ccfe1 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -3839,6 +3839,7 @@ bool32 AnyPartyMemberStatused(u32 battlerId, bool32 checkSoundproof) { struct Pokemon *party; u32 i, battlerOnField1, battlerOnField2; + bool32 hasStatusToCure = FALSE; party = GetBattlerParty(battlerId); @@ -3850,8 +3851,9 @@ bool32 AnyPartyMemberStatused(u32 battlerId, bool32 checkSoundproof) if ((GetConfig(CONFIG_HEAL_BELL_SOUNDPROOF) == GEN_5 || gAiLogicData->abilities[BATTLE_PARTNER(battlerId)] != ABILITY_SOUNDPROOF || !checkSoundproof) - && GetMonData(&party[battlerOnField2], MON_DATA_STATUS) != STATUS1_NONE) - return TRUE; + && GetMonData(&party[battlerOnField2], MON_DATA_STATUS) != STATUS1_NONE + && ShouldCureStatus(battlerId, BATTLE_PARTNER(battlerId), gAiLogicData)) + hasStatusToCure = TRUE; } else // In singles there's only one battlerId by side. { @@ -3863,8 +3865,9 @@ bool32 AnyPartyMemberStatused(u32 battlerId, bool32 checkSoundproof) if ((GetConfig(CONFIG_HEAL_BELL_SOUNDPROOF) == GEN_5 || GetConfig(CONFIG_HEAL_BELL_SOUNDPROOF) >= GEN_8 || gAiLogicData->abilities[battlerId] != ABILITY_SOUNDPROOF || !checkSoundproof) - && GetMonData(&party[battlerOnField1], MON_DATA_STATUS) != STATUS1_NONE) - return TRUE; + && GetMonData(&party[battlerOnField1], MON_DATA_STATUS) != STATUS1_NONE + && ShouldCureStatus(battlerId, battlerId, gAiLogicData)) + hasStatusToCure = TRUE; // Check inactive party mons' status for (i = 0; i < PARTY_SIZE; i++) @@ -3879,7 +3882,7 @@ bool32 AnyPartyMemberStatused(u32 battlerId, bool32 checkSoundproof) return TRUE; } - return FALSE; + return hasStatusToCure; } bool32 ShouldUseRecoilMove(u32 battlerAtk, u32 battlerDef, u32 recoilDmg, u32 moveIndex) diff --git a/test/battle/ai/ai_check_viability.c b/test/battle/ai/ai_check_viability.c index a605cf7da6..5ec4161a17 100644 --- a/test/battle/ai/ai_check_viability.c +++ b/test/battle/ai/ai_check_viability.c @@ -232,6 +232,29 @@ AI_DOUBLE_BATTLE_TEST("AI chooses moves that cure self or partner") } } +AI_DOUBLE_BATTLE_TEST("AI uses Refresh only when curing status is worthwhile") +{ + u32 status1; + enum Ability ability; + u32 expectedMove; + + PARAMETRIZE { status1 = STATUS1_BURN; ability = ABILITY_GUTS; expectedMove = MOVE_ROCK_SLIDE; } + PARAMETRIZE { status1 = STATUS1_BURN; ability = ABILITY_PRESSURE; expectedMove = MOVE_REFRESH; } + PARAMETRIZE { status1 = STATUS1_TOXIC_POISON; ability = ABILITY_POISON_HEAL; expectedMove = MOVE_ROCK_SLIDE; } + PARAMETRIZE { status1 = STATUS1_TOXIC_POISON; ability = ABILITY_SCRAPPY; expectedMove = MOVE_REFRESH; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_REFRESH) == EFFECT_REFRESH); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_REGIROCK) { Moves(MOVE_ROCK_SLIDE, MOVE_REFRESH); Status1(status1); Ability(ability); } + OPPONENT(SPECIES_EXPLOUD) { Moves(MOVE_CELEBRATE); } + } WHEN { + TURN { EXPECT_MOVE(opponentLeft, expectedMove); } + } +} + AI_SINGLE_BATTLE_TEST("AI chooses moves that cure inactive party members") { u32 status, config; diff --git a/test/battle/ai/ai_doubles.c b/test/battle/ai/ai_doubles.c index 08ed4d7807..712c811286 100644 --- a/test/battle/ai/ai_doubles.c +++ b/test/battle/ai/ai_doubles.c @@ -139,6 +139,32 @@ AI_DOUBLE_BATTLE_TEST("AI won't use the same nondamaging move as its partner for } } +AI_DOUBLE_BATTLE_TEST("Heal Bell and Jungle Healing skip curing a partner that benefits from burn") +{ + u32 move; + + PARAMETRIZE { move = MOVE_HEAL_BELL; } + PARAMETRIZE { move = MOVE_JUNGLE_HEALING; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_HEAL_BELL) == EFFECT_HEAL_BELL); + ASSUME(GetMoveEffect(MOVE_JUNGLE_HEALING) == EFFECT_JUNGLE_HEALING); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_MEMENTO); Speed(1); } + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_MEMENTO); Speed(1); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(move, MOVE_SCRATCH); Speed(20); } + OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_GUTS); Moves(MOVE_TACKLE); Status1(STATUS1_BURN); MaxHP(200); HP(200); Speed(10); } + } WHEN { + TURN { + NOT_EXPECT_MOVE(opponentLeft, move); + EXPECT_MOVE(opponentLeft, MOVE_SCRATCH, target: playerLeft); + EXPECT_MOVE(opponentRight, MOVE_TACKLE, target: playerLeft); + MOVE(playerLeft, MOVE_MEMENTO); + MOVE(playerRight, MOVE_MEMENTO); + } + } +} + AI_DOUBLE_BATTLE_TEST("AI will not choose Earthquake if it damages the partner without a positive effect") { ASSUME(GetMoveTarget(MOVE_EARTHQUAKE) == MOVE_TARGET_FOES_AND_ALLY);