Merge branch 'master' into master-to-upcoming

This commit is contained in:
Pawkkie 2025-07-13 13:34:35 -04:00
commit bc4ad07f47
2 changed files with 36 additions and 11 deletions

View File

@ -262,13 +262,10 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler)
if (playerMove != MOVE_NONE && !IsBattleMoveStatus(playerMove) && GetMoveEffect(playerMove) != EFFECT_FOCUS_PUNCH)
{
damageTaken = AI_GetDamage(opposingBattler, battler, i, AI_DEFENDING, gAiLogicData);
if (playerMove == gBattleStruct->choicedMove[opposingBattler]) // If player is choiced, only care about the choice locked move
if (damageTaken > maxDamageTaken && !AI_DoesChoiceEffectBlockMove(opposingBattler, playerMove))
{
return maxDamageTaken = damageTaken;
break;
}
if (damageTaken > maxDamageTaken)
maxDamageTaken = damageTaken;
}
}
}
@ -282,7 +279,7 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler)
}
// 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)))
if ((hitsToKoPlayer != 0 && (hitsToKoPlayer < hitsToKoAI || hitsToKoAI == 0)) || (hitsToKoPlayer == hitsToKoAI && AI_IsFaster(battler, opposingBattler, aiBestMove)))
return FALSE;
// If we don't have any other viable options, don't switch out
@ -1810,7 +1807,7 @@ static u32 GetSwitchinHitsToKO(s32 damageTaken, u32 battler)
// No damage being dealt
if ((damageTaken + statusDamage + recurringDamage <= recurringHealing) || damageTaken + statusDamage + recurringDamage == 0)
return startingHP;
return hitsToKO;
// Mon fainted to hazards
if (startingHP == 0)
@ -2018,6 +2015,18 @@ static inline bool32 IsFreeSwitch(enum SwitchType switchType, u32 battlerSwitchi
static inline bool32 CanSwitchinWin1v1(u32 hitsToKOAI, u32 hitsToKOPlayer, bool32 isSwitchinFirst, bool32 isFreeSwitch)
{
// Player's best move deals 0 damage
if (hitsToKOAI == 0 && hitsToKOPlayer > 0)
return TRUE;
// AI's best move deals 0 damage
if (hitsToKOPlayer == 0 && hitsToKOAI > 0)
return FALSE;
// Neither mon can damage the other
if (hitsToKOPlayer == 0 && hitsToKOAI == 0)
return FALSE;
// Free switch, need to outspeed or take 1 extra hit
if (isFreeSwitch)
{
@ -2034,7 +2043,7 @@ static inline bool32 CanSwitchinWin1v1(u32 hitsToKOAI, u32 hitsToKOPlayer, bool3
// Everything runs in the same loop to minimize computation time. This makes it harder to read, but hopefully the comments can guide you!
static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId, u32 battler, u32 opposingBattler, u32 battlerIn1, u32 battlerIn2, enum SwitchType switchType)
{
int revengeKillerId = PARTY_SIZE, slowRevengeKillerId = PARTY_SIZE, fastThreatenId = PARTY_SIZE, slowThreatenId = PARTY_SIZE, damageMonId = PARTY_SIZE;
int revengeKillerId = PARTY_SIZE, slowRevengeKillerId = PARTY_SIZE, fastThreatenId = PARTY_SIZE, slowThreatenId = PARTY_SIZE, damageMonId = PARTY_SIZE, generic1v1MonId = PARTY_SIZE;
int batonPassId = PARTY_SIZE, typeMatchupId = PARTY_SIZE, typeMatchupEffectiveId = PARTY_SIZE, defensiveMonId = PARTY_SIZE, aceMonId = PARTY_SIZE, trapperId = PARTY_SIZE;
int i, j, aliveCount = 0, bits = 0, aceMonCount = 0;
s32 defensiveMonHitKOThreshold = 3; // 3HKO threshold that candidate defensive mons must exceed
@ -2079,9 +2088,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
for (j = 0; j < MAX_MON_MOVES; j++)
{
aiMove = gAiLogicData->switchinCandidate.battleMon.moves[j];
if (aiMove != MOVE_NONE && !IsBattleMoveStatus(aiMove))
damageDealt = AI_CalcPartyMonDamage(aiMove, battler, opposingBattler, gAiLogicData->switchinCandidate.battleMon, AI_ATTACKING);
damageDealt = AI_CalcPartyMonDamage(aiMove, battler, opposingBattler, gAiLogicData->switchinCandidate.battleMon, AI_ATTACKING);
// Offensive switchin decisions are based on which whether switchin moves first and whether it can win a 1v1
isSwitchinFirst = AI_WhoStrikesFirstPartyMon(battler, opposingBattler, gAiLogicData->switchinCandidate.battleMon, aiMove);
@ -2112,6 +2119,9 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
defensiveMonId = i;
}
if (canSwitchinWin1v1)
generic1v1MonId = i;
// Check for mon with resistance and super effective move for best type matchup mon with effective move
if (aiMove != MOVE_NONE && !IsBattleMoveStatus(aiMove))
{
@ -2192,6 +2202,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
else if (typeMatchupEffectiveId != PARTY_SIZE) return typeMatchupEffectiveId;
else if (typeMatchupId != PARTY_SIZE) return typeMatchupId;
else if (batonPassId != PARTY_SIZE) return batonPassId;
else if (generic1v1MonId != PARTY_SIZE) return generic1v1MonId;
else if (damageMonId != PARTY_SIZE) return damageMonId;
}
else
@ -2202,6 +2213,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
else if (typeMatchupId != PARTY_SIZE) return typeMatchupId;
else if (defensiveMonId != PARTY_SIZE) return defensiveMonId;
else if (batonPassId != PARTY_SIZE) return batonPassId;
else if (generic1v1MonId != PARTY_SIZE) return generic1v1MonId;
}
// If ace mon is the last available Pokemon and U-Turn/Volt Switch or Eject Pack/Button was used - switch to the mon.
if (aceMonId != PARTY_SIZE && CountUsablePartyMons(battler) <= aceMonCount

View File

@ -1254,3 +1254,16 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI won't send out defensive mon
TURN { MOVE(player, MOVE_WATER_PULSE); EXPECT_MOVE(opponent, MOVE_BULLDOZE); }
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI considers 0 hits to KO as losing a 1v1")
{
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_JOLTEON) { Level(100); Ability(ABILITY_VOLT_ABSORB); Moves(MOVE_TACKLE); }
OPPONENT(SPECIES_ZIGZAGOON) { Level(1); HP(1); Moves(MOVE_TACKLE); }
OPPONENT(SPECIES_TANGELA) { Level(100); Moves(MOVE_THUNDERBOLT); }
OPPONENT(SPECIES_TANGELA) { Level(100); Moves(MOVE_GIGA_DRAIN); }
} WHEN {
TURN { MOVE(player, MOVE_TACKLE); EXPECT_SEND_OUT(opponent, 2); }
}
}