Fix bad odds / defensive switchin double switch cases (#6927)

This commit is contained in:
Pawkkie 2025-05-26 10:59:25 -04:00 committed by GitHub
parent de463af0df
commit 99073dc9f2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 76 additions and 15 deletions

View File

@ -180,6 +180,7 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler)
bool32 getsOneShot = FALSE, hasStatusMove = FALSE, hasSuperEffectiveMove = FALSE;
u16 typeEffectiveness = UQ_4_12(1.0); //baseline typing damage
enum BattleMoveEffects aiMoveEffect;
u32 hitsToKoPlayer = 0, hitsToKoAI = 0;
// Only use this if AI_FLAG_SMART_SWITCHING is set for the trainer
if (!(gAiThinkingStruct->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SMART_SWITCHING))
@ -238,6 +239,8 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler)
}
}
hitsToKoPlayer = GetNoOfHitsToKOBattlerDmg(maxDamageDealt, opposingBattler);
// Calculate type advantage
typeEffectiveness = uq4_12_multiply(typeEffectiveness, (GetTypeModifier(atkType1, defType1)));
if (atkType2 != atkType1)
@ -266,6 +269,8 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler)
}
}
hitsToKoAI = GetNoOfHitsToKOBattlerDmg(maxDamageTaken, battler);
// Check if mon gets one shot
if(maxDamageTaken > gBattleMons[battler].hp
&& !(gItemsInfo[gBattleMons[battler].item].holdEffect == HOLD_EFFECT_FOCUS_SASH || (!IsMoldBreakerTypeAbility(opposingBattler, gBattleMons[opposingBattler].ability) && B_STURDY >= GEN_5 && aiAbility == ABILITY_STURDY)))
@ -273,12 +278,9 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler)
getsOneShot = TRUE;
}
// Check if current mon can outspeed and KO in spite of bad matchup, and don't switch out if it can
if(damageDealt > gBattleMons[opposingBattler].hp)
{
if (AI_IsFaster(battler, opposingBattler, aiBestMove))
return FALSE;
}
// Check if current mon can 1v1 in spite of bad matchup, and don't switch out if it can
if(hitsToKoPlayer < hitsToKoAI || (hitsToKoPlayer == hitsToKoAI && AI_IsFaster(battler, opposingBattler, aiBestMove)))
return FALSE;
// If we don't have any other viable options, don't switch out
if (gAiLogicData->mostSuitableMonId[battler] == PARTY_SIZE)
@ -2063,14 +2065,6 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
hitsToKOAI = GetSwitchinHitsToKO(GetMaxDamagePlayerCouldDealToSwitchin(battler, opposingBattler, gAiLogicData->switchinCandidate.battleMon), battler);
typeMatchup = GetSwitchinTypeMatchup(opposingBattler, gAiLogicData->switchinCandidate.battleMon);
// Track max hits to KO and set defensive mon
if(hitsToKOAI > maxHitsToKO)
{
maxHitsToKO = hitsToKOAI;
if(maxHitsToKO > defensiveMonHitKOThreshold)
defensiveMonId = i;
}
// Check through current mon's moves
for (j = 0; j < MAX_MON_MOVES; j++)
{
@ -2100,6 +2094,14 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
}
}
// Track max hits to KO and set defensive mon
if(hitsToKOAI > maxHitsToKO && (canSwitchinWin1v1 || gAiThinkingStruct->aiFlags[battler] & AI_FLAG_STALL))
{
maxHitsToKO = hitsToKOAI;
if(maxHitsToKO > defensiveMonHitKOThreshold)
defensiveMonId = i;
}
// Check for mon with resistance and super effective move for best type matchup mon with effective move
if (aiMove != MOVE_NONE && !IsBattleMoveStatus(aiMove))
{

View File

@ -296,7 +296,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Mid-battle switches prioritize
PARAMETRIZE { expectedIndex = 3; move1 = MOVE_SCRATCH; move2 = MOVE_SCRATCH; aiSmartSwitchFlags = 0; } // When not smart, AI will only switch in a defensive mon if it has a SE move, otherwise will just default to damage
PARAMETRIZE { expectedIndex = 1; move1 = MOVE_GIGA_DRAIN; move2 = MOVE_SCRATCH; aiSmartSwitchFlags = 0; }
PARAMETRIZE { expectedIndex = 2; move1 = MOVE_SCRATCH; move2 = MOVE_SCRATCH; aiSmartSwitchFlags = AI_FLAG_SMART_MON_CHOICES; } // When smart, AI will prioritize SE move, but still switch in good type matchup without SE move
PARAMETRIZE { expectedIndex = 2; move1 = MOVE_SCRATCH; move2 = MOVE_WATER_PULSE; aiSmartSwitchFlags = AI_FLAG_SMART_MON_CHOICES; } // When smart, AI will prioritize SE move, but still switch in good type matchup without SE move
PARAMETRIZE { expectedIndex = 1; move1 = MOVE_GIGA_DRAIN; move2 = MOVE_SCRATCH; aiSmartSwitchFlags = AI_FLAG_SMART_MON_CHOICES; }
GIVEN {
@ -1180,3 +1180,62 @@ AI_SINGLE_BATTLE_TEST("Switch AI: AI will use pivot move to activate Palafin's Z
TURN { MOVE(player, MOVE_CELEBRATE); EXPECT_MOVE(opponent, MOVE_FLIP_TURN); EXPECT_SEND_OUT(opponent, 1); }
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI won't send out defensive mon that can lose 1v1, or switch out a mon that can win 1v1 even with bad type matchup")
{
PASSES_RANDOMLY(100, 100, RNG_AI_SWITCH_HASBADODDS);
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_OMNISCIENT);
PLAYER(SPECIES_PANPOUR) {
Level(15);
Moves(MOVE_WATER_PULSE, MOVE_PLAY_NICE, MOVE_FURY_SWIPES, MOVE_LICK);
Item(ITEM_MYSTIC_WATER);
Ability(ABILITY_GLUTTONY);
Nature(NATURE_MODEST);
HPIV(31);
AttackIV(31);
DefenseIV(31);
SpAttackIV(31);
SpDefenseIV(31);
SpeedIV(31); }
OPPONENT(SPECIES_RHYHORN) {
Level(14);
Moves(MOVE_ROCK_TOMB, MOVE_HORN_ATTACK, MOVE_BULLDOZE, MOVE_ROCK_SMASH);
Item(ITEM_RINDO_BERRY);
Ability(ABILITY_LIGHTNING_ROD);
Nature(NATURE_ADAMANT);
HPIV(31);
AttackIV(31);
DefenseIV(31);
SpAttackIV(31);
SpDefenseIV(31);
SpeedIV(31); }
OPPONENT(SPECIES_GLIGAR) {
Level(15);
Moves(MOVE_WING_ATTACK, MOVE_QUICK_ATTACK, MOVE_BULLDOZE);
Item(ITEM_ORAN_BERRY);
Ability(ABILITY_SAND_VEIL);
Nature(NATURE_ADAMANT);
HPIV(31);
AttackIV(31);
DefenseIV(31);
SpAttackIV(31);
SpDefenseIV(31);
SpeedIV(31); }
OPPONENT(SPECIES_WOOPER_PALDEA) {
Level(15);
Moves(MOVE_MUD_SHOT, MOVE_ACID_SPRAY, MOVE_YAWN, MOVE_SANDSTORM);
Item(ITEM_ORAN_BERRY);
Ability(ABILITY_WATER_ABSORB);
Nature(NATURE_MODEST);
HPIV(31);
AttackIV(31);
DefenseIV(31);
SpAttackIV(31);
SpDefenseIV(31);
SpeedIV(31); }
} WHEN {
TURN { MOVE(player, MOVE_WATER_PULSE); EXPECT_MOVE(opponent, MOVE_BULLDOZE); EXPECT_SEND_OUT(opponent, 1); }
TURN { MOVE(player, MOVE_WATER_PULSE); EXPECT_MOVE(opponent, MOVE_BULLDOZE); }
}
}