Use stored values for ai switch-in effectiveness checks (#7794)

Co-authored-by: Bassoonian <iasperbassoonian@gmail.com>
This commit is contained in:
Alex 2025-09-25 14:41:06 +02:00 committed by GitHub
parent 66b7a7bf1e
commit 5e07d4a509
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 39 additions and 31 deletions

View File

@ -172,6 +172,7 @@ uq4_12_t AI_GetMoveEffectiveness(u32 move, u32 battlerAtk, u32 battlerDef);
u16 *GetMovesArray(u32 battler);
bool32 IsConfusionMoveEffect(enum BattleMoveEffects moveEffect);
bool32 HasMove(u32 battlerId, u32 move);
u32 GetIndexInMoveArray(u32 battler, u32 move);
bool32 HasOnlyMovesWithCategory(u32 battlerId, enum DamageCategory category, bool32 onlyOffensive);
bool32 HasMoveWithCategory(u32 battler, enum DamageCategory category);
bool32 HasMoveWithType(u32 battler, u32 type);
@ -292,7 +293,7 @@ void IncreaseSleepScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score);
void IncreaseConfusionScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score);
void IncreaseFrostbiteScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score);
s32 AI_CalcPartyMonDamage(u32 move, u32 battlerAtk, u32 battlerDef, struct BattlePokemon switchinCandidate, enum DamageCalcContext calcContext);
s32 AI_CalcPartyMonDamage(u32 move, u32 battlerAtk, u32 battlerDef, struct BattlePokemon switchinCandidate, uq4_12_t *effectiveness, enum DamageCalcContext calcContext);
u32 AI_WhoStrikesFirstPartyMon(u32 battlerAtk, u32 battlerDef, struct BattlePokemon switchinCandidate, u32 aiMoveConsidered, u32 playerMoveConsidered, enum ConsiderPriority ConsiderPriority);
s32 AI_TryToClearStats(u32 battlerAtk, u32 battlerDef, bool32 isDoubleBattle);
bool32 AI_ShouldCopyStatChanges(u32 battlerAtk, u32 battlerDef);

View File

@ -276,7 +276,7 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler)
if (!IsBattleMoveStatus(aiMove) && !AI_DoesChoiceEffectBlockMove(battler, aiMove))
{
// Check if mon has a super effective move
if (AI_GetMoveEffectiveness(aiMove, battler, opposingBattler) >= UQ_4_12(2.0))
if (gAiLogicData->effectiveness[battler][opposingBattler][i] >= UQ_4_12(2.0))
hasSuperEffectiveMove = TRUE;
// Get maximum damage mon can deal
@ -408,9 +408,10 @@ static bool32 ShouldSwitchIfAllMovesBad(u32 battler)
for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++)
{
aiMove = gBattleMons[battler].moves[moveIndex];
if ((AI_GetMoveEffectiveness(aiMove, battler, opposingBattler) > UQ_4_12(0.0)
|| AI_GetMoveEffectiveness(aiMove, battler, opposingPartner) > UQ_4_12(0.0))
&& aiMove != MOVE_NONE)
if (aiMove == MOVE_NONE)
continue;
if (gAiLogicData->effectiveness[battler][opposingBattler][moveIndex] > UQ_4_12(0.0)
|| gAiLogicData->effectiveness[battler][opposingPartner][moveIndex] > UQ_4_12(0.0))
return FALSE;
}
}
@ -419,7 +420,7 @@ static bool32 ShouldSwitchIfAllMovesBad(u32 battler)
for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++)
{
aiMove = gBattleMons[battler].moves[moveIndex];
if (AI_GetMoveEffectiveness(aiMove, battler, opposingBattler) > UQ_4_12(0.0) && aiMove != MOVE_NONE
if (gAiLogicData->effectiveness[battler][opposingBattler][moveIndex] > UQ_4_12(0.0) && aiMove != MOVE_NONE
&& !CanAbilityAbsorbMove(battler, opposingBattler, gAiLogicData->abilities[opposingBattler], aiMove, GetBattleMoveType(aiMove), AI_CHECK)
&& !CanAbilityBlockMove(battler, opposingBattler, gBattleMons[battler].ability, gAiLogicData->abilities[opposingBattler], aiMove, AI_CHECK)
&& (!ALL_MOVES_BAD_STATUS_MOVES_BAD || gMovesInfo[aiMove].power != 0)) // If using ALL_MOVES_BAD_STATUS_MOVES_BAD, then need power to be non-zero
@ -441,7 +442,7 @@ static bool32 ShouldSwitchIfAllMovesBad(u32 battler)
static bool32 ShouldSwitchIfWonderGuard(u32 battler)
{
u32 opposingBattler = GetOppositeBattler(battler);
u32 i, move;
u32 i;
if (IsDoubleBattle())
return FALSE;
@ -452,12 +453,8 @@ static bool32 ShouldSwitchIfWonderGuard(u32 battler)
// Check if Pokémon has a super effective move.
for (i = 0; i < MAX_MON_MOVES; i++)
{
move = gBattleMons[battler].moves[i];
if (move != MOVE_NONE)
{
if (AI_GetMoveEffectiveness(move, battler, opposingBattler) >= UQ_4_12(2.0))
return FALSE;
}
if (gBattleMons[battler].moves[i] != MOVE_NONE && gAiLogicData->effectiveness[battler][opposingBattler][i] >= UQ_4_12(2.0))
return FALSE;
}
if (RandomPercentage(RNG_AI_SWITCH_WONDER_GUARD, GetSwitchChance(SHOULD_SWITCH_WONDER_GUARD)))
@ -707,8 +704,8 @@ static bool32 ShouldSwitchIfBadlyStatused(u32 battler)
|| monAbility == ABILITY_EARLY_BIRD)
|| holdEffect == (HOLD_EFFECT_CURE_SLP | HOLD_EFFECT_CURE_STATUS)
|| HasMove(battler, MOVE_SLEEP_TALK)
|| (HasMoveWithEffect(battler, MOVE_SNORE) && AI_GetMoveEffectiveness(MOVE_SNORE, battler, opposingBattler) >= UQ_4_12(2.0))
|| (IsBattlerGrounded(battler, gAiLogicData->abilities[battler], gAiLogicData->holdEffects[battler])
|| (HasMove(battler, MOVE_SNORE) && gAiLogicData->effectiveness[battler][opposingBattler][GetIndexInMoveArray(battler, MOVE_SNORE)] >= UQ_4_12(2.0))
|| (IsBattlerGrounded(battler, monAbility, gAiLogicData->holdEffects[battler])
&& (HasMove(battler, MOVE_MISTY_TERRAIN) || HasMove(battler, MOVE_ELECTRIC_TERRAIN)))
)
switchMon = FALSE;
@ -831,10 +828,8 @@ static bool32 CanUseSuperEffectiveMoveAgainstOpponents(u32 battler)
if (move == MOVE_NONE || AI_DoesChoiceEffectBlockMove(battler, move))
continue;
if (AI_GetMoveEffectiveness(move, battler, opposingBattler) >= UQ_4_12(2.0))
{
if (gAiLogicData->effectiveness[battler][opposingBattler][i] >= UQ_4_12(2.0))
return TRUE;
}
}
}
if (!IsDoubleBattle())
@ -850,10 +845,8 @@ static bool32 CanUseSuperEffectiveMoveAgainstOpponents(u32 battler)
if (move == MOVE_NONE || AI_DoesChoiceEffectBlockMove(battler, move))
continue;
if (AI_GetMoveEffectiveness(move, battler, opposingBattler) >= UQ_4_12(2.0))
{
if (gAiLogicData->effectiveness[battler][opposingBattler][i] >= UQ_4_12(2.0))
return TRUE;
}
}
}
@ -1020,7 +1013,7 @@ static bool32 ShouldSwitchIfEncored(u32 battler)
return SetSwitchinAndSwitch(battler, PARTY_SIZE);
// Stay in if effective move
else if (AI_GetMoveEffectiveness(encoredMove, battler, opposingBattler) >= UQ_4_12(2.0))
else if (gAiLogicData->effectiveness[battler][opposingBattler][GetIndexInMoveArray(battler, encoredMove)] >= UQ_4_12(2.0))
return FALSE;
// Switch out 50% of the time otherwise
@ -1495,8 +1488,8 @@ static u32 GetBestMonDmg(struct Pokemon *party, int firstId, int lastId, u8 inva
int i, j;
int dmg, bestDmg = 0;
int bestMonId = PARTY_SIZE;
u32 aiMove;
uq4_12_t effectiveness;
// If we couldn't find the best mon in terms of typing, find the one that deals most damage.
for (i = firstId; i < lastId; i++)
@ -1510,7 +1503,7 @@ static u32 GetBestMonDmg(struct Pokemon *party, int firstId, int lastId, u8 inva
if (aiMove != MOVE_NONE && !IsBattleMoveStatus(aiMove))
{
aiMove = GetMonData(&party[i], MON_DATA_MOVE1 + j);
dmg = AI_CalcPartyMonDamage(aiMove, battler, opposingBattler, gAiLogicData->switchinCandidate.battleMon, AI_ATTACKING);
dmg = AI_CalcPartyMonDamage(aiMove, battler, opposingBattler, gAiLogicData->switchinCandidate.battleMon, &effectiveness, AI_ATTACKING);
if (bestDmg < dmg)
{
bestDmg = dmg;
@ -1987,13 +1980,14 @@ static s32 GetMaxDamagePlayerCouldDealToSwitchin(u32 battler, u32 opposingBattle
u32 playerMove;
u16 *playerMoves = GetMovesArray(opposingBattler);
s32 damageTaken = 0, maxDamageTaken = 0;
uq4_12_t effectiveness;
for (i = 0; i < MAX_MON_MOVES; i++)
{
playerMove = SMART_SWITCHING_OMNISCIENT ? gBattleMons[opposingBattler].moves[i] : playerMoves[i];
if (playerMove != MOVE_NONE && !IsBattleMoveStatus(playerMove) && GetMoveEffect(playerMove) != EFFECT_FOCUS_PUNCH && gBattleMons[opposingBattler].pp[i] > 0)
{
damageTaken = AI_CalcPartyMonDamage(playerMove, opposingBattler, battler, battleMon, AI_DEFENDING);
damageTaken = AI_CalcPartyMonDamage(playerMove, opposingBattler, battler, battleMon, &effectiveness, AI_DEFENDING);
if (playerMove == gBattleStruct->choicedMove[opposingBattler]) // If player is choiced, only care about the choice locked move
return damageTaken;
if (damageTaken > maxDamageTaken)
@ -2012,6 +2006,7 @@ static s32 GetMaxPriorityDamagePlayerCouldDealToSwitchin(u32 battler, u32 opposi
u32 playerMove;
u16 *playerMoves = GetMovesArray(opposingBattler);
s32 damageTaken = 0, maxDamageTaken = 0;
uq4_12_t effectiveness = UQ_4_12(1.0);
for (i = 0; i < MAX_MON_MOVES; i++)
{
@ -2019,7 +2014,7 @@ static s32 GetMaxPriorityDamagePlayerCouldDealToSwitchin(u32 battler, u32 opposi
if (GetBattleMovePriority(opposingBattler, gAiLogicData->abilities[opposingBattler], playerMove) > 0
&& playerMove != MOVE_NONE && !IsBattleMoveStatus(playerMove) && GetMoveEffect(playerMove) != EFFECT_FOCUS_PUNCH && gBattleMons[opposingBattler].pp[i] > 0)
{
damageTaken = AI_CalcPartyMonDamage(playerMove, opposingBattler, battler, battleMon, AI_DEFENDING);
damageTaken = AI_CalcPartyMonDamage(playerMove, opposingBattler, battler, battleMon, &effectiveness, AI_DEFENDING);
if (playerMove == gBattleStruct->choicedMove[opposingBattler]) // If player is choiced, only care about the choice locked move
return damageTaken;
if (damageTaken > maxDamageTaken)
@ -2119,6 +2114,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
u32 bestResist = UQ_4_12(2.0), bestResistEffective = UQ_4_12(2.0), typeMatchup; // 2.0 is the default "Neutral" matchup from GetBattleMonTypeMatchup
bool32 isFreeSwitch = IsFreeSwitch(switchType, battlerIn1, opposingBattler), isSwitchinFirst, isSwitchinFirstPriority, canSwitchinWin1v1;
u32 invalidMons = 0;
uq4_12_t effectiveness = UQ_4_12(1.0);
// Iterate through mons
for (i = firstId; i < lastId; i++)
@ -2163,7 +2159,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
continue;
aiMove = gAiLogicData->switchinCandidate.battleMon.moves[j];
damageDealt = AI_CalcPartyMonDamage(aiMove, battler, opposingBattler, gAiLogicData->switchinCandidate.battleMon, AI_ATTACKING);
damageDealt = AI_CalcPartyMonDamage(aiMove, battler, opposingBattler, gAiLogicData->switchinCandidate.battleMon, &effectiveness, AI_ATTACKING);
hitsToKOPlayer = GetNoOfHitsToKOBattlerDmg(damageDealt, opposingBattler);
// Offensive switchin decisions are based on which whether switchin moves first and whether it can win a 1v1
@ -2204,7 +2200,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
{
if (typeMatchup < bestResistEffective)
{
if (AI_GetMoveEffectiveness(aiMove, battler, opposingBattler) >= UQ_4_12(2.0))
if (effectiveness >= UQ_4_12(2.0))
{
if (canSwitchinWin1v1)
{

View File

@ -2313,6 +2313,18 @@ u16 *GetMovesArray(u32 battler)
return gBattleHistory->usedMoves[battler];
}
u32 GetIndexInMoveArray(u32 battler, u32 move)
{
u16 *moves = GetMovesArray(battler);
u32 i;
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (moves[i] == move)
return i;
}
return MAX_MON_MOVES;
}
bool32 HasOnlyMovesWithCategory(u32 battlerId, enum DamageCategory category, bool32 onlyOffensive)
{
u32 i;
@ -4319,10 +4331,9 @@ static void SetBattlerFieldStatusForSwitchin(u32 battler)
}
// party logic
s32 AI_CalcPartyMonDamage(u32 move, u32 battlerAtk, u32 battlerDef, struct BattlePokemon switchinCandidate, enum DamageCalcContext calcContext)
s32 AI_CalcPartyMonDamage(u32 move, u32 battlerAtk, u32 battlerDef, struct BattlePokemon switchinCandidate, uq4_12_t *effectiveness, enum DamageCalcContext calcContext)
{
struct SimulatedDamage dmg;
uq4_12_t effectiveness;
struct BattlePokemon *savedBattleMons = AllocSaveBattleMons();
if (calcContext == AI_ATTACKING)
@ -4342,7 +4353,7 @@ s32 AI_CalcPartyMonDamage(u32 move, u32 battlerAtk, u32 battlerDef, struct Battl
gAiThinkingStruct->saved[battlerAtk].saved = FALSE;
}
dmg = AI_CalcDamage(move, battlerAtk, battlerDef, &effectiveness, NO_GIMMICK, NO_GIMMICK, AI_GetSwitchinWeather(switchinCandidate));
dmg = AI_CalcDamage(move, battlerAtk, battlerDef, effectiveness, NO_GIMMICK, NO_GIMMICK, AI_GetSwitchinWeather(switchinCandidate));
// restores original gBattleMon struct
FreeRestoreBattleMons(savedBattleMons);