pokeemerald/src/battle_ai_switch_items.c

1161 lines
39 KiB
C
Raw Normal View History

2017-10-10 18:01:45 +02:00
#include "global.h"
#include "battle.h"
#include "constants/battle_ai.h"
2020-12-20 14:47:20 -07:00
#include "battle_ai_main.h"
2020-12-15 21:57:33 -07:00
#include "battle_ai_util.h"
#include "battle_util.h"
#include "battle_anim.h"
2017-10-10 18:01:45 +02:00
#include "battle_controllers.h"
#include "battle_main.h"
#include "constants/hold_effects.h"
2019-01-27 20:54:34 +01:00
#include "battle_setup.h"
2021-10-02 23:47:59 -04:00
#include "data.h"
#include "item.h"
#include "party_menu.h"
2017-10-10 18:01:45 +02:00
#include "pokemon.h"
#include "random.h"
2017-10-10 18:01:45 +02:00
#include "util.h"
2018-11-14 00:01:50 +00:00
#include "constants/abilities.h"
2019-04-14 17:20:26 +02:00
#include "constants/item_effects.h"
#include "constants/battle_move_effects.h"
2017-12-05 11:55:48 -06:00
#include "constants/items.h"
2018-11-14 00:01:50 +00:00
#include "constants/moves.h"
2017-10-10 18:01:45 +02:00
// this file's functions
2017-10-11 12:49:42 +02:00
static bool8 HasSuperEffectiveMoveAgainstOpponents(bool8 noRng);
2018-07-15 12:39:07 +02:00
static bool8 FindMonWithFlagsAndSuperEffective(u16 flags, u8 moduloPercent);
2017-10-11 12:49:42 +02:00
static bool8 ShouldUseItem(void);
static bool32 AiExpectsToFaintPlayer(void);
static bool32 AI_ShouldHeal(u32 healAmount);
static bool32 AI_OpponentCanFaintAiWithMod(u32 healAmount);
2023-07-18 11:36:09 +02:00
static bool32 IsAiPartyMonOHKOBy(u32 battlerAtk, struct Pokemon *aiMon);
2017-10-10 18:01:45 +02:00
2023-02-16 15:57:15 +01:00
static bool32 IsAceMon(u32 battlerId, u32 monPartyId)
2023-02-08 12:42:38 +01:00
{
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_ACE_POKEMON
2023-02-16 21:58:00 +01:00
&& !(gBattleStruct->forcedSwitch & gBitTable[battlerId])
2023-02-08 12:42:38 +01:00
&& monPartyId == CalculateEnemyPartyCount()-1)
return TRUE;
return FALSE;
}
2019-01-27 20:54:34 +01:00
void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId)
{
if (BATTLE_TWO_VS_ONE_OPPONENT && (battlerId & BIT_SIDE) == B_SIDE_OPPONENT)
{
*firstId = 0, *lastId = PARTY_SIZE;
2019-01-27 20:54:34 +01:00
}
else if (gBattleTypeFlags & (BATTLE_TYPE_TWO_OPPONENTS | BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_TOWER_LINK_MULTI))
2019-01-27 20:54:34 +01:00
{
if ((battlerId & BIT_FLANK) == B_FLANK_LEFT)
*firstId = 0, *lastId = PARTY_SIZE / 2;
2019-01-27 20:54:34 +01:00
else
*firstId = PARTY_SIZE / 2, *lastId = PARTY_SIZE;
2019-01-27 20:54:34 +01:00
}
else
{
*firstId = 0, *lastId = PARTY_SIZE;
2019-01-27 20:54:34 +01:00
}
}
static bool8 ShouldSwitchIfAllBadMoves(void)
{
if (gBattleResources->ai->switchMon)
{
gBattleResources->ai->switchMon = 0;
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = PARTY_SIZE;
BtlController_EmitTwoReturnValues(BUFFER_B, B_ACTION_SWITCH, 0);
return TRUE;
}
else
{
return FALSE;
}
}
2017-10-11 12:49:42 +02:00
static bool8 ShouldSwitchIfWonderGuard(void)
2017-10-10 18:01:45 +02:00
{
2018-01-16 15:12:38 -06:00
u8 opposingPosition;
2018-06-17 16:48:58 +02:00
u8 opposingBattler;
2017-10-10 18:01:45 +02:00
s32 i, j;
s32 firstId;
s32 lastId; // + 1
struct Pokemon *party = NULL;
u16 move;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
return FALSE;
2018-02-06 16:09:39 -06:00
opposingPosition = BATTLE_OPPOSITE(GetBattlerPosition(gActiveBattler));
2017-10-10 18:01:45 +02:00
if (GetBattlerAbility(GetBattlerAtPosition(opposingPosition)) != ABILITY_WONDER_GUARD)
2017-10-10 18:01:45 +02:00
return FALSE;
2018-06-17 16:48:58 +02:00
// Check if Pokemon has a super effective move.
for (opposingBattler = GetBattlerAtPosition(opposingPosition), i = 0; i < MAX_MON_MOVES; i++)
2017-10-10 18:01:45 +02:00
{
2018-02-05 19:46:59 -06:00
move = gBattleMons[gActiveBattler].moves[i];
2018-07-15 12:39:07 +02:00
if (move != MOVE_NONE)
{
if (AI_GetTypeEffectiveness(move, gActiveBattler, opposingBattler) >= UQ_4_12(2.0))
return FALSE;
}
2017-10-10 18:01:45 +02:00
}
2018-06-17 16:48:58 +02:00
// Get party information.
2019-01-27 20:54:34 +01:00
GetAIPartyIndexes(gActiveBattler, &firstId, &lastId);
2017-10-10 18:01:45 +02:00
2018-02-05 19:46:59 -06:00
if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
2017-10-10 18:01:45 +02:00
party = gPlayerParty;
else
party = gEnemyParty;
2018-06-17 16:48:58 +02:00
// Find a Pokemon in the party that has a super effective move.
2017-10-10 18:01:45 +02:00
for (i = firstId; i < lastId; i++)
{
2023-07-26 09:34:23 +02:00
if (!IsValidForBattle(&party[i]))
2017-10-10 18:01:45 +02:00
continue;
2018-02-06 13:48:02 -06:00
if (i == gBattlerPartyIndexes[gActiveBattler])
2017-10-10 18:01:45 +02:00
continue;
2023-02-16 15:57:15 +01:00
if (IsAceMon(gActiveBattler, i))
2022-09-18 00:50:21 -03:00
continue;
2017-10-10 18:01:45 +02:00
for (opposingBattler = GetBattlerAtPosition(opposingPosition), j = 0; j < MAX_MON_MOVES; j++)
2017-10-10 18:01:45 +02:00
{
move = GetMonData(&party[i], MON_DATA_MOVE1 + j);
2018-07-15 12:39:07 +02:00
if (move != MOVE_NONE)
2017-10-10 18:01:45 +02:00
{
2018-07-15 12:39:07 +02:00
if (AI_GetTypeEffectiveness(move, gActiveBattler, opposingBattler) >= UQ_4_12(2.0) && Random() % 3 < 2)
{
// We found a mon.
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = i;
BtlController_EmitTwoReturnValues(BUFFER_B, B_ACTION_SWITCH, 0);
2018-07-15 12:39:07 +02:00
return TRUE;
}
2017-10-10 18:01:45 +02:00
}
}
}
2018-06-17 16:48:58 +02:00
return FALSE; // There is not a single Pokemon in the party that has a super effective move against a mon with Wonder Guard.
2017-10-10 18:01:45 +02:00
}
2017-10-11 12:49:42 +02:00
static bool8 FindMonThatAbsorbsOpponentsMove(void)
2017-10-10 18:01:45 +02:00
{
2018-06-17 16:48:58 +02:00
u8 battlerIn1, battlerIn2;
u16 absorbingTypeAbility;
2017-10-10 18:01:45 +02:00
s32 firstId;
s32 lastId; // + 1
struct Pokemon *party;
s32 i;
if (HasSuperEffectiveMoveAgainstOpponents(TRUE) && Random() % 3 != 0)
return FALSE;
2022-07-11 15:18:13 +01:00
if (gLastLandedMoves[gActiveBattler] == MOVE_NONE)
2017-10-10 18:01:45 +02:00
return FALSE;
2022-07-11 15:18:13 +01:00
if (gLastLandedMoves[gActiveBattler] == MOVE_UNAVAILABLE)
2017-10-10 18:01:45 +02:00
return FALSE;
if (IS_MOVE_STATUS(gLastLandedMoves[gActiveBattler]))
2017-10-10 18:01:45 +02:00
return FALSE;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
{
2018-06-17 16:48:58 +02:00
battlerIn1 = gActiveBattler;
2018-02-06 16:09:39 -06:00
if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler)))])
2018-06-17 16:48:58 +02:00
battlerIn2 = gActiveBattler;
2017-10-10 18:01:45 +02:00
else
2018-06-17 16:48:58 +02:00
battlerIn2 = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler)));
2017-10-10 18:01:45 +02:00
}
else
{
2018-06-17 16:48:58 +02:00
battlerIn1 = gActiveBattler;
battlerIn2 = gActiveBattler;
2017-10-10 18:01:45 +02:00
}
2018-02-05 19:46:59 -06:00
if (gBattleMoves[gLastLandedMoves[gActiveBattler]].type == TYPE_FIRE)
2017-10-10 18:01:45 +02:00
absorbingTypeAbility = ABILITY_FLASH_FIRE;
2018-02-05 19:46:59 -06:00
else if (gBattleMoves[gLastLandedMoves[gActiveBattler]].type == TYPE_WATER)
2017-10-10 18:01:45 +02:00
absorbingTypeAbility = ABILITY_WATER_ABSORB;
2018-02-05 19:46:59 -06:00
else if (gBattleMoves[gLastLandedMoves[gActiveBattler]].type == TYPE_ELECTRIC)
2017-10-10 18:01:45 +02:00
absorbingTypeAbility = ABILITY_VOLT_ABSORB;
else
return FALSE;
if (AI_DATA->abilities[gActiveBattler] == absorbingTypeAbility)
2017-10-10 18:01:45 +02:00
return FALSE;
2019-01-27 20:54:34 +01:00
GetAIPartyIndexes(gActiveBattler, &firstId, &lastId);
2017-10-10 18:01:45 +02:00
2018-02-05 19:46:59 -06:00
if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
2017-10-10 18:01:45 +02:00
party = gPlayerParty;
else
party = gEnemyParty;
for (i = firstId; i < lastId; i++)
{
u16 monAbility;
2017-10-10 18:01:45 +02:00
2023-07-26 09:34:23 +02:00
if (!IsValidForBattle(&party[i]))
2017-10-10 18:01:45 +02:00
continue;
2018-06-17 16:48:58 +02:00
if (i == gBattlerPartyIndexes[battlerIn1])
2017-10-10 18:01:45 +02:00
continue;
2018-06-17 16:48:58 +02:00
if (i == gBattlerPartyIndexes[battlerIn2])
2017-10-10 18:01:45 +02:00
continue;
2018-06-17 16:48:58 +02:00
if (i == *(gBattleStruct->monToSwitchIntoId + battlerIn1))
2017-10-10 18:01:45 +02:00
continue;
2018-06-17 16:48:58 +02:00
if (i == *(gBattleStruct->monToSwitchIntoId + battlerIn2))
2017-10-10 18:01:45 +02:00
continue;
2023-02-16 15:57:15 +01:00
if (IsAceMon(gActiveBattler, i))
2022-09-18 00:50:21 -03:00
continue;
2023-02-08 12:42:38 +01:00
monAbility = GetMonAbility(&party[i]);
2017-10-10 18:01:45 +02:00
if (absorbingTypeAbility == monAbility && Random() & 1)
{
2018-06-17 16:48:58 +02:00
// we found a mon.
2018-02-05 19:46:59 -06:00
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = i;
BtlController_EmitTwoReturnValues(BUFFER_B, B_ACTION_SWITCH, 0);
2017-10-10 18:01:45 +02:00
return TRUE;
}
}
return FALSE;
}
static bool8 ShouldSwitchIfGameStatePrompt(void)
2017-10-10 18:01:45 +02:00
{
bool8 switchMon = FALSE;
2022-08-12 15:21:25 -06:00
u16 monAbility = AI_DATA->abilities[gActiveBattler];
u16 holdEffect = AI_DATA->holdEffects[gActiveBattler];
u8 opposingPosition = BATTLE_OPPOSITE(GetBattlerPosition(gActiveBattler));
u8 opposingBattler = GetBattlerAtPosition(opposingPosition);
s32 moduloChance = 4; //25% Chance Default
s32 chanceReducer = 1; //No Reduce default. Increase to reduce
s32 firstId;
s32 lastId;
s32 i;
struct Pokemon *party;
if (AnyStatIsRaised(gActiveBattler))
chanceReducer = 5; // Reduce switchout probability by factor of 5 if setup
2017-10-10 18:01:45 +02:00
//Perish Song
if (gStatuses3[gActiveBattler] & STATUS3_PERISH_SONG
&& gDisableStructs[gActiveBattler].perishSongTimer == 0
&& monAbility != ABILITY_SOUNDPROOF)
switchMon = TRUE;
2017-10-10 18:01:45 +02:00
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_SMART_SWITCHING)
2017-10-10 18:01:45 +02:00
{
//Yawn
if (gStatuses3[gActiveBattler] & STATUS3_YAWN
&& AI_CanSleep(gActiveBattler, monAbility)
&& gBattleMons[gActiveBattler].hp > gBattleMons[gActiveBattler].maxHP / 3)
{
switchMon = TRUE;
//Double Battles
//Check if partner can prevent sleep
if (IsDoubleBattle())
{
if (IsBattlerAlive(BATTLE_PARTNER(gActiveBattler))
&& (GetAIChosenMove(BATTLE_PARTNER(gActiveBattler)) == MOVE_UPROAR)
)
switchMon = FALSE;
2023-02-08 12:42:38 +01:00
if (IsBattlerAlive(BATTLE_PARTNER(gActiveBattler))
&& (gBattleMoves[AI_DATA->partnerMove].effect == EFFECT_MISTY_TERRAIN
|| gBattleMoves[AI_DATA->partnerMove].effect == EFFECT_ELECTRIC_TERRAIN)
&& IsBattlerGrounded(gActiveBattler)
)
switchMon = FALSE;
if (*(gBattleStruct->AI_monToSwitchIntoId + BATTLE_PARTNER(gActiveBattler)) != PARTY_SIZE) //Partner is switching
{
GetAIPartyIndexes(gActiveBattler, &firstId, &lastId);
2023-02-08 12:42:38 +01:00
if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
party = gPlayerParty;
2023-02-08 12:42:38 +01:00
for (i = firstId; i < lastId; i++)
{
2023-02-16 15:57:15 +01:00
if (IsAceMon(gActiveBattler, i))
2023-02-08 12:42:38 +01:00
continue;
//Look for mon in party that is able to be switched into and has ability that sets terrain
2023-07-26 09:34:23 +02:00
if (IsValidForBattle(&party[i])
&& i != gBattlerPartyIndexes[gActiveBattler]
&& i != gBattlerPartyIndexes[BATTLE_PARTNER(gActiveBattler)]
&& IsBattlerGrounded(gActiveBattler)
&& (GetMonAbility(&party[i]) == ABILITY_MISTY_SURGE
|| GetMonAbility(&party[i]) == ABILITY_ELECTRIC_SURGE)) //Ally has Misty or Electric Surge
{
*(gBattleStruct->AI_monToSwitchIntoId + BATTLE_PARTNER(gActiveBattler)) = i;
BtlController_EmitTwoReturnValues(BUFFER_B, B_ACTION_SWITCH, 0);
switchMon = FALSE;
break;
}
}
}
}
2023-02-08 12:42:38 +01:00
//Check if Active Pokemon can KO opponent instead of switching
//Will still fall asleep, but take out opposing Pokemon first
if (AiExpectsToFaintPlayer())
switchMon = FALSE;
//Checks to see if active Pokemon can do something against sleep
if ((monAbility == ABILITY_NATURAL_CURE
|| monAbility == ABILITY_SHED_SKIN
|| monAbility == ABILITY_EARLY_BIRD)
|| holdEffect == (HOLD_EFFECT_CURE_SLP | HOLD_EFFECT_CURE_STATUS)
|| HasMove(gActiveBattler, MOVE_SLEEP_TALK)
|| (HasMoveEffect(gActiveBattler, MOVE_SNORE) && AI_GetTypeEffectiveness(MOVE_SNORE, gActiveBattler, opposingBattler) >= UQ_4_12(1.0))
|| (IsBattlerGrounded(gActiveBattler)
&& (HasMove(gActiveBattler, MOVE_MISTY_TERRAIN) || HasMove(gActiveBattler, MOVE_ELECTRIC_TERRAIN)))
)
switchMon = FALSE;
//Check if Active Pokemon evasion boosted and might be able to dodge until awake
if (gBattleMons[gActiveBattler].statStages[STAT_EVASION] > (DEFAULT_STAT_STAGE + 3)
2022-08-12 15:21:25 -06:00
&& AI_DATA->abilities[opposingBattler] != ABILITY_UNAWARE
&& AI_DATA->abilities[opposingBattler] != ABILITY_KEEN_EYE
&& !(gBattleMons[gActiveBattler].status2 & STATUS2_FORESIGHT)
2023-02-08 12:42:38 +01:00
&& !(gStatuses3[gActiveBattler] & STATUS3_MIRACLE_EYED))
switchMon = FALSE;
}
//Secondary Damage
if (monAbility != ABILITY_MAGIC_GUARD
&& !AiExpectsToFaintPlayer())
{
//Toxic
moduloChance = 2; //50%
2022-09-12 20:39:21 -06:00
if (((gBattleMons[gActiveBattler].status1 & STATUS1_TOXIC_COUNTER) >= STATUS1_TOXIC_TURN(2))
&& gBattleMons[gActiveBattler].hp >= (gBattleMons[gActiveBattler].maxHP / 3)
&& (Random() % (moduloChance*chanceReducer)) == 0)
switchMon = TRUE;
2023-02-08 12:42:38 +01:00
//Cursed
moduloChance = 2; //50%
if (gBattleMons[gActiveBattler].status2 & STATUS2_CURSED
&& (Random() % (moduloChance*chanceReducer)) == 0)
switchMon = TRUE;
//Nightmare
moduloChance = 3; //33.3%
if (gBattleMons[gActiveBattler].status2 & STATUS2_NIGHTMARE
&& (Random() % (moduloChance*chanceReducer)) == 0)
switchMon = TRUE;
//Leech Seed
moduloChance = 4; //25%
if (gStatuses3[gActiveBattler] & STATUS3_LEECHSEED
&& (Random() % (moduloChance*chanceReducer)) == 0)
switchMon = TRUE;
}
2017-10-10 18:01:45 +02:00
//Infatuation
if (gBattleMons[gActiveBattler].status2 & STATUS2_INFATUATION
&& !AiExpectsToFaintPlayer())
switchMon = TRUE;
//Todo
//Pass Wish Heal
2023-02-08 12:42:38 +01:00
//Semi-Invulnerable
2022-09-15 22:10:39 -04:00
if (gStatuses3[opposingBattler] & STATUS3_SEMI_INVULNERABLE)
{
if (FindMonThatAbsorbsOpponentsMove()) //If find absorber default to switch
switchMon = TRUE;
if (!AI_OpponentCanFaintAiWithMod(0)
&& AnyStatIsRaised(gActiveBattler))
switchMon = FALSE;
if (AiExpectsToFaintPlayer()
&& !WillAIStrikeFirst()
&& !AI_OpponentCanFaintAiWithMod(0))
switchMon = FALSE;
2022-09-15 18:34:15 -04:00
}
2017-10-10 18:01:45 +02:00
}
if (switchMon)
2017-10-10 18:01:45 +02:00
{
2018-06-17 16:48:58 +02:00
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = PARTY_SIZE;
BtlController_EmitTwoReturnValues(BUFFER_B, B_ACTION_SWITCH, 0);
2017-10-10 18:01:45 +02:00
return TRUE;
}
else
2017-10-10 18:01:45 +02:00
{
return FALSE;
2017-10-10 18:01:45 +02:00
}
}
2017-10-10 18:01:45 +02:00
static bool8 ShouldSwitchIfAbilityBenefit(void)
{
s32 monToSwitchId;
s32 moduloChance = 4; //25% Chance Default
s32 chanceReducer = 1; //No Reduce default. Increase to reduce
u8 battlerId = GetBattlerPosition(gActiveBattler);
2018-06-17 16:48:58 +02:00
if (AnyStatIsRaised(battlerId))
chanceReducer = 5; // Reduce switchout probability by factor of 5 if setup
//Check if ability is blocked
if (gStatuses3[gActiveBattler] & STATUS3_GASTRO_ACID
||IsNeutralizingGasOnField())
return FALSE;
2022-08-12 15:21:25 -06:00
switch(AI_DATA->abilities[gActiveBattler]) {
case ABILITY_NATURAL_CURE:
moduloChance = 4; //25%
2023-02-08 12:42:38 +01:00
//Attempt to cure bad ailment
if (gBattleMons[gActiveBattler].status1 & (STATUS1_SLEEP | STATUS1_FREEZE | STATUS1_TOXIC_POISON)
&& GetMostSuitableMonToSwitchInto() != PARTY_SIZE)
break;
//Attempt to cure lesser ailment
if ((gBattleMons[gActiveBattler].status1 & STATUS1_ANY)
&& (gBattleMons[gActiveBattler].hp >= gBattleMons[gActiveBattler].maxHP / 2)
&& GetMostSuitableMonToSwitchInto() != PARTY_SIZE
&& Random() % (moduloChance*chanceReducer) == 0)
break;
return FALSE;
case ABILITY_REGENERATOR:
moduloChance = 2; //50%
//Don't switch if ailment
2023-02-08 12:42:38 +01:00
if (gBattleMons[gActiveBattler].status1 & STATUS1_ANY)
return FALSE;
if ((gBattleMons[gActiveBattler].hp <= ((gBattleMons[gActiveBattler].maxHP * 2) / 3))
&& GetMostSuitableMonToSwitchInto() != PARTY_SIZE
&& Random() % (moduloChance*chanceReducer) == 0)
break;
2023-02-08 12:42:38 +01:00
return FALSE;
default:
2023-02-08 12:42:38 +01:00
return FALSE;
2017-10-10 18:01:45 +02:00
}
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = PARTY_SIZE;
BtlController_EmitTwoReturnValues(BUFFER_B, B_ACTION_SWITCH, 0);
return TRUE;
2017-10-10 18:01:45 +02:00
}
2017-10-11 12:49:42 +02:00
static bool8 HasSuperEffectiveMoveAgainstOpponents(bool8 noRng)
2017-10-10 18:01:45 +02:00
{
2018-01-16 15:12:38 -06:00
u8 opposingPosition;
2018-06-17 16:48:58 +02:00
u8 opposingBattler;
2017-10-10 18:01:45 +02:00
s32 i;
u16 move;
2018-02-06 16:09:39 -06:00
opposingPosition = BATTLE_OPPOSITE(GetBattlerPosition(gActiveBattler));
2018-06-17 16:48:58 +02:00
opposingBattler = GetBattlerAtPosition(opposingPosition);
2017-10-10 18:01:45 +02:00
2018-06-17 16:48:58 +02:00
if (!(gAbsentBattlerFlags & gBitTable[opposingBattler]))
2017-10-10 18:01:45 +02:00
{
for (i = 0; i < MAX_MON_MOVES; i++)
2017-10-10 18:01:45 +02:00
{
2018-02-05 19:46:59 -06:00
move = gBattleMons[gActiveBattler].moves[i];
2017-10-10 18:01:45 +02:00
if (move == MOVE_NONE)
continue;
2018-07-15 12:39:07 +02:00
if (AI_GetTypeEffectiveness(move, gActiveBattler, opposingBattler) >= UQ_4_12(2.0))
2017-10-10 18:01:45 +02:00
{
if (noRng)
return TRUE;
if (Random() % 10 != 0)
return TRUE;
}
}
}
if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
return FALSE;
2018-06-17 16:48:58 +02:00
opposingBattler = GetBattlerAtPosition(BATTLE_PARTNER(opposingPosition));
2017-10-10 18:01:45 +02:00
2018-06-17 16:48:58 +02:00
if (!(gAbsentBattlerFlags & gBitTable[opposingBattler]))
2017-10-10 18:01:45 +02:00
{
for (i = 0; i < MAX_MON_MOVES; i++)
2017-10-10 18:01:45 +02:00
{
2018-02-05 19:46:59 -06:00
move = gBattleMons[gActiveBattler].moves[i];
2017-10-10 18:01:45 +02:00
if (move == MOVE_NONE)
continue;
2018-07-15 12:39:07 +02:00
if (AI_GetTypeEffectiveness(move, gActiveBattler, opposingBattler) >= UQ_4_12(2.0))
2017-10-10 18:01:45 +02:00
{
if (noRng)
return TRUE;
if (Random() % 10 != 0)
return TRUE;
}
}
}
return FALSE;
}
2017-10-11 12:49:42 +02:00
static bool8 AreStatsRaised(void)
2017-10-10 18:01:45 +02:00
{
u8 buffedStatsValue = 0;
s32 i;
2018-11-18 20:00:36 +01:00
for (i = 0; i < NUM_BATTLE_STATS; i++)
2017-10-10 18:01:45 +02:00
{
2020-08-04 20:33:05 -04:00
if (gBattleMons[gActiveBattler].statStages[i] > DEFAULT_STAT_STAGE)
buffedStatsValue += gBattleMons[gActiveBattler].statStages[i] - DEFAULT_STAT_STAGE;
2017-10-10 18:01:45 +02:00
}
return (buffedStatsValue > 3);
}
2018-07-15 12:39:07 +02:00
static bool8 FindMonWithFlagsAndSuperEffective(u16 flags, u8 moduloPercent)
2017-10-10 18:01:45 +02:00
{
2018-06-17 16:48:58 +02:00
u8 battlerIn1, battlerIn2;
2017-10-10 18:01:45 +02:00
s32 firstId;
s32 lastId; // + 1
struct Pokemon *party;
s32 i, j;
u16 move;
2022-07-11 15:18:13 +01:00
if (gLastLandedMoves[gActiveBattler] == MOVE_NONE)
2017-10-10 18:01:45 +02:00
return FALSE;
2022-07-11 15:18:13 +01:00
if (gLastLandedMoves[gActiveBattler] == MOVE_UNAVAILABLE)
2017-10-10 18:01:45 +02:00
return FALSE;
2018-02-05 19:46:59 -06:00
if (gLastHitBy[gActiveBattler] == 0xFF)
2017-10-10 18:01:45 +02:00
return FALSE;
if (IS_MOVE_STATUS(gLastLandedMoves[gActiveBattler]))
2017-10-10 18:01:45 +02:00
return FALSE;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
{
2018-06-17 16:48:58 +02:00
battlerIn1 = gActiveBattler;
2018-02-06 16:09:39 -06:00
if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler)))])
2018-06-17 16:48:58 +02:00
battlerIn2 = gActiveBattler;
2017-10-10 18:01:45 +02:00
else
2018-06-17 16:48:58 +02:00
battlerIn2 = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler)));
2017-10-10 18:01:45 +02:00
}
else
{
2018-06-17 16:48:58 +02:00
battlerIn1 = gActiveBattler;
battlerIn2 = gActiveBattler;
2017-10-10 18:01:45 +02:00
}
2019-01-27 20:54:34 +01:00
GetAIPartyIndexes(gActiveBattler, &firstId, &lastId);
2017-10-10 18:01:45 +02:00
2018-02-05 19:46:59 -06:00
if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
2017-10-10 18:01:45 +02:00
party = gPlayerParty;
else
party = gEnemyParty;
for (i = firstId; i < lastId; i++)
{
2023-07-26 09:34:23 +02:00
u16 species, monAbility;
2017-10-10 18:01:45 +02:00
2023-07-26 09:34:23 +02:00
if (!IsValidForBattle(&party[i]))
2017-10-10 18:01:45 +02:00
continue;
2018-06-17 16:48:58 +02:00
if (i == gBattlerPartyIndexes[battlerIn1])
2017-10-10 18:01:45 +02:00
continue;
2018-06-17 16:48:58 +02:00
if (i == gBattlerPartyIndexes[battlerIn2])
2017-10-10 18:01:45 +02:00
continue;
2018-06-17 16:48:58 +02:00
if (i == *(gBattleStruct->monToSwitchIntoId + battlerIn1))
2017-10-10 18:01:45 +02:00
continue;
2018-06-17 16:48:58 +02:00
if (i == *(gBattleStruct->monToSwitchIntoId + battlerIn2))
2017-10-10 18:01:45 +02:00
continue;
2023-02-16 15:57:15 +01:00
if (IsAceMon(gActiveBattler, i))
2022-09-18 00:50:21 -03:00
continue;
2023-07-26 09:34:23 +02:00
species = GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG);
monAbility = GetMonAbility(&party[i]);
2018-07-15 12:39:07 +02:00
CalcPartyMonTypeEffectivenessMultiplier(gLastLandedMoves[gActiveBattler], species, monAbility);
if (gMoveResultFlags & flags)
2017-10-10 18:01:45 +02:00
{
2018-06-17 16:48:58 +02:00
battlerIn1 = gLastHitBy[gActiveBattler];
2017-10-10 18:01:45 +02:00
for (j = 0; j < MAX_MON_MOVES; j++)
2017-10-10 18:01:45 +02:00
{
move = GetMonData(&party[i], MON_DATA_MOVE1 + j);
if (move == 0)
continue;
2018-07-15 12:39:07 +02:00
if (AI_GetTypeEffectiveness(move, gActiveBattler, battlerIn1) >= UQ_4_12(2.0) && Random() % moduloPercent == 0)
2017-10-10 18:01:45 +02:00
{
2018-02-05 19:46:59 -06:00
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = i;
BtlController_EmitTwoReturnValues(BUFFER_B, B_ACTION_SWITCH, 0);
2017-10-10 18:01:45 +02:00
return TRUE;
}
}
}
}
return FALSE;
}
2020-12-19 21:58:23 -07:00
bool32 ShouldSwitch(void)
2017-10-10 18:01:45 +02:00
{
2018-06-17 16:48:58 +02:00
u8 battlerIn1, battlerIn2;
2017-10-10 18:01:45 +02:00
s32 firstId;
s32 lastId; // + 1
struct Pokemon *party;
s32 i;
s32 availableToSwitch;
2023-02-08 12:42:38 +01:00
bool32 hasAceMon = FALSE;
2017-10-10 18:01:45 +02:00
if (gBattleMons[gActiveBattler].status2 & (STATUS2_WRAPPED | STATUS2_ESCAPE_PREVENTION))
2017-10-10 18:01:45 +02:00
return FALSE;
2018-02-05 19:46:59 -06:00
if (gStatuses3[gActiveBattler] & STATUS3_ROOTED)
2017-10-10 18:01:45 +02:00
return FALSE;
if (IsAbilityPreventingEscape(gActiveBattler))
2017-10-10 18:01:45 +02:00
return FALSE;
if (gBattleTypeFlags & BATTLE_TYPE_ARENA)
return FALSE;
availableToSwitch = 0;
2017-10-10 18:01:45 +02:00
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
{
battlerIn1 = gActiveBattler;
if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler)))])
battlerIn2 = gActiveBattler;
2017-10-10 18:01:45 +02:00
else
battlerIn2 = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler)));
2017-10-10 18:01:45 +02:00
}
else
{
battlerIn1 = gActiveBattler;
battlerIn2 = gActiveBattler;
2017-10-10 18:01:45 +02:00
}
2019-01-27 20:54:34 +01:00
GetAIPartyIndexes(gActiveBattler, &firstId, &lastId);
2017-10-10 18:01:45 +02:00
2018-02-05 19:46:59 -06:00
if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
2017-10-10 18:01:45 +02:00
party = gPlayerParty;
else
party = gEnemyParty;
for (i = firstId; i < lastId; i++)
{
2023-07-26 09:34:23 +02:00
if (!IsValidForBattle(&party[i]))
2017-10-10 18:01:45 +02:00
continue;
2018-06-17 16:48:58 +02:00
if (i == gBattlerPartyIndexes[battlerIn1])
2017-10-10 18:01:45 +02:00
continue;
2018-06-17 16:48:58 +02:00
if (i == gBattlerPartyIndexes[battlerIn2])
2017-10-10 18:01:45 +02:00
continue;
2018-06-17 16:48:58 +02:00
if (i == *(gBattleStruct->monToSwitchIntoId + battlerIn1))
2017-10-10 18:01:45 +02:00
continue;
2018-06-17 16:48:58 +02:00
if (i == *(gBattleStruct->monToSwitchIntoId + battlerIn2))
2017-10-10 18:01:45 +02:00
continue;
2023-02-16 15:57:15 +01:00
if (IsAceMon(gActiveBattler, i))
2023-02-08 12:42:38 +01:00
{
hasAceMon = TRUE;
2022-09-18 00:50:21 -03:00
continue;
2023-02-08 12:42:38 +01:00
}
2017-10-10 18:01:45 +02:00
availableToSwitch++;
}
if (availableToSwitch == 0)
2023-02-08 12:42:38 +01:00
{
if (hasAceMon) // If the ace mon is the only available mon, use it
availableToSwitch++;
else
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.
//These Functions can prompt switch to specific party members
2017-10-10 18:01:45 +02:00
if (ShouldSwitchIfWonderGuard())
return TRUE;
if (ShouldSwitchIfGameStatePrompt())
2017-10-10 18:01:45 +02:00
return TRUE;
2017-10-10 21:45:07 +02:00
if (FindMonThatAbsorbsOpponentsMove())
2017-10-10 18:01:45 +02:00
return TRUE;
//These Functions can prompt switch to generic pary members
if (ShouldSwitchIfAllBadMoves())
2017-10-10 18:01:45 +02:00
return TRUE;
if (ShouldSwitchIfAbilityBenefit())
2017-10-10 18:01:45 +02:00
return TRUE;
2023-02-08 12:42:38 +01:00
//Removing switch capabilites under specific conditions
//These Functions prevent the "FindMonWithFlagsAndSuperEffective" from getting out of hand.
2017-10-10 18:01:45 +02:00
if (HasSuperEffectiveMoveAgainstOpponents(FALSE))
return FALSE;
if (AreStatsRaised())
return FALSE;
2023-02-08 12:42:38 +01:00
//Default Function
//Can prompt switch if AI has a pokemon in party that resists current opponent & has super effective move
2018-01-16 15:12:38 -06:00
if (FindMonWithFlagsAndSuperEffective(MOVE_RESULT_DOESNT_AFFECT_FOE, 2)
|| FindMonWithFlagsAndSuperEffective(MOVE_RESULT_NOT_VERY_EFFECTIVE, 3))
2017-10-10 18:01:45 +02:00
return TRUE;
return FALSE;
}
2017-10-10 21:45:07 +02:00
void AI_TrySwitchOrUseItem(void)
{
struct Pokemon *party;
2018-06-17 16:48:58 +02:00
u8 battlerIn1, battlerIn2;
2017-10-10 21:45:07 +02:00
s32 firstId;
s32 lastId; // + 1
2018-06-17 16:48:58 +02:00
u8 battlerIdentity = GetBattlerPosition(gActiveBattler);
2017-10-10 21:45:07 +02:00
2018-02-05 19:46:59 -06:00
if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
2017-10-10 21:45:07 +02:00
party = gPlayerParty;
else
party = gEnemyParty;
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER)
{
if (ShouldSwitch())
{
2018-06-17 16:48:58 +02:00
if (*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) == PARTY_SIZE)
2017-10-10 21:45:07 +02:00
{
s32 monToSwitchId = GetMostSuitableMonToSwitchInto();
2018-06-17 16:48:58 +02:00
if (monToSwitchId == PARTY_SIZE)
2017-10-10 21:45:07 +02:00
{
if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
{
2018-06-17 16:48:58 +02:00
battlerIn1 = GetBattlerAtPosition(battlerIdentity);
battlerIn2 = battlerIn1;
2017-10-10 21:45:07 +02:00
}
else
{
2018-06-17 16:48:58 +02:00
battlerIn1 = GetBattlerAtPosition(battlerIdentity);
2022-08-27 01:26:13 -04:00
battlerIn2 = GetBattlerAtPosition(BATTLE_PARTNER(battlerIdentity));
2017-10-10 21:45:07 +02:00
}
2019-01-27 20:54:34 +01:00
GetAIPartyIndexes(gActiveBattler, &firstId, &lastId);
2017-10-10 21:45:07 +02:00
for (monToSwitchId = (lastId-1); monToSwitchId >= firstId; monToSwitchId--)
2017-10-10 21:45:07 +02:00
{
2023-07-26 09:34:23 +02:00
if (!IsValidForBattle(&party[monToSwitchId]))
2022-09-15 22:57:29 -06:00
continue;
2018-06-17 16:48:58 +02:00
if (monToSwitchId == gBattlerPartyIndexes[battlerIn1])
2017-10-10 21:45:07 +02:00
continue;
2018-06-17 16:48:58 +02:00
if (monToSwitchId == gBattlerPartyIndexes[battlerIn2])
2017-10-10 21:45:07 +02:00
continue;
2018-06-17 16:48:58 +02:00
if (monToSwitchId == *(gBattleStruct->monToSwitchIntoId + battlerIn1))
2017-10-10 21:45:07 +02:00
continue;
2018-06-17 16:48:58 +02:00
if (monToSwitchId == *(gBattleStruct->monToSwitchIntoId + battlerIn2))
2017-10-10 21:45:07 +02:00
continue;
2023-02-16 15:57:15 +01:00
if (IsAceMon(gActiveBattler, monToSwitchId))
2022-09-18 00:50:21 -03:00
continue;
2017-10-10 21:45:07 +02:00
break;
}
}
2018-02-05 19:46:59 -06:00
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = monToSwitchId;
2017-10-10 21:45:07 +02:00
}
2018-02-05 19:46:59 -06:00
*(gBattleStruct->monToSwitchIntoId + gActiveBattler) = *(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler);
2017-10-10 21:45:07 +02:00
return;
}
else if (ShouldUseItem())
{
return;
}
}
2022-08-27 01:44:39 -04:00
BtlController_EmitTwoReturnValues(BUFFER_B, B_ACTION_USE_MOVE, BATTLE_OPPOSITE(gActiveBattler) << 8);
2017-10-10 21:45:07 +02:00
}
2019-09-01 14:23:11 +02:00
// If there are two(or more) mons to choose from, always choose one that has baton pass
// as most often it can't do much on its own.
2023-07-18 12:01:25 +02:00
static u32 GetBestMonBatonPass(struct Pokemon *party, int firstId, int lastId, u8 invalidMons, int aliveCount, u32 opposingBattler)
2017-10-10 21:45:07 +02:00
{
2019-09-01 14:23:11 +02:00
int i, j, bits = 0;
2017-10-10 21:45:07 +02:00
for (i = firstId; i < lastId; i++)
{
if (invalidMons & gBitTable[i])
continue;
2023-07-18 12:01:25 +02:00
if (IsAiPartyMonOHKOBy(opposingBattler, &party[i]))
2022-09-15 22:57:29 -06:00
continue;
for (j = 0; j < MAX_MON_MOVES; j++)
{
if (GetMonData(&party[i], MON_DATA_MOVE1 + j, NULL) == MOVE_BATON_PASS)
{
bits |= gBitTable[i];
break;
}
}
}
2019-09-01 14:23:11 +02:00
if ((aliveCount == 2 || (aliveCount > 2 && Random() % 3 == 0)) && bits)
{
do
{
2019-09-01 14:23:11 +02:00
i = (Random() % (lastId - firstId)) + firstId;
} while (!(bits & gBitTable[i]));
return i;
}
2017-10-10 21:45:07 +02:00
2019-09-01 14:23:11 +02:00
return PARTY_SIZE;
}
2022-06-18 19:18:01 +03:00
static u32 GetBestMonTypeMatchup(struct Pokemon *party, int firstId, int lastId, u8 invalidMons, u32 opposingBattler)
2019-09-01 14:23:11 +02:00
{
int i, bits = 0;
while (bits != 0x3F) // All mons were checked.
2017-10-10 21:45:07 +02:00
{
uq4_12_t bestResist = UQ_4_12(1.0);
2019-09-01 14:23:11 +02:00
int bestMonId = PARTY_SIZE;
2022-06-18 19:00:33 +03:00
// Find the mon whose type is the most suitable defensively.
2017-10-10 21:45:07 +02:00
for (i = firstId; i < lastId; i++)
{
if (!(gBitTable[i] & invalidMons) && !(gBitTable[i] & bits))
2017-10-10 21:45:07 +02:00
{
u16 species = GetMonData(&party[i], MON_DATA_SPECIES);
uq4_12_t typeEffectiveness = UQ_4_12(1.0);
2018-07-15 12:39:07 +02:00
2022-06-18 19:00:33 +03:00
u8 atkType1 = gBattleMons[opposingBattler].type1;
u8 atkType2 = gBattleMons[opposingBattler].type2;
u8 defType1 = gSpeciesInfo[species].types[0];
u8 defType2 = gSpeciesInfo[species].types[1];
2018-07-15 12:39:07 +02:00
2023-07-18 11:36:09 +02:00
if (IsAiPartyMonOHKOBy(opposingBattler, &party[i]))
continue;
2022-09-15 22:57:29 -06:00
typeEffectiveness = uq4_12_multiply(typeEffectiveness, (GetTypeModifier(atkType1, defType1)));
2018-07-15 12:39:07 +02:00
if (atkType2 != atkType1)
typeEffectiveness = uq4_12_multiply(typeEffectiveness, (GetTypeModifier(atkType2, defType1)));
2018-07-15 12:39:07 +02:00
if (defType2 != defType1)
{
typeEffectiveness = uq4_12_multiply(typeEffectiveness, (GetTypeModifier(atkType1, defType2)));
2018-07-15 12:39:07 +02:00
if (atkType2 != atkType1)
typeEffectiveness = uq4_12_multiply(typeEffectiveness, (GetTypeModifier(atkType2, defType2)));
2018-07-15 12:39:07 +02:00
}
2022-06-18 19:00:33 +03:00
if (typeEffectiveness < bestResist)
2017-10-10 21:45:07 +02:00
{
2022-06-18 19:00:33 +03:00
bestResist = typeEffectiveness;
2017-10-10 21:45:07 +02:00
bestMonId = i;
}
}
}
2018-06-17 16:48:58 +02:00
// Ok, we know the mon has the right typing but does it have at least one super effective move?
if (bestMonId != PARTY_SIZE)
2017-10-10 21:45:07 +02:00
{
for (i = 0; i < MAX_MON_MOVES; i++)
2017-10-10 21:45:07 +02:00
{
2019-09-01 14:23:11 +02:00
u32 move = GetMonData(&party[bestMonId], MON_DATA_MOVE1 + i);
2018-07-15 12:39:07 +02:00
if (move != MOVE_NONE && AI_GetTypeEffectiveness(move, gActiveBattler, opposingBattler) >= UQ_4_12(2.0))
2017-10-10 21:45:07 +02:00
break;
}
if (i != MAX_MON_MOVES)
2018-06-17 16:48:58 +02:00
return bestMonId; // Has both the typing and at least one super effective move.
2017-10-10 21:45:07 +02:00
bits |= gBitTable[bestMonId]; // Sorry buddy, we want something better.
2017-10-10 21:45:07 +02:00
}
else
{
bits = 0x3F; // No viable mon to switch.
2017-10-10 21:45:07 +02:00
}
}
2019-09-01 14:23:11 +02:00
return PARTY_SIZE;
}
2017-10-10 21:45:07 +02:00
2019-09-01 14:23:11 +02:00
static u32 GetBestMonDmg(struct Pokemon *party, int firstId, int lastId, u8 invalidMons, u32 opposingBattler)
{
int i, j;
2023-07-18 11:36:09 +02:00
int dmg, bestDmg = 0;
2019-09-01 14:23:11 +02:00
int bestMonId = PARTY_SIZE;
gMoveResultFlags = 0;
2018-06-17 16:48:58 +02:00
// If we couldn't find the best mon in terms of typing, find the one that deals most damage.
2017-10-10 21:45:07 +02:00
for (i = firstId; i < lastId; i++)
{
if (gBitTable[i] & invalidMons)
2017-10-10 21:45:07 +02:00
continue;
2023-07-18 11:36:09 +02:00
if (IsAiPartyMonOHKOBy(opposingBattler, &party[i]))
2022-09-15 22:57:29 -06:00
continue;
2017-10-10 21:45:07 +02:00
2023-07-18 11:36:09 +02:00
dmg = AI_CalcPartyMonBestMoveDamage(gActiveBattler, opposingBattler, &party[i], NULL);
if (bestDmg < dmg)
2017-10-10 21:45:07 +02:00
{
2023-07-18 11:36:09 +02:00
bestDmg = dmg;
bestMonId = i;
2017-10-10 21:45:07 +02:00
}
}
return bestMonId;
}
2017-10-11 12:49:42 +02:00
2019-09-01 14:23:11 +02:00
u8 GetMostSuitableMonToSwitchInto(void)
{
u32 opposingBattler = 0;
2022-09-15 22:57:29 -06:00
u32 bestMonId = PARTY_SIZE;
2019-09-01 14:23:11 +02:00
u8 battlerIn1 = 0, battlerIn2 = 0;
s32 firstId = 0;
s32 lastId = 0; // + 1
struct Pokemon *party;
s32 i, j, aliveCount = 0;
2023-02-08 12:42:38 +01:00
u32 invalidMons = 0, aceMonId = PARTY_SIZE;
2019-09-01 14:23:11 +02:00
if (*(gBattleStruct->monToSwitchIntoId + gActiveBattler) != PARTY_SIZE)
return *(gBattleStruct->monToSwitchIntoId + gActiveBattler);
if (gBattleTypeFlags & BATTLE_TYPE_ARENA)
return gBattlerPartyIndexes[gActiveBattler] + 1;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
{
battlerIn1 = gActiveBattler;
if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler)))])
2019-09-01 14:23:11 +02:00
battlerIn2 = gActiveBattler;
else
battlerIn2 = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler)));
2019-09-01 14:23:11 +02:00
opposingBattler = BATTLE_OPPOSITE(battlerIn1);
if (gAbsentBattlerFlags & gBitTable[opposingBattler])
opposingBattler ^= BIT_FLANK;
}
else
{
opposingBattler = GetBattlerAtPosition(BATTLE_OPPOSITE(GetBattlerPosition(gActiveBattler)));
2019-09-01 14:23:11 +02:00
battlerIn1 = gActiveBattler;
battlerIn2 = gActiveBattler;
}
GetAIPartyIndexes(gActiveBattler, &firstId, &lastId);
if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
party = gPlayerParty;
else
party = gEnemyParty;
// Get invalid slots ids.
for (i = firstId; i < lastId; i++)
{
2023-07-26 09:34:23 +02:00
if (!IsValidForBattle(&party[i])
2019-09-01 14:23:11 +02:00
|| gBattlerPartyIndexes[battlerIn1] == i
|| gBattlerPartyIndexes[battlerIn2] == i
|| i == *(gBattleStruct->monToSwitchIntoId + battlerIn1)
|| i == *(gBattleStruct->monToSwitchIntoId + battlerIn2)
2023-02-08 12:42:38 +01:00
|| (GetMonAbility(&party[i]) == ABILITY_TRUANT && IsTruantMonVulnerable(gActiveBattler, opposingBattler))) // While not really invalid per say, not really wise to switch into this mon.)
{
invalidMons |= gBitTable[i];
}
2023-02-16 15:57:15 +01:00
else if (IsAceMon(gActiveBattler, i))// Save Ace Pokemon for last.
2023-02-08 12:42:38 +01:00
{
aceMonId = i;
2019-09-01 14:23:11 +02:00
invalidMons |= gBitTable[i];
2023-02-08 12:42:38 +01:00
}
2019-09-01 14:23:11 +02:00
else
2023-02-08 12:42:38 +01:00
{
2019-09-01 14:23:11 +02:00
aliveCount++;
2023-02-08 12:42:38 +01:00
}
2019-09-01 14:23:11 +02:00
}
2023-07-18 12:01:25 +02:00
bestMonId = GetBestMonBatonPass(party, firstId, lastId, invalidMons, aliveCount, opposingBattler);
2019-09-01 14:23:11 +02:00
if (bestMonId != PARTY_SIZE)
return bestMonId;
2022-06-18 19:18:01 +03:00
bestMonId = GetBestMonTypeMatchup(party, firstId, lastId, invalidMons, opposingBattler);
2019-09-01 14:23:11 +02:00
if (bestMonId != PARTY_SIZE)
return bestMonId;
bestMonId = GetBestMonDmg(party, firstId, lastId, invalidMons, opposingBattler);
if (bestMonId != PARTY_SIZE)
return bestMonId;
// If ace mon is the last available Pokemon and switch move was used - switch to the mon.
if (aceMonId != PARTY_SIZE)
2023-02-08 12:42:38 +01:00
return aceMonId;
2019-09-01 14:23:11 +02:00
return PARTY_SIZE;
}
static bool32 AiExpectsToFaintPlayer(void)
{
bool32 canFaintPlayer;
u32 i;
u8 target = gBattleStruct->aiChosenTarget[gActiveBattler];
2022-09-13 16:26:36 -03:00
if (gBattleStruct->aiMoveOrAction[gActiveBattler] > 3)
return FALSE; // AI not planning to use move
2022-09-13 16:26:36 -03:00
if (GetBattlerSide(target) != GetBattlerSide(gActiveBattler)
&& CanIndexMoveFaintTarget(gActiveBattler, target, gBattleStruct->aiMoveOrAction[gActiveBattler], 0)
&& AI_WhoStrikesFirst(gActiveBattler, target, GetAIChosenMove(gActiveBattler)) == AI_IS_FASTER) {
// We expect to faint the target and move first -> dont use an item
return TRUE;
}
2022-09-13 16:26:36 -03:00
return FALSE;
}
2017-10-11 12:49:42 +02:00
static bool8 ShouldUseItem(void)
{
struct Pokemon *party;
s32 i;
u8 validMons = 0;
bool8 shouldUse = FALSE;
// If teaming up with player and Pokemon is on the right, or Pokemon is currently held by Sky Drop
if ((gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && GetBattlerPosition(gActiveBattler) == B_POSITION_PLAYER_RIGHT)
|| gStatuses3[gActiveBattler] & STATUS3_SKY_DROPPED)
2017-10-11 12:49:42 +02:00
return FALSE;
2022-09-13 16:26:36 -03:00
2021-07-25 07:39:02 -06:00
if (gStatuses3[gActiveBattler] & STATUS3_EMBARGO)
return FALSE;
2022-09-13 16:26:36 -03:00
if (AiExpectsToFaintPlayer())
return FALSE;
2017-10-11 12:49:42 +02:00
2018-02-05 19:46:59 -06:00
if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
2017-10-11 12:49:42 +02:00
party = gPlayerParty;
else
party = gEnemyParty;
2018-06-17 16:48:58 +02:00
for (i = 0; i < PARTY_SIZE; i++)
2017-10-11 12:49:42 +02:00
{
2023-07-26 09:34:23 +02:00
if (IsValidForBattle(&party[i]))
2017-10-11 12:49:42 +02:00
{
validMons++;
}
}
2019-09-08 12:21:24 -04:00
for (i = 0; i < MAX_TRAINER_ITEMS; i++)
2017-10-11 12:49:42 +02:00
{
u16 item;
const u8 *itemEffects;
u8 paramOffset;
2018-06-17 16:48:58 +02:00
u8 battlerSide;
2017-10-11 12:49:42 +02:00
item = gBattleResources->battleHistory->trainerItems[i];
if (item == ITEM_NONE)
continue;
itemEffects = GetItemEffect(item);
if (itemEffects == NULL)
2017-10-11 12:49:42 +02:00
continue;
switch (ItemId_GetBattleUsage(item))
2017-10-11 12:49:42 +02:00
{
case EFFECT_ITEM_HEAL_AND_CURE_STATUS:
shouldUse = AI_ShouldHeal(0);
2017-10-11 12:49:42 +02:00
break;
case EFFECT_ITEM_RESTORE_HP:
shouldUse = AI_ShouldHeal(itemEffects[GetItemEffectParamOffset(item, 4, 4)]);
2017-10-11 12:49:42 +02:00
break;
case EFFECT_ITEM_CURE_STATUS:
2019-04-14 17:20:26 +02:00
if (itemEffects[3] & ITEM3_SLEEP && gBattleMons[gActiveBattler].status1 & STATUS1_SLEEP)
2017-10-11 12:49:42 +02:00
shouldUse = TRUE;
if (itemEffects[3] & ITEM3_POISON && (gBattleMons[gActiveBattler].status1 & STATUS1_POISON
|| gBattleMons[gActiveBattler].status1 & STATUS1_TOXIC_POISON))
2017-10-11 12:49:42 +02:00
shouldUse = TRUE;
2019-04-14 17:20:26 +02:00
if (itemEffects[3] & ITEM3_BURN && gBattleMons[gActiveBattler].status1 & STATUS1_BURN)
2017-10-11 12:49:42 +02:00
shouldUse = TRUE;
2023-04-22 19:18:10 +02:00
if (itemEffects[3] & ITEM3_FREEZE && (gBattleMons[gActiveBattler].status1 & STATUS1_FREEZE || gBattleMons[gActiveBattler].status1 & STATUS1_FROSTBITE))
2017-10-11 12:49:42 +02:00
shouldUse = TRUE;
2019-04-14 17:20:26 +02:00
if (itemEffects[3] & ITEM3_PARALYSIS && gBattleMons[gActiveBattler].status1 & STATUS1_PARALYSIS)
2017-10-11 12:49:42 +02:00
shouldUse = TRUE;
2019-04-14 17:20:26 +02:00
if (itemEffects[3] & ITEM3_CONFUSION && gBattleMons[gActiveBattler].status2 & STATUS2_CONFUSION)
2017-10-11 12:49:42 +02:00
shouldUse = TRUE;
break;
case EFFECT_ITEM_INCREASE_STAT:
case EFFECT_ITEM_INCREASE_ALL_STATS:
if (!gDisableStructs[gActiveBattler].isFirstTurn
|| AI_OpponentCanFaintAiWithMod(0))
2017-10-11 12:49:42 +02:00
break;
shouldUse = TRUE;
break;
case EFFECT_ITEM_SET_FOCUS_ENERGY:
if (!gDisableStructs[gActiveBattler].isFirstTurn
|| gBattleMons[gActiveBattler].status2 & STATUS2_FOCUS_ENERGY
|| AI_OpponentCanFaintAiWithMod(0))
break;
shouldUse = TRUE;
break;
case EFFECT_ITEM_SET_MIST:
2018-06-17 16:48:58 +02:00
battlerSide = GetBattlerSide(gActiveBattler);
if (gDisableStructs[gActiveBattler].isFirstTurn && gSideTimers[battlerSide].mistTimer == 0)
shouldUse = TRUE;
break;
case EFFECT_ITEM_REVIVE:
gBattleStruct->itemPartyIndex[gActiveBattler] = GetFirstFaintedPartyIndex(gActiveBattler);
if (gBattleStruct->itemPartyIndex[gActiveBattler] != PARTY_SIZE) // Revive if possible.
2017-10-11 12:49:42 +02:00
shouldUse = TRUE;
break;
default:
2017-10-11 12:49:42 +02:00
return FALSE;
}
if (shouldUse)
{
// Set selected party ID to current battler if none chosen.
if (gBattleStruct->itemPartyIndex[gActiveBattler] == PARTY_SIZE)
gBattleStruct->itemPartyIndex[gActiveBattler] = gBattlerPartyIndexes[gActiveBattler];
BtlController_EmitTwoReturnValues(BUFFER_B, B_ACTION_USE_ITEM, 0);
gBattleStruct->chosenItem[gActiveBattler] = item;
2017-10-11 12:49:42 +02:00
gBattleResources->battleHistory->trainerItems[i] = 0;
return shouldUse;
}
}
return FALSE;
}
static bool32 AI_ShouldHeal(u32 healAmount)
{
bool32 shouldHeal = FALSE;
2022-09-13 16:26:36 -03:00
if (gBattleMons[gActiveBattler].hp < gBattleMons[gActiveBattler].maxHP / 4
|| gBattleMons[gActiveBattler].hp == 0
|| (healAmount != 0 && gBattleMons[gActiveBattler].maxHP - gBattleMons[gActiveBattler].hp > healAmount)) {
// We have low enough HP to consider healing
shouldHeal = !AI_OpponentCanFaintAiWithMod(healAmount); // if target can kill us even after we heal, why bother
}
2022-09-13 16:26:36 -03:00
return shouldHeal;
}
static bool32 AI_OpponentCanFaintAiWithMod(u32 healAmount)
{
u32 i;
// Check special cases to NOT heal
for (i = 0; i < gBattlersCount; i++) {
if (GetBattlerSide(i) == B_SIDE_PLAYER) {
if (CanTargetFaintAiWithMod(i, gActiveBattler, healAmount, 0)) {
// Target is expected to faint us
return TRUE;
}
}
}
return FALSE;
}
2022-09-15 22:57:29 -06:00
2023-07-18 11:36:09 +02:00
static bool32 IsAiPartyMonOHKOBy(u32 battlerAtk, struct Pokemon *aiMon)
2022-09-15 22:57:29 -06:00
{
2023-07-18 12:01:25 +02:00
bool32 ret = FALSE;
struct BattlePokemon *savedBattleMons;
2023-07-18 11:36:09 +02:00
s32 hp = GetMonData(aiMon, MON_DATA_HP);
s32 bestDmg = AI_CalcPartyMonBestMoveDamage(battlerAtk, gActiveBattler, NULL, aiMon);
2022-09-15 22:57:29 -06:00
2023-07-18 11:36:09 +02:00
switch (GetNoOfHitsToKO(bestDmg, hp))
{
case 1:
2023-07-18 12:01:25 +02:00
ret = TRUE;
break;
case 2: // if AI mon is faster allow 2 turns
savedBattleMons = AllocSaveBattleMons();
PokemonToBattleMon(aiMon, &gBattleMons[gActiveBattler]);
if (AI_WhoStrikesFirst(gActiveBattler, battlerAtk, 0) == AI_IS_SLOWER)
ret = TRUE;
else
ret = FALSE;
FreeRestoreBattleMons(savedBattleMons);
break;
2023-07-18 11:36:09 +02:00
}
2022-09-15 22:57:29 -06:00
2023-07-18 12:01:25 +02:00
return ret;
2023-07-18 11:36:09 +02:00
}