Respect beneficial status in healing moves and add tests (#8478)

This commit is contained in:
GGbond 2025-12-09 23:58:11 +08:00 committed by GitHub
parent aa172aad6c
commit 04b26b752e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 77 additions and 13 deletions

View File

@ -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))

View File

@ -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)

View File

@ -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;

View File

@ -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);