Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Minor switch AI refactor #4849

Merged
merged 1 commit into from
Jun 22, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 27 additions & 41 deletions src/battle_ai_switch_items.c
Original file line number Diff line number Diff line change
Expand Up @@ -1312,7 +1312,7 @@ static bool32 IsMonGrounded(u16 heldItemEffect, u32 ability, u8 type1, u8 type2)
static u32 GetSwitchinHazardsDamage(u32 battler, struct BattlePokemon *battleMon)
{
u8 defType1 = battleMon->type1, defType2 = battleMon->type2, tSpikesLayers;
u16 heldItemEffect = gItemsInfo[battleMon->item].holdEffect;
u16 heldItemEffect = ItemId_GetHoldEffect(battleMon->item);
u32 maxHP = battleMon->maxHP, ability = battleMon->ability, status = battleMon->status1;
u32 spikesDamage = 0, tSpikesDamage = 0, hazardDamage = 0;
u32 hazardFlags = gSideStatuses[GetBattlerSide(battler)] & (SIDE_STATUS_SPIKES | SIDE_STATUS_STEALTH_ROCK | SIDE_STATUS_STICKY_WEB | SIDE_STATUS_TOXIC_SPIKES | SIDE_STATUS_SAFEGUARD);
Expand Down Expand Up @@ -1372,7 +1372,7 @@ static u32 GetSwitchinHazardsDamage(u32 battler, struct BattlePokemon *battleMon
static s32 GetSwitchinWeatherImpact(void)
{
s32 weatherImpact = 0, maxHP = AI_DATA->switchinCandidate.battleMon.maxHP, ability = AI_DATA->switchinCandidate.battleMon.ability;
u32 holdEffect = gItemsInfo[AI_DATA->switchinCandidate.battleMon.item].holdEffect;
u32 holdEffect = ItemId_GetHoldEffect(AI_DATA->switchinCandidate.battleMon.item);

if (WEATHER_HAS_EFFECT)
{
Expand Down Expand Up @@ -1436,7 +1436,7 @@ static s32 GetSwitchinWeatherImpact(void)
static u32 GetSwitchinRecurringHealing(void)
{
u32 recurringHealing = 0, maxHP = AI_DATA->switchinCandidate.battleMon.maxHP, ability = AI_DATA->switchinCandidate.battleMon.ability;
u32 holdEffect = gItemsInfo[AI_DATA->switchinCandidate.battleMon.item].holdEffect;
u32 holdEffect = ItemId_GetHoldEffect(AI_DATA->switchinCandidate.battleMon.item);

// Items
if (ability != ABILITY_KLUTZ)
Expand Down Expand Up @@ -1470,7 +1470,7 @@ static u32 GetSwitchinRecurringHealing(void)
static u32 GetSwitchinRecurringDamage(void)
{
u32 passiveDamage = 0, maxHP = AI_DATA->switchinCandidate.battleMon.maxHP, ability = AI_DATA->switchinCandidate.battleMon.ability;
u32 holdEffect = gItemsInfo[AI_DATA->switchinCandidate.battleMon.item].holdEffect;
u32 holdEffect = ItemId_GetHoldEffect(AI_DATA->switchinCandidate.battleMon.item);

// Items
if (ability != ABILITY_MAGIC_GUARD && ability != ABILITY_KLUTZ)
Expand Down Expand Up @@ -1502,7 +1502,7 @@ static u32 GetSwitchinStatusDamage(u32 battler)
{
u8 defType1 = AI_DATA->switchinCandidate.battleMon.type1, defType2 = AI_DATA->switchinCandidate.battleMon.type2;
u8 tSpikesLayers = gSideTimers[GetBattlerSide(battler)].toxicSpikesAmount;
u16 heldItemEffect = gItemsInfo[AI_DATA->switchinCandidate.battleMon.item].holdEffect;
u16 heldItemEffect = ItemId_GetHoldEffect(AI_DATA->switchinCandidate.battleMon.item);
u32 status = AI_DATA->switchinCandidate.battleMon.status1, ability = AI_DATA->switchinCandidate.battleMon.ability, maxHP = AI_DATA->switchinCandidate.battleMon.maxHP;
u32 statusDamage = 0;

Expand Down Expand Up @@ -1580,8 +1580,8 @@ static u32 GetSwitchinHitsToKO(s32 damageTaken, u32 battler)
u32 recurringHealing = GetSwitchinRecurringHealing();
u32 statusDamage = GetSwitchinStatusDamage(battler);
u32 hitsToKO = 0, singleUseItemHeal = 0;
u16 maxHP = AI_DATA->switchinCandidate.battleMon.maxHP, item = AI_DATA->switchinCandidate.battleMon.item, heldItemEffect = gItemsInfo[item].holdEffect;
u8 weatherDuration = gWishFutureKnock.weatherDuration, holdEffectParam = gItemsInfo[item].holdEffectParam;
u16 maxHP = AI_DATA->switchinCandidate.battleMon.maxHP, item = AI_DATA->switchinCandidate.battleMon.item, heldItemEffect = ItemId_GetHoldEffect(item);
u8 weatherDuration = gWishFutureKnock.weatherDuration, holdEffectParam = ItemId_GetHoldEffectParam(item);
u32 opposingBattler = GetBattlerAtPosition(BATTLE_OPPOSITE(GetBattlerPosition(battler)));
u32 opposingAbility = gBattleMons[opposingBattler].ability;
bool32 usedSingleUseHealingItem = FALSE;
Expand Down Expand Up @@ -1803,7 +1803,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,

InitializeSwitchinCandidate(&party[i]);

// While not really invalid per say, not really wise to switch into this mon
// While not really invalid per se, not really wise to switch into this mon
if (AI_DATA->switchinCandidate.battleMon.ability == ABILITY_TRUANT && IsTruantMonVulnerable(battler, opposingBattler))
continue;

Expand Down Expand Up @@ -1883,7 +1883,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
// If AI mon outspeeds and doesn't die to hazards
if ((((aiMonSpeed > playerMonSpeed && !(gFieldStatuses & STATUS_FIELD_TRICK_ROOM)) || aiMovePriority > 0) // Outspeed if not Trick Room
|| ((gFieldStatuses & STATUS_FIELD_TRICK_ROOM) // Trick Room
&& (aiMonSpeed < playerMonSpeed || (gItemsInfo[AI_DATA->switchinCandidate.battleMon.item].holdEffect == HOLD_EFFECT_ROOM_SERVICE && aiMonSpeed * 2 / 3 < playerMonSpeed)))) // Trick Room speeds
&& (aiMonSpeed < playerMonSpeed || (ItemId_GetHoldEffect(AI_DATA->switchinCandidate.battleMon.item) == HOLD_EFFECT_ROOM_SERVICE && aiMonSpeed * 2 / 3 < playerMonSpeed)))) // Trick Room speeds
&& AI_DATA->switchinCandidate.battleMon.hp > GetSwitchinHazardsDamage(battler, &AI_DATA->switchinCandidate.battleMon)) // Hazards
{
// We have a revenge killer
Expand All @@ -1908,7 +1908,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
// If AI mon outspeeds
if (((aiMonSpeed > playerMonSpeed && !(gFieldStatuses & STATUS_FIELD_TRICK_ROOM)) || aiMovePriority > 0) // Outspeed if not Trick Room
|| (((gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && gFieldTimers.trickRoomTimer > 1) // Trick Room has at least 2 turns left
&& (aiMonSpeed < playerMonSpeed || (gItemsInfo[AI_DATA->switchinCandidate.battleMon.item].holdEffect == HOLD_EFFECT_ROOM_SERVICE && aiMonSpeed * 2/ 3 < playerMonSpeed)))) // Trick Room speeds
&& (aiMonSpeed < playerMonSpeed || (ItemId_GetHoldEffect(AI_DATA->switchinCandidate.battleMon.item) == HOLD_EFFECT_ROOM_SERVICE && aiMonSpeed * 2/ 3 < playerMonSpeed)))) // Trick Room speeds
{
// If AI mon can't be OHKO'd
if (hitsToKOAI > hitsToKOAIThreshold)
Expand Down Expand Up @@ -1949,39 +1949,25 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
// Different switching priorities depending on switching mid battle vs switching after a KO
if (isSwitchAfterKO)
{
// Return Trapper > GetBestMonRevengeKiller > GetBestMonTypeMatchup > GetBestMonBatonPass > GetBestMonDmg
if (trapperId != PARTY_SIZE)
return trapperId;
else if (revengeKillerId != PARTY_SIZE)
return revengeKillerId;
else if (slowRevengeKillerId != PARTY_SIZE)
return slowRevengeKillerId;
else if (fastThreatenId != PARTY_SIZE)
return fastThreatenId;
else if (slowThreatenId != PARTY_SIZE)
return slowThreatenId;
else if (typeMatchupEffectiveId != PARTY_SIZE)
return typeMatchupEffectiveId;
else if (typeMatchupId != PARTY_SIZE)
return typeMatchupId;
else if (batonPassId != PARTY_SIZE)
return batonPassId;
else if (damageMonId != PARTY_SIZE)
return damageMonId;
// Return Trapper > Revenge Killer > Type Matchup > Baton Pass > Best Damage
if (trapperId != PARTY_SIZE) return trapperId;
else if (revengeKillerId != PARTY_SIZE) return revengeKillerId;
else if (slowRevengeKillerId != PARTY_SIZE) return slowRevengeKillerId;
else if (fastThreatenId != PARTY_SIZE) return fastThreatenId;
else if (slowThreatenId != PARTY_SIZE) return slowThreatenId;
else if (typeMatchupEffectiveId != PARTY_SIZE) return typeMatchupEffectiveId;
else if (typeMatchupId != PARTY_SIZE) return typeMatchupId;
else if (batonPassId != PARTY_SIZE) return batonPassId;
else if (damageMonId != PARTY_SIZE) return damageMonId;
}
else
{
// Return Trapper > GetBestMonTypeMatchup > GetBestMonDefensive > GetBestMonBatonPass
if (trapperId != PARTY_SIZE)
return trapperId;
else if (typeMatchupEffectiveId != PARTY_SIZE)
return typeMatchupEffectiveId;
else if (typeMatchupId != PARTY_SIZE)
return typeMatchupId;
else if (defensiveMonId != PARTY_SIZE)
return defensiveMonId;
else if (batonPassId != PARTY_SIZE)
return batonPassId;
// Return Trapper > Type Matchup > Best Defensive > Baton Pass
if (trapperId != PARTY_SIZE) return trapperId;
else if (typeMatchupEffectiveId != PARTY_SIZE) return typeMatchupEffectiveId;
else if (typeMatchupId != PARTY_SIZE) return typeMatchupId;
else if (defensiveMonId != PARTY_SIZE) return defensiveMonId;
else if (batonPassId != PARTY_SIZE) return batonPassId;

// If ace mon is the last available Pokemon and U-Turn/Volt Switch was used - switch to the mon.
else if (aceMonId != PARTY_SIZE
Expand Down Expand Up @@ -2052,7 +2038,7 @@ u8 GetMostSuitableMonToSwitchInto(u32 battler, bool32 switchAfterMonKOd)
|| gBattlerPartyIndexes[battlerIn2] == i
|| i == gBattleStruct->monToSwitchIntoId[battlerIn1]
|| i == gBattleStruct->monToSwitchIntoId[battlerIn2]
|| (GetMonAbility(&party[i]) == ABILITY_TRUANT && IsTruantMonVulnerable(battler, opposingBattler))) // While not really invalid per say, not really wise to switch into this mon.)
|| (GetMonAbility(&party[i]) == ABILITY_TRUANT && IsTruantMonVulnerable(battler, opposingBattler))) // While not really invalid per se, not really wise to switch into this mon.)
{
invalidMons |= gBitTable[i];
}
Expand Down
Loading