Fix Substitute / Shed Tail Switch AI (#6334)

This commit is contained in:
Pawkkie 2025-02-25 07:06:39 -05:00 committed by GitHub
parent 9479b67964
commit b109c4c36b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 60 additions and 24 deletions

View File

@ -155,6 +155,7 @@ bool32 HasHighCritRatioMove(u32 battler);
bool32 HasMagicCoatAffectedMove(u32 battler);
bool32 HasSnatchAffectedMove(u32 battler);
bool32 IsHazardClearingMove(u32 move);
bool32 IsSubstituteEffect(u32 effect);
// status checks
bool32 AI_CanGetFrostbite(u32 battler, u32 ability);
@ -216,7 +217,7 @@ bool32 AI_ShouldCopyStatChanges(u32 battlerAtk, u32 battlerDef);
bool32 AI_ShouldSetUpHazards(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData);
void IncreaseTidyUpScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score);
bool32 AI_ShouldSpicyExtract(u32 battlerAtk, u32 battlerAtkPartner, u32 move, struct AiLogicData *aiData);
void IncreaseSubstituteMoveScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score);
u32 IncreaseSubstituteMoveScore(u32 battlerAtk, u32 battlerDef, u32 move);
bool32 IsBattlerPredictedToSwitch(u32 battler);
bool32 HasLowAccuracyMove(u32 battlerAtk, u32 battlerDef);

View File

@ -3650,7 +3650,8 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
break;
case EFFECT_SUBSTITUTE:
case EFFECT_SHED_TAIL:
IncreaseSubstituteMoveScore(battlerAtk, battlerDef, move, &score);
ADJUST_SCORE(IncreaseSubstituteMoveScore(battlerAtk, battlerDef, move));
break;
case EFFECT_MIMIC:
if (AI_IsFaster(battlerAtk, battlerDef, move))
{

View File

@ -1021,6 +1021,23 @@ static bool32 ShouldSwitchIfAttackingStatsLowered(u32 battler)
return FALSE;
}
static bool32 HasGoodSubstituteMove(u32 battler)
{
int i;
u32 aiMove, aiMoveEffect, opposingBattler = GetOppositeBattler(battler);
for (i = 0; i < MAX_MON_MOVES; i++)
{
aiMove = gBattleMons[battler].moves[i];
aiMoveEffect = GetMoveEffect(aiMove);
if (IsSubstituteEffect(aiMoveEffect))
{
if (IncreaseSubstituteMoveScore(battler, opposingBattler, aiMove) > 0)
return TRUE;
}
}
return FALSE;
}
bool32 ShouldSwitch(u32 battler)
{
u32 battlerIn1, battlerIn2;
@ -1085,19 +1102,21 @@ bool32 ShouldSwitch(u32 battler)
return FALSE;
// NOTE: The sequence of the below functions matter! Do not change unless you have carefully considered the outcome.
// Since the order is sequencial, and some of these functions prompt switch to specific party members.
// Since the order is sequential, and some of these functions prompt switch to specific party members.
// FindMon functions can prompt a switch to specific party members that override GetMostSuitableMonToSwitchInto
// The rest can prompt a switch to party member returned by GetMostSuitableMonToSwitchInto
// These Functions can prompt switch to specific party members that override GetMostSuitableMonToSwitchInto
if (FindMonThatHitsWonderGuard(battler))
return TRUE;
if (FindMonThatAbsorbsOpponentsMove(battler))
return TRUE;
// These Functions can prompt switch to party member returned by GetMostSuitableMonToSwitchInto
if ((AI_THINKING_STRUCT->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SMART_SWITCHING) && (CanMonSurviveHazardSwitchin(battler) == FALSE))
return FALSE;
if (HasGoodSubstituteMove(battler))
return FALSE;
if (ShouldSwitchIfTrapperInParty(battler))
return TRUE;
if (FindMonThatAbsorbsOpponentsMove(battler))
return TRUE;
if (ShouldSwitchIfOpponentChargingOrInvulnerable(battler))
return TRUE;
if (ShouldSwitchIfTruant(battler))

View File

@ -2427,6 +2427,19 @@ bool32 IsSwitchOutEffect(u32 effect)
}
}
bool32 IsSubstituteEffect(u32 effect)
{
// Substitute effects like Substitute, Shed Tail, etc.
switch (effect)
{
case EFFECT_SUBSTITUTE:
case EFFECT_SHED_TAIL:
return TRUE;
default:
return FALSE;
}
}
bool32 IsChaseEffect(u32 effect)
{
// Effects that hit switching out mons like Pursuit
@ -4232,31 +4245,32 @@ bool32 AI_ShouldSpicyExtract(u32 battlerAtk, u32 battlerAtkPartner, u32 move, st
&& HasMoveWithCategory(battlerAtkPartner, DAMAGE_CATEGORY_PHYSICAL));
}
void IncreaseSubstituteMoveScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score)
u32 IncreaseSubstituteMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
{
u32 effect = GetMoveEffect(move);
u32 scoreIncrease = 0;
if (effect == EFFECT_SUBSTITUTE) // Substitute specific
{
if (HasAnyKnownMove(battlerDef) && GetBestDmgFromBattler(battlerDef, battlerAtk) < gBattleMons[battlerAtk].maxHP / 4)
ADJUST_SCORE_PTR(GOOD_EFFECT);
scoreIncrease += GOOD_EFFECT;
}
else if (effect == EFFECT_SHED_TAIL) // Shed Tail specific
{
if ((ShouldPivot(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_THINKING_STRUCT->movesetIndex))
&& (HasAnyKnownMove(battlerDef) && (GetBestDmgFromBattler(battlerDef, battlerAtk) < gBattleMons[battlerAtk].maxHP / 2)))
ADJUST_SCORE_PTR(BEST_EFFECT);
scoreIncrease += BEST_EFFECT;
}
if (gStatuses3[battlerDef] & STATUS3_PERISH_SONG)
ADJUST_SCORE_PTR(GOOD_EFFECT);
scoreIncrease += GOOD_EFFECT;
if (gBattleMons[battlerDef].status1 & STATUS1_SLEEP)
ADJUST_SCORE_PTR(GOOD_EFFECT);
scoreIncrease += GOOD_EFFECT;
else if (gBattleMons[battlerDef].status1 & (STATUS1_BURN | STATUS1_PSN_ANY | STATUS1_FROSTBITE))
ADJUST_SCORE_PTR(DECENT_EFFECT);
scoreIncrease += DECENT_EFFECT;
if (IsBattlerPredictedToSwitch(battlerDef))
ADJUST_SCORE(DECENT_EFFECT);
scoreIncrease += DECENT_EFFECT;
if (HasMoveEffect(battlerDef, EFFECT_SLEEP)
|| HasMoveEffect(battlerDef, EFFECT_TOXIC)
@ -4265,10 +4279,11 @@ void IncreaseSubstituteMoveScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *
|| HasMoveEffect(battlerDef, EFFECT_WILL_O_WISP)
|| HasMoveEffect(battlerDef, EFFECT_CONFUSE)
|| HasMoveEffect(battlerDef, EFFECT_LEECH_SEED))
ADJUST_SCORE_PTR(GOOD_EFFECT);
scoreIncrease += GOOD_EFFECT;
if (AI_DATA->hpPercents[battlerAtk] > 70)
ADJUST_SCORE_PTR(WEAK_EFFECT);
scoreIncrease += WEAK_EFFECT;
return scoreIncrease;
}
bool32 HasLowAccuracyMove(u32 battlerAtk, u32 battlerDef)

View File

@ -86,22 +86,22 @@ SINGLE_BATTLE_TEST("Shed Tail's HP cost doesn't trigger effects that trigger on
}
}
// Passes for some reason even though it seems there is some code missing
/*
AI_SINGLE_BATTLE_TEST("AI will use Shed Tail to pivot to another mon while in damage stalemate with player")
AI_SINGLE_BATTLE_TEST("AI will use Shed Tail to pivot to another mon while in damage stalemate with player rather than hard switching")
{
KNOWN_FAILING; // missing AI code
u32 aiFlags;
PARAMETRIZE { aiFlags = 0; }
PARAMETRIZE { aiFlags = AI_FLAG_SMART_SWITCHING | AI_FLAG_OMNISCIENT | AI_FLAG_SMART_MON_CHOICES; }
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiFlags);
PLAYER(SPECIES_WOBBUFFET) { Speed(100); Ability(ABILITY_RUN_AWAY); Moves(MOVE_TACKLE, MOVE_CELEBRATE); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(50); Ability(ABILITY_RUN_AWAY); Moves(MOVE_CONFUSION, MOVE_SHED_TAIL); }
OPPONENT(SPECIES_SCIZOR) { Speed(101); Moves(MOVE_CELEBRATE, MOVE_X_SCISSOR); }
} WHEN {
TURN { MOVE(player, MOVE_TACKLE); EXPECT_MOVE(opponent, MOVE_CONFUSION); }
if (aiFlags == 0)
TURN { MOVE(player, MOVE_TACKLE); EXPECT_MOVE(opponent, MOVE_CONFUSION); }
TURN { MOVE(player, MOVE_TACKLE); EXPECT_MOVE(opponent, MOVE_SHED_TAIL); }
}
}
*/
SINGLE_BATTLE_TEST("Shed Tail creates a Substitute with 1/4 of user maximum health")
{