2017-10-10 18:01:45 +02:00
# include "global.h"
# include "battle.h"
2022-08-13 00:39:44 -06:00
# 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"
2022-08-11 22:48:36 -06:00
# include "battle_util.h"
2019-03-31 12:15:39 -05:00
# include "battle_anim.h"
2017-10-10 18:01:45 +02:00
# include "battle_controllers.h"
2022-06-17 21:52:58 -04:00
# include "battle_main.h"
2022-08-11 22:48:36 -06:00
# 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"
2023-04-14 11:25:50 -07:00
# include "item.h"
# include "party_menu.h"
2017-10-10 18:01:45 +02:00
# include "pokemon.h"
2017-12-05 12:27:33 -06:00
# 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"
2022-08-13 00:39:44 -06:00
# 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
2023-08-29 15:21:31 +02:00
static bool8 HasSuperEffectiveMoveAgainstOpponents ( u32 battler , bool8 noRng ) ;
static bool8 FindMonWithFlagsAndSuperEffective ( u32 battler , u16 flags , u8 moduloPercent ) ;
static bool8 ShouldUseItem ( u32 battler ) ;
static bool32 AiExpectsToFaintPlayer ( u32 battler ) ;
static bool32 AI_ShouldHeal ( u32 battler , u32 healAmount ) ;
static bool32 AI_OpponentCanFaintAiWithMod ( u32 battler , u32 healAmount ) ;
static bool32 IsAiPartyMonOHKOBy ( u32 battlerAi , u32 battlerAtk , struct Pokemon * aiMon ) ;
static bool32 IsAceMon ( u32 battler , u32 monPartyId )
2023-02-08 12:42:38 +01:00
{
if ( AI_THINKING_STRUCT - > aiFlags & AI_FLAG_ACE_POKEMON
2023-08-29 15:21:31 +02:00
& & ! ( gBattleStruct - > forcedSwitch & gBitTable [ battler ] )
2023-02-08 12:42:38 +01:00
& & monPartyId = = CalculateEnemyPartyCount ( ) - 1 )
return TRUE ;
return FALSE ;
}
2023-08-29 15:21:31 +02:00
void GetAIPartyIndexes ( u32 battler , s32 * firstId , s32 * lastId )
2019-01-27 20:54:34 +01:00
{
2023-08-29 15:21:31 +02:00
if ( BATTLE_TWO_VS_ONE_OPPONENT & & ( battler & BIT_SIDE ) = = B_SIDE_OPPONENT )
2019-01-27 20:54:34 +01:00
{
2022-07-15 12:58:46 -04:00
* firstId = 0 , * lastId = PARTY_SIZE ;
2019-01-27 20:54:34 +01:00
}
2021-01-28 22:45:58 -08: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
{
2023-08-29 15:21:31 +02:00
if ( ( battler & BIT_FLANK ) = = B_FLANK_LEFT )
2022-07-15 12:58:46 -04:00
* firstId = 0 , * lastId = PARTY_SIZE / 2 ;
2019-01-27 20:54:34 +01:00
else
2022-07-15 12:58:46 -04:00
* firstId = PARTY_SIZE / 2 , * lastId = PARTY_SIZE ;
2019-01-27 20:54:34 +01:00
}
else
{
2022-07-15 12:58:46 -04:00
* firstId = 0 , * lastId = PARTY_SIZE ;
2019-01-27 20:54:34 +01:00
}
}
2023-08-29 15:21:31 +02:00
static bool8 ShouldSwitchIfAllBadMoves ( u32 battler )
2019-06-11 10:45:12 +02:00
{
2023-08-30 13:23:55 +02:00
if ( AI_DATA - > shouldSwitchMon & gBitTable [ battler ] )
2019-06-11 10:45:12 +02:00
{
2023-08-30 13:23:55 +02:00
AI_DATA - > shouldSwitchMon & = ~ ( gBitTable [ battler ] ) ;
gBattleStruct - > AI_monToSwitchIntoId [ battler ] = AI_DATA - > monToSwitchId [ battler ] ;
2023-08-29 15:21:31 +02:00
BtlController_EmitTwoReturnValues ( battler , BUFFER_B , B_ACTION_SWITCH , 0 ) ;
2019-06-11 10:45:12 +02:00
return TRUE ;
}
else
{
return FALSE ;
}
}
2023-08-29 15:21:31 +02:00
static bool8 ShouldSwitchIfWonderGuard ( u32 battler )
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 ;
2023-08-29 15:21:31 +02:00
opposingPosition = BATTLE_OPPOSITE ( GetBattlerPosition ( battler ) ) ;
2017-10-10 18:01:45 +02:00
2021-10-31 17:19:30 -04: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.
2018-12-25 12:50:15 -05:00
for ( opposingBattler = GetBattlerAtPosition ( opposingPosition ) , i = 0 ; i < MAX_MON_MOVES ; i + + )
2017-10-10 18:01:45 +02:00
{
2023-08-29 15:21:31 +02:00
move = gBattleMons [ battler ] . moves [ i ] ;
2018-07-15 12:39:07 +02:00
if ( move ! = MOVE_NONE )
{
2023-08-29 15:21:31 +02:00
if ( AI_GetTypeEffectiveness ( move , battler , opposingBattler ) > = UQ_4_12 ( 2.0 ) )
2018-07-15 12:39:07 +02:00
return FALSE ;
}
2017-10-10 18:01:45 +02:00
}
2018-06-17 16:48:58 +02:00
// Get party information.
2023-08-29 15:21:31 +02:00
GetAIPartyIndexes ( battler , & firstId , & lastId ) ;
2017-10-10 18:01:45 +02:00
2023-08-29 15:21:31 +02:00
if ( GetBattlerSide ( battler ) = = 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 ;
2023-08-29 15:21:31 +02:00
if ( i = = gBattlerPartyIndexes [ battler ] )
2017-10-10 18:01:45 +02:00
continue ;
2023-08-29 15:21:31 +02:00
if ( IsAceMon ( battler , i ) )
2022-09-18 00:50:21 -03:00
continue ;
2017-10-10 18:01:45 +02:00
2018-12-25 12:50:15 -05: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
{
2023-08-29 15:21:31 +02:00
if ( AI_GetTypeEffectiveness ( move , battler , opposingBattler ) > = UQ_4_12 ( 2.0 ) & & Random ( ) % 3 < 2 )
2018-07-15 12:39:07 +02:00
{
// We found a mon.
2023-08-29 15:21:31 +02:00
* ( gBattleStruct - > AI_monToSwitchIntoId + battler ) = i ;
BtlController_EmitTwoReturnValues ( battler , 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
}
2023-08-29 15:21:31 +02:00
static bool8 FindMonThatAbsorbsOpponentsMove ( u32 battler )
2017-10-10 18:01:45 +02:00
{
2018-06-17 16:48:58 +02:00
u8 battlerIn1 , battlerIn2 ;
2023-08-29 15:21:31 +02:00
u8 numAbsorbingAbilities = 0 ;
2023-08-28 07:39:27 -04:00
u16 absorbingTypeAbilities [ 3 ] ; // Array size is maximum number of absorbing abilities for a single type
2017-10-10 18:01:45 +02:00
s32 firstId ;
s32 lastId ; // + 1
struct Pokemon * party ;
2023-08-28 07:39:27 -04:00
s32 i , j ;
2017-10-10 18:01:45 +02:00
2023-08-29 15:21:31 +02:00
if ( HasSuperEffectiveMoveAgainstOpponents ( battler , TRUE ) & & Random ( ) % 3 ! = 0 )
2017-10-10 18:01:45 +02:00
return FALSE ;
2023-08-29 15:21:31 +02:00
if ( gLastLandedMoves [ battler ] = = MOVE_NONE )
2017-10-10 18:01:45 +02:00
return FALSE ;
2023-08-29 15:21:31 +02:00
if ( gLastLandedMoves [ battler ] = = MOVE_UNAVAILABLE )
2017-10-10 18:01:45 +02:00
return FALSE ;
2023-08-29 15:21:31 +02:00
if ( IS_MOVE_STATUS ( gLastLandedMoves [ battler ] ) )
2017-10-10 18:01:45 +02:00
return FALSE ;
if ( gBattleTypeFlags & BATTLE_TYPE_DOUBLE )
{
2023-08-29 15:21:31 +02:00
battlerIn1 = battler ;
if ( gAbsentBattlerFlags & gBitTable [ GetBattlerAtPosition ( BATTLE_PARTNER ( GetBattlerPosition ( battler ) ) ) ] )
battlerIn2 = battler ;
2017-10-10 18:01:45 +02:00
else
2023-08-29 15:21:31 +02:00
battlerIn2 = GetBattlerAtPosition ( BATTLE_PARTNER ( GetBattlerPosition ( battler ) ) ) ;
2017-10-10 18:01:45 +02:00
}
else
{
2023-08-29 15:21:31 +02:00
battlerIn1 = battler ;
battlerIn2 = battler ;
2017-10-10 18:01:45 +02:00
}
2023-08-28 07:39:27 -04:00
// Create an array of possible absorb abilities so the AI considers all of them
2023-08-29 15:21:31 +02:00
if ( gBattleMoves [ gLastLandedMoves [ battler ] ] . type = = TYPE_FIRE )
2023-08-28 07:39:27 -04:00
{
absorbingTypeAbilities [ 0 ] = ABILITY_FLASH_FIRE ;
numAbsorbingAbilities = 1 ;
}
2023-08-29 15:21:31 +02:00
else if ( gBattleMoves [ gLastLandedMoves [ battler ] ] . type = = TYPE_WATER )
2023-08-28 07:39:27 -04:00
{
absorbingTypeAbilities [ 0 ] = ABILITY_WATER_ABSORB ;
absorbingTypeAbilities [ 1 ] = ABILITY_STORM_DRAIN ;
absorbingTypeAbilities [ 2 ] = ABILITY_DRY_SKIN ;
numAbsorbingAbilities = 3 ;
}
2023-08-29 15:21:31 +02:00
else if ( gBattleMoves [ gLastLandedMoves [ battler ] ] . type = = TYPE_ELECTRIC )
2023-08-28 07:39:27 -04:00
{
absorbingTypeAbilities [ 0 ] = ABILITY_VOLT_ABSORB ;
absorbingTypeAbilities [ 1 ] = ABILITY_MOTOR_DRIVE ;
absorbingTypeAbilities [ 2 ] = ABILITY_LIGHTNING_ROD ;
numAbsorbingAbilities = 3 ;
}
2023-08-29 15:21:31 +02:00
else if ( gBattleMoves [ gLastLandedMoves [ battler ] ] . type = = TYPE_GRASS )
2023-08-28 07:39:27 -04:00
{
absorbingTypeAbilities [ 0 ] = ABILITY_SAP_SIPPER ;
numAbsorbingAbilities = 1 ;
}
2017-10-10 18:01:45 +02:00
else
2023-08-28 07:39:27 -04:00
{
2017-10-10 18:01:45 +02:00
return FALSE ;
2023-08-28 07:39:27 -04:00
}
2017-10-10 18:01:45 +02:00
2023-08-28 07:39:27 -04:00
// Check current mon for all absorbing abilities
for ( i = 0 ; i < numAbsorbingAbilities ; i + + )
{
2023-08-29 15:21:31 +02:00
if ( AI_DATA - > abilities [ battler ] = = absorbingTypeAbilities [ i ] )
2023-08-28 07:39:27 -04:00
return FALSE ;
}
2017-10-10 18:01:45 +02:00
2023-08-29 15:21:31 +02:00
GetAIPartyIndexes ( battler , & firstId , & lastId ) ;
2017-10-10 18:01:45 +02:00
2023-08-29 15:21:31 +02:00
if ( GetBattlerSide ( battler ) = = B_SIDE_PLAYER )
2017-10-10 18:01:45 +02:00
party = gPlayerParty ;
else
party = gEnemyParty ;
for ( i = firstId ; i < lastId ; i + + )
{
2020-05-18 14:54:12 -07:00
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-08-29 15:21:31 +02:00
if ( IsAceMon ( battler , i ) )
2022-09-18 00:50:21 -03:00
continue ;
2023-02-08 12:42:38 +01:00
2023-04-14 09:42:48 -04:00
monAbility = GetMonAbility ( & party [ i ] ) ;
2023-08-28 07:39:27 -04:00
for ( j = 0 ; j < numAbsorbingAbilities ; j + + )
2017-10-10 18:01:45 +02:00
{
2023-08-28 07:39:27 -04:00
if ( absorbingTypeAbilities [ j ] = = monAbility & & Random ( ) & 1 )
{
// we found a mon.
2023-08-29 15:21:31 +02:00
* ( gBattleStruct - > AI_monToSwitchIntoId + battler ) = i ;
BtlController_EmitTwoReturnValues ( battler , 1 , B_ACTION_SWITCH , 0 ) ;
2023-08-28 07:39:27 -04:00
return TRUE ;
}
2017-10-10 18:01:45 +02:00
}
}
return FALSE ;
}
2023-08-29 15:21:31 +02:00
static bool8 ShouldSwitchIfGameStatePrompt ( u32 battler )
2017-10-10 18:01:45 +02:00
{
2022-08-11 22:48:36 -06:00
bool8 switchMon = FALSE ;
2023-08-29 15:21:31 +02:00
u16 monAbility = AI_DATA - > abilities [ battler ] ;
u16 holdEffect = AI_DATA - > holdEffects [ battler ] ;
u8 opposingPosition = BATTLE_OPPOSITE ( GetBattlerPosition ( battler ) ) ;
2022-08-11 22:48:36 -06:00
u8 opposingBattler = GetBattlerAtPosition ( opposingPosition ) ;
s32 moduloChance = 4 ; //25% Chance Default
s32 chanceReducer = 1 ; //No Reduce default. Increase to reduce
2022-08-13 00:39:44 -06:00
s32 firstId ;
s32 lastId ;
s32 i ;
struct Pokemon * party ;
2022-08-11 22:48:36 -06:00
2023-08-29 15:21:31 +02:00
if ( AnyStatIsRaised ( battler ) )
2022-08-11 22:48:36 -06:00
chanceReducer = 5 ; // Reduce switchout probability by factor of 5 if setup
2017-10-10 18:01:45 +02:00
2022-08-11 22:48:36 -06:00
//Perish Song
2023-08-29 15:21:31 +02:00
if ( gStatuses3 [ battler ] & STATUS3_PERISH_SONG
& & gDisableStructs [ battler ] . perishSongTimer = = 0
2022-08-11 22:48:36 -06:00
& & monAbility ! = ABILITY_SOUNDPROOF )
switchMon = TRUE ;
2017-10-10 18:01:45 +02:00
2022-08-11 22:48:36 -06:00
if ( AI_THINKING_STRUCT - > aiFlags & AI_FLAG_SMART_SWITCHING )
2017-10-10 18:01:45 +02:00
{
2022-08-11 22:48:36 -06:00
//Yawn
2023-08-29 15:21:31 +02:00
if ( gStatuses3 [ battler ] & STATUS3_YAWN
& & AI_CanSleep ( battler , monAbility )
& & gBattleMons [ battler ] . hp > gBattleMons [ battler ] . maxHP / 3 )
2022-08-11 22:48:36 -06:00
{
switchMon = TRUE ;
2022-08-13 00:39:44 -06:00
//Double Battles
//Check if partner can prevent sleep
if ( IsDoubleBattle ( ) )
{
2023-08-29 15:21:31 +02:00
if ( IsBattlerAlive ( BATTLE_PARTNER ( battler ) )
& & ( GetAIChosenMove ( BATTLE_PARTNER ( battler ) ) = = MOVE_UPROAR )
2022-08-13 00:39:44 -06:00
)
switchMon = FALSE ;
2023-08-29 15:21:31 +02:00
if ( IsBattlerAlive ( BATTLE_PARTNER ( battler ) )
2022-08-13 00:39:44 -06:00
& & ( gBattleMoves [ AI_DATA - > partnerMove ] . effect = = EFFECT_MISTY_TERRAIN
| | gBattleMoves [ AI_DATA - > partnerMove ] . effect = = EFFECT_ELECTRIC_TERRAIN )
2023-08-29 15:21:31 +02:00
& & IsBattlerGrounded ( battler )
2022-08-13 00:39:44 -06:00
)
switchMon = FALSE ;
2023-08-29 15:21:31 +02:00
if ( * ( gBattleStruct - > AI_monToSwitchIntoId + BATTLE_PARTNER ( battler ) ) ! = PARTY_SIZE ) //Partner is switching
2022-08-13 00:39:44 -06:00
{
2023-08-29 15:21:31 +02:00
GetAIPartyIndexes ( battler , & firstId , & lastId ) ;
2023-02-08 12:42:38 +01:00
2023-08-29 15:21:31 +02:00
if ( GetBattlerSide ( battler ) = = B_SIDE_PLAYER )
2022-08-13 00:39:44 -06:00
party = gPlayerParty ;
2023-02-08 12:42:38 +01:00
2022-08-13 00:39:44 -06:00
for ( i = firstId ; i < lastId ; i + + )
{
2023-08-29 15:21:31 +02:00
if ( IsAceMon ( battler , i ) )
2023-02-08 12:42:38 +01:00
continue ;
2022-08-17 18:23:12 -06:00
2022-08-13 00:39:44 -06:00
//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 ] )
2023-08-29 15:21:31 +02:00
& & i ! = gBattlerPartyIndexes [ battler ]
& & i ! = gBattlerPartyIndexes [ BATTLE_PARTNER ( battler ) ]
& & IsBattlerGrounded ( battler )
2022-08-23 19:49:54 -06:00
& & ( GetMonAbility ( & party [ i ] ) = = ABILITY_MISTY_SURGE
| | GetMonAbility ( & party [ i ] ) = = ABILITY_ELECTRIC_SURGE ) ) //Ally has Misty or Electric Surge
2022-08-13 00:39:44 -06:00
{
2023-08-29 15:21:31 +02:00
* ( gBattleStruct - > AI_monToSwitchIntoId + BATTLE_PARTNER ( battler ) ) = i ;
BtlController_EmitTwoReturnValues ( battler , BUFFER_B , B_ACTION_SWITCH , 0 ) ;
2022-08-13 00:39:44 -06:00
switchMon = FALSE ;
break ;
}
}
}
}
2023-02-08 12:42:38 +01:00
2022-08-11 22:48:36 -06:00
//Check if Active Pokemon can KO opponent instead of switching
//Will still fall asleep, but take out opposing Pokemon first
2023-08-29 15:21:31 +02:00
if ( AiExpectsToFaintPlayer ( battler ) )
2022-08-11 22:48:36 -06:00
switchMon = FALSE ;
//Checks to see if active Pokemon can do something against sleep
2022-09-18 22:47:30 -04:00
if ( ( monAbility = = ABILITY_NATURAL_CURE
| | monAbility = = ABILITY_SHED_SKIN
| | monAbility = = ABILITY_EARLY_BIRD )
2022-08-11 22:48:36 -06:00
| | holdEffect = = ( HOLD_EFFECT_CURE_SLP | HOLD_EFFECT_CURE_STATUS )
2023-08-29 15:21:31 +02:00
| | HasMove ( battler , MOVE_SLEEP_TALK )
| | ( HasMoveEffect ( battler , MOVE_SNORE ) & & AI_GetTypeEffectiveness ( MOVE_SNORE , battler , opposingBattler ) > = UQ_4_12 ( 1.0 ) )
| | ( IsBattlerGrounded ( battler )
& & ( HasMove ( battler , MOVE_MISTY_TERRAIN ) | | HasMove ( battler , MOVE_ELECTRIC_TERRAIN ) ) )
2022-08-11 22:48:36 -06:00
)
switchMon = FALSE ;
//Check if Active Pokemon evasion boosted and might be able to dodge until awake
2023-08-29 15:21:31 +02:00
if ( gBattleMons [ battler ] . 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
2023-08-29 15:21:31 +02:00
& & ! ( gBattleMons [ battler ] . status2 & STATUS2_FORESIGHT )
& & ! ( gStatuses3 [ battler ] & STATUS3_MIRACLE_EYED ) )
2022-08-11 22:48:36 -06:00
switchMon = FALSE ;
}
//Secondary Damage
if ( monAbility ! = ABILITY_MAGIC_GUARD
2023-08-29 15:21:31 +02:00
& & ! AiExpectsToFaintPlayer ( battler ) )
2022-08-11 22:48:36 -06:00
{
//Toxic
moduloChance = 2 ; //50%
2023-08-29 15:21:31 +02:00
if ( ( ( gBattleMons [ battler ] . status1 & STATUS1_TOXIC_COUNTER ) > = STATUS1_TOXIC_TURN ( 2 ) )
& & gBattleMons [ battler ] . hp > = ( gBattleMons [ battler ] . maxHP / 3 )
2022-08-11 22:48:36 -06:00
& & ( Random ( ) % ( moduloChance * chanceReducer ) ) = = 0 )
switchMon = TRUE ;
2023-02-08 12:42:38 +01:00
2022-08-11 22:48:36 -06:00
//Cursed
moduloChance = 2 ; //50%
2023-08-29 15:21:31 +02:00
if ( gBattleMons [ battler ] . status2 & STATUS2_CURSED
2022-08-11 22:48:36 -06:00
& & ( Random ( ) % ( moduloChance * chanceReducer ) ) = = 0 )
switchMon = TRUE ;
//Nightmare
moduloChance = 3 ; //33.3%
2023-08-29 15:21:31 +02:00
if ( gBattleMons [ battler ] . status2 & STATUS2_NIGHTMARE
2022-08-11 22:48:36 -06:00
& & ( Random ( ) % ( moduloChance * chanceReducer ) ) = = 0 )
switchMon = TRUE ;
//Leech Seed
moduloChance = 4 ; //25%
2023-08-29 15:21:31 +02:00
if ( gStatuses3 [ battler ] & STATUS3_LEECHSEED
2022-08-11 22:48:36 -06:00
& & ( Random ( ) % ( moduloChance * chanceReducer ) ) = = 0 )
switchMon = TRUE ;
}
2017-10-10 18:01:45 +02:00
2022-08-11 22:48:36 -06:00
//Infatuation
2023-08-29 15:21:31 +02:00
if ( gBattleMons [ battler ] . status2 & STATUS2_INFATUATION
& & ! AiExpectsToFaintPlayer ( battler ) )
2022-08-11 22:48:36 -06:00
switchMon = TRUE ;
//Todo
//Pass Wish Heal
2023-02-08 12:42:38 +01:00
2022-08-11 22:48:36 -06:00
//Semi-Invulnerable
2022-09-15 22:10:39 -04:00
if ( gStatuses3 [ opposingBattler ] & STATUS3_SEMI_INVULNERABLE )
{
2023-08-29 15:21:31 +02:00
if ( FindMonThatAbsorbsOpponentsMove ( battler ) ) //If find absorber default to switch
2022-08-11 22:48:36 -06:00
switchMon = TRUE ;
2023-08-29 15:21:31 +02:00
if ( ! AI_OpponentCanFaintAiWithMod ( battler , 0 )
& & AnyStatIsRaised ( battler ) )
2022-08-11 22:48:36 -06:00
switchMon = FALSE ;
2023-08-29 15:21:31 +02:00
if ( AiExpectsToFaintPlayer ( battler )
2023-09-13 13:23:19 +02:00
& & ! AI_STRIKES_FIRST ( battler , opposingBattler , 0 )
2023-08-29 15:21:31 +02:00
& & ! AI_OpponentCanFaintAiWithMod ( battler , 0 ) )
2022-08-11 22:48:36 -06:00
switchMon = FALSE ;
2022-09-15 18:34:15 -04:00
}
2017-10-10 18:01:45 +02:00
}
2022-08-11 22:48:36 -06:00
if ( switchMon )
2017-10-10 18:01:45 +02:00
{
2023-08-29 15:21:31 +02:00
* ( gBattleStruct - > AI_monToSwitchIntoId + battler ) = PARTY_SIZE ;
BtlController_EmitTwoReturnValues ( battler , BUFFER_B , B_ACTION_SWITCH , 0 ) ;
2017-10-10 18:01:45 +02:00
return TRUE ;
}
2022-08-11 22:48:36 -06:00
else
2017-10-10 18:01:45 +02:00
{
2022-08-11 22:48:36 -06:00
return FALSE ;
2017-10-10 18:01:45 +02:00
}
2022-08-11 22:48:36 -06:00
}
2017-10-10 18:01:45 +02:00
2023-08-29 15:21:31 +02:00
static bool8 ShouldSwitchIfAbilityBenefit ( u32 battler )
2022-08-11 22:48:36 -06:00
{
s32 monToSwitchId ;
s32 moduloChance = 4 ; //25% Chance Default
s32 chanceReducer = 1 ; //No Reduce default. Increase to reduce
2018-06-17 16:48:58 +02:00
2023-08-29 15:21:31 +02:00
if ( AnyStatIsRaised ( battler ) )
2022-08-11 22:48:36 -06:00
chanceReducer = 5 ; // Reduce switchout probability by factor of 5 if setup
//Check if ability is blocked
2023-08-29 15:21:31 +02:00
if ( gStatuses3 [ battler ] & STATUS3_GASTRO_ACID
2022-08-11 22:48:36 -06:00
| | IsNeutralizingGasOnField ( ) )
return FALSE ;
2023-08-29 15:21:31 +02:00
switch ( AI_DATA - > abilities [ battler ] ) {
2022-08-11 22:48:36 -06:00
case ABILITY_NATURAL_CURE :
moduloChance = 4 ; //25%
2023-02-08 12:42:38 +01:00
//Attempt to cure bad ailment
2023-08-29 15:21:31 +02:00
if ( gBattleMons [ battler ] . status1 & ( STATUS1_SLEEP | STATUS1_FREEZE | STATUS1_TOXIC_POISON )
& & GetMostSuitableMonToSwitchInto ( battler ) ! = PARTY_SIZE )
2022-08-11 22:48:36 -06:00
break ;
//Attempt to cure lesser ailment
2023-08-29 15:21:31 +02:00
if ( ( gBattleMons [ battler ] . status1 & STATUS1_ANY )
& & ( gBattleMons [ battler ] . hp > = gBattleMons [ battler ] . maxHP / 2 )
& & GetMostSuitableMonToSwitchInto ( battler ) ! = PARTY_SIZE
2022-08-11 22:48:36 -06:00
& & Random ( ) % ( moduloChance * chanceReducer ) = = 0 )
break ;
return FALSE ;
case ABILITY_REGENERATOR :
moduloChance = 2 ; //50%
//Don't switch if ailment
2023-08-29 15:21:31 +02:00
if ( gBattleMons [ battler ] . status1 & STATUS1_ANY )
2023-02-08 12:42:38 +01:00
return FALSE ;
2023-08-29 15:21:31 +02:00
if ( ( gBattleMons [ battler ] . hp < = ( ( gBattleMons [ battler ] . maxHP * 2 ) / 3 ) )
& & GetMostSuitableMonToSwitchInto ( battler ) ! = PARTY_SIZE
2022-08-11 22:48:36 -06:00
& & Random ( ) % ( moduloChance * chanceReducer ) = = 0 )
break ;
2023-02-08 12:42:38 +01:00
2022-08-11 22:48:36 -06:00
return FALSE ;
default :
2023-02-08 12:42:38 +01:00
return FALSE ;
2017-10-10 18:01:45 +02:00
}
2023-08-29 15:21:31 +02:00
* ( gBattleStruct - > AI_monToSwitchIntoId + battler ) = PARTY_SIZE ;
BtlController_EmitTwoReturnValues ( battler , BUFFER_B , B_ACTION_SWITCH , 0 ) ;
2022-08-11 22:48:36 -06:00
return TRUE ;
2017-10-10 18:01:45 +02:00
}
2023-08-29 15:21:31 +02:00
static bool8 HasSuperEffectiveMoveAgainstOpponents ( u32 battler , 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 ;
2023-08-29 15:21:31 +02:00
opposingPosition = BATTLE_OPPOSITE ( GetBattlerPosition ( battler ) ) ;
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
{
2018-12-25 12:50:15 -05:00
for ( i = 0 ; i < MAX_MON_MOVES ; i + + )
2017-10-10 18:01:45 +02:00
{
2023-08-29 15:21:31 +02:00
move = gBattleMons [ battler ] . moves [ i ] ;
2017-10-10 18:01:45 +02:00
if ( move = = MOVE_NONE )
continue ;
2023-08-29 15:21:31 +02:00
if ( AI_GetTypeEffectiveness ( move , battler , 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
{
2018-12-25 12:50:15 -05:00
for ( i = 0 ; i < MAX_MON_MOVES ; i + + )
2017-10-10 18:01:45 +02:00
{
2023-08-29 15:21:31 +02:00
move = gBattleMons [ battler ] . moves [ i ] ;
2017-10-10 18:01:45 +02:00
if ( move = = MOVE_NONE )
continue ;
2023-08-29 15:21:31 +02:00
if ( AI_GetTypeEffectiveness ( move , battler , 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 ;
}
2023-08-29 15:21:31 +02:00
static bool8 AreStatsRaised ( u32 battler )
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
{
2023-08-29 15:21:31 +02:00
if ( gBattleMons [ battler ] . statStages [ i ] > DEFAULT_STAT_STAGE )
buffedStatsValue + = gBattleMons [ battler ] . statStages [ i ] - DEFAULT_STAT_STAGE ;
2017-10-10 18:01:45 +02:00
}
return ( buffedStatsValue > 3 ) ;
}
2023-08-29 15:21:31 +02:00
static bool8 FindMonWithFlagsAndSuperEffective ( u32 battler , 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 ;
2023-08-29 15:21:31 +02:00
if ( gLastLandedMoves [ battler ] = = MOVE_NONE )
2017-10-10 18:01:45 +02:00
return FALSE ;
2023-08-29 15:21:31 +02:00
if ( gLastLandedMoves [ battler ] = = MOVE_UNAVAILABLE )
2017-10-10 18:01:45 +02:00
return FALSE ;
2023-08-29 15:21:31 +02:00
if ( gLastHitBy [ battler ] = = 0xFF )
2017-10-10 18:01:45 +02:00
return FALSE ;
2023-08-29 15:21:31 +02:00
if ( IS_MOVE_STATUS ( gLastLandedMoves [ battler ] ) )
2017-10-10 18:01:45 +02:00
return FALSE ;
if ( gBattleTypeFlags & BATTLE_TYPE_DOUBLE )
{
2023-08-29 15:21:31 +02:00
battlerIn1 = battler ;
if ( gAbsentBattlerFlags & gBitTable [ GetBattlerAtPosition ( BATTLE_PARTNER ( GetBattlerPosition ( battler ) ) ) ] )
battlerIn2 = battler ;
2017-10-10 18:01:45 +02:00
else
2023-08-29 15:21:31 +02:00
battlerIn2 = GetBattlerAtPosition ( BATTLE_PARTNER ( GetBattlerPosition ( battler ) ) ) ;
2017-10-10 18:01:45 +02:00
}
else
{
2023-08-29 15:21:31 +02:00
battlerIn1 = battler ;
battlerIn2 = battler ;
2017-10-10 18:01:45 +02:00
}
2023-08-29 15:21:31 +02:00
GetAIPartyIndexes ( battler , & firstId , & lastId ) ;
2017-10-10 18:01:45 +02:00
2023-08-29 15:21:31 +02:00
if ( GetBattlerSide ( battler ) = = 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-08-29 15:21:31 +02:00
if ( IsAceMon ( battler , i ) )
2022-09-18 00:50:21 -03:00
continue ;
2022-08-17 18:23:12 -06:00
2023-07-26 09:34:23 +02:00
species = GetMonData ( & party [ i ] , MON_DATA_SPECIES_OR_EGG ) ;
2023-04-14 09:42:48 -04:00
monAbility = GetMonAbility ( & party [ i ] ) ;
2023-08-29 15:21:31 +02:00
CalcPartyMonTypeEffectivenessMultiplier ( gLastLandedMoves [ battler ] , species , monAbility ) ;
2018-07-15 12:39:07 +02:00
if ( gMoveResultFlags & flags )
2017-10-10 18:01:45 +02:00
{
2023-08-29 15:21:31 +02:00
battlerIn1 = gLastHitBy [ battler ] ;
2017-10-10 18:01:45 +02:00
2018-12-25 12:50:15 -05: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 ;
2023-08-29 15:21:31 +02:00
if ( AI_GetTypeEffectiveness ( move , battler , battlerIn1 ) > = UQ_4_12 ( 2.0 ) & & Random ( ) % moduloPercent = = 0 )
2017-10-10 18:01:45 +02:00
{
2023-08-29 15:21:31 +02:00
* ( gBattleStruct - > AI_monToSwitchIntoId + battler ) = i ;
BtlController_EmitTwoReturnValues ( battler , BUFFER_B , B_ACTION_SWITCH , 0 ) ;
2017-10-10 18:01:45 +02:00
return TRUE ;
}
}
}
}
return FALSE ;
}
2023-08-29 15:21:31 +02:00
bool32 ShouldSwitch ( u32 battler )
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
2023-08-29 15:21:31 +02:00
if ( gBattleMons [ battler ] . status2 & ( STATUS2_WRAPPED | STATUS2_ESCAPE_PREVENTION ) )
2017-10-10 18:01:45 +02:00
return FALSE ;
2023-08-29 15:21:31 +02:00
if ( gStatuses3 [ battler ] & STATUS3_ROOTED )
2017-10-10 18:01:45 +02:00
return FALSE ;
2023-08-29 15:21:31 +02:00
if ( IsAbilityPreventingEscape ( battler ) )
2017-10-10 18:01:45 +02:00
return FALSE ;
if ( gBattleTypeFlags & BATTLE_TYPE_ARENA )
return FALSE ;
availableToSwitch = 0 ;
2021-12-03 17:30:45 -08:00
2017-10-10 18:01:45 +02:00
if ( gBattleTypeFlags & BATTLE_TYPE_DOUBLE )
{
2023-08-29 15:21:31 +02:00
battlerIn1 = battler ;
if ( gAbsentBattlerFlags & gBitTable [ GetBattlerAtPosition ( BATTLE_PARTNER ( GetBattlerPosition ( battler ) ) ) ] )
battlerIn2 = battler ;
2017-10-10 18:01:45 +02:00
else
2023-08-29 15:21:31 +02:00
battlerIn2 = GetBattlerAtPosition ( BATTLE_PARTNER ( GetBattlerPosition ( battler ) ) ) ;
2017-10-10 18:01:45 +02:00
}
else
{
2023-08-29 15:21:31 +02:00
battlerIn1 = battler ;
battlerIn2 = battler ;
2017-10-10 18:01:45 +02:00
}
2023-08-29 15:21:31 +02:00
GetAIPartyIndexes ( battler , & firstId , & lastId ) ;
2017-10-10 18:01:45 +02:00
2023-08-29 15:21:31 +02:00
if ( GetBattlerSide ( battler ) = = 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-08-29 15:21:31 +02:00
if ( IsAceMon ( battler , 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 ;
}
2022-08-23 19:49:54 -06:00
//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
2023-08-29 15:21:31 +02:00
if ( ShouldSwitchIfWonderGuard ( battler ) )
2017-10-10 18:01:45 +02:00
return TRUE ;
2023-08-29 15:21:31 +02:00
if ( ShouldSwitchIfGameStatePrompt ( battler ) )
2017-10-10 18:01:45 +02:00
return TRUE ;
2023-08-29 15:21:31 +02:00
if ( FindMonThatAbsorbsOpponentsMove ( battler ) )
2017-10-10 18:01:45 +02:00
return TRUE ;
2022-08-23 19:49:54 -06:00
//These Functions can prompt switch to generic pary members
2023-08-29 15:21:31 +02:00
if ( ShouldSwitchIfAllBadMoves ( battler ) )
2017-10-10 18:01:45 +02:00
return TRUE ;
2023-08-29 15:21:31 +02:00
if ( ShouldSwitchIfAbilityBenefit ( battler ) )
2017-10-10 18:01:45 +02:00
return TRUE ;
2023-02-08 12:42:38 +01:00
2022-08-23 19:49:54 -06:00
//Removing switch capabilites under specific conditions
//These Functions prevent the "FindMonWithFlagsAndSuperEffective" from getting out of hand.
2023-08-29 15:21:31 +02:00
if ( HasSuperEffectiveMoveAgainstOpponents ( battler , FALSE ) )
2017-10-10 18:01:45 +02:00
return FALSE ;
2023-08-29 15:21:31 +02:00
if ( AreStatsRaised ( battler ) )
2017-10-10 18:01:45 +02:00
return FALSE ;
2023-02-08 12:42:38 +01:00
2022-08-23 19:49:54 -06:00
//Default Function
//Can prompt switch if AI has a pokemon in party that resists current opponent & has super effective move
2023-08-29 15:21:31 +02:00
if ( FindMonWithFlagsAndSuperEffective ( battler , MOVE_RESULT_DOESNT_AFFECT_FOE , 2 )
| | FindMonWithFlagsAndSuperEffective ( battler , 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
2023-08-29 15:21:31 +02:00
void AI_TrySwitchOrUseItem ( u32 battler )
2017-10-10 21:45:07 +02:00
{
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
2023-08-29 15:21:31 +02:00
u8 battlerPosition = GetBattlerPosition ( battler ) ;
2017-10-10 21:45:07 +02:00
2023-08-29 15:21:31 +02:00
if ( GetBattlerSide ( battler ) = = B_SIDE_PLAYER )
2017-10-10 21:45:07 +02:00
party = gPlayerParty ;
else
party = gEnemyParty ;
if ( gBattleTypeFlags & BATTLE_TYPE_TRAINER )
{
2023-08-29 15:21:31 +02:00
if ( ShouldSwitch ( battler ) )
2017-10-10 21:45:07 +02:00
{
2023-08-29 15:21:31 +02:00
if ( * ( gBattleStruct - > AI_monToSwitchIntoId + battler ) = = PARTY_SIZE )
2017-10-10 21:45:07 +02:00
{
2023-08-29 15:21:31 +02:00
s32 monToSwitchId = GetMostSuitableMonToSwitchInto ( battler ) ;
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 ) )
{
2023-08-29 15:21:31 +02:00
battlerIn1 = GetBattlerAtPosition ( battlerPosition ) ;
2018-06-17 16:48:58 +02:00
battlerIn2 = battlerIn1 ;
2017-10-10 21:45:07 +02:00
}
else
{
2023-08-29 15:21:31 +02:00
battlerIn1 = GetBattlerAtPosition ( battlerPosition ) ;
battlerIn2 = GetBattlerAtPosition ( BATTLE_PARTNER ( battlerPosition ) ) ;
2017-10-10 21:45:07 +02:00
}
2023-08-29 15:21:31 +02:00
GetAIPartyIndexes ( battler , & firstId , & lastId ) ;
2017-10-10 21:45:07 +02:00
2022-08-17 18:23:12 -06: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-08-29 15:21:31 +02:00
if ( IsAceMon ( battler , monToSwitchId ) )
2022-09-18 00:50:21 -03:00
continue ;
2017-10-10 21:45:07 +02:00
break ;
}
}
2023-08-29 15:21:31 +02:00
* ( gBattleStruct - > AI_monToSwitchIntoId + battler ) = monToSwitchId ;
2017-10-10 21:45:07 +02:00
}
2023-08-29 15:21:31 +02:00
* ( gBattleStruct - > monToSwitchIntoId + battler ) = * ( gBattleStruct - > AI_monToSwitchIntoId + battler ) ;
2017-10-10 21:45:07 +02:00
return ;
}
2023-08-29 15:21:31 +02:00
else if ( ShouldUseItem ( battler ) )
2017-10-10 21:45:07 +02:00
{
return ;
}
}
2023-08-29 15:21:31 +02:00
BtlController_EmitTwoReturnValues ( battler , BUFFER_B , B_ACTION_USE_MOVE , BATTLE_OPPOSITE ( battler ) < < 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-08-29 15:21:31 +02:00
static u32 GetBestMonBatonPass ( struct Pokemon * party , int firstId , int lastId , u8 invalidMons , int aliveCount , u32 battler , 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
2019-08-30 17:57:19 +02:00
for ( i = firstId ; i < lastId ; i + + )
{
if ( invalidMons & gBitTable [ i ] )
continue ;
2023-08-29 15:21:31 +02:00
if ( IsAiPartyMonOHKOBy ( battler , opposingBattler , & party [ i ] ) )
2022-09-15 22:57:29 -06:00
continue ;
2019-08-30 17:57:19 +02:00
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
2019-08-30 17:57:19 +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 ;
2019-08-30 17:57:19 +02:00
}
2017-10-10 21:45:07 +02:00
2019-09-01 14:23:11 +02:00
return PARTY_SIZE ;
}
2023-08-29 15:21:31 +02:00
static u32 GetBestMonTypeMatchup ( struct Pokemon * party , int firstId , int lastId , u8 invalidMons , u32 battler , 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
{
2023-07-07 14:11:49 +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 + + )
{
2019-08-30 17:57:19 +02:00
if ( ! ( gBitTable [ i ] & invalidMons ) & & ! ( gBitTable [ i ] & bits ) )
2017-10-10 21:45:07 +02:00
{
2019-08-30 17:57:19 +02:00
u16 species = GetMonData ( & party [ i ] , MON_DATA_SPECIES ) ;
2023-07-07 14:11:49 +02:00
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 ;
2023-01-20 12:31:54 -03:00
u8 defType1 = gSpeciesInfo [ species ] . types [ 0 ] ;
u8 defType2 = gSpeciesInfo [ species ] . types [ 1 ] ;
2018-07-15 12:39:07 +02:00
2023-08-29 15:21:31 +02:00
if ( IsAiPartyMonOHKOBy ( battler , opposingBattler , & party [ i ] ) )
2023-07-18 11:36:09 +02:00
continue ;
2022-09-15 22:57:29 -06:00
2023-07-07 14:11:49 +02:00
typeEffectiveness = uq4_12_multiply ( typeEffectiveness , ( GetTypeModifier ( atkType1 , defType1 ) ) ) ;
2018-07-15 12:39:07 +02:00
if ( atkType2 ! = atkType1 )
2023-07-07 14:11:49 +02:00
typeEffectiveness = uq4_12_multiply ( typeEffectiveness , ( GetTypeModifier ( atkType2 , defType1 ) ) ) ;
2018-07-15 12:39:07 +02:00
if ( defType2 ! = defType1 )
{
2023-07-07 14:11:49 +02:00
typeEffectiveness = uq4_12_multiply ( typeEffectiveness , ( GetTypeModifier ( atkType1 , defType2 ) ) ) ;
2018-07-15 12:39:07 +02:00
if ( atkType2 ! = atkType1 )
2023-07-07 14:11:49 +02:00
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
{
2018-12-25 12:50:15 -05: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 ) ;
2023-08-29 15:21:31 +02:00
if ( move ! = MOVE_NONE & & AI_GetTypeEffectiveness ( move , battler , opposingBattler ) > = UQ_4_12 ( 2.0 ) )
2017-10-10 21:45:07 +02:00
break ;
}
2018-12-25 12:50:15 -05:00
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
2019-08-30 17:57:19 +02:00
bits | = gBitTable [ bestMonId ] ; // Sorry buddy, we want something better.
2017-10-10 21:45:07 +02:00
}
else
{
2019-08-30 17:57:19 +02:00
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
2023-08-29 15:21:31 +02:00
static u32 GetBestMonDmg ( struct Pokemon * party , int firstId , int lastId , u8 invalidMons , u32 battler , u32 opposingBattler )
2019-09-01 14:23:11 +02:00
{
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 + + )
{
2019-08-30 17:57:19 +02:00
if ( gBitTable [ i ] & invalidMons )
2017-10-10 21:45:07 +02:00
continue ;
2023-08-29 15:21:31 +02:00
if ( IsAiPartyMonOHKOBy ( battler , opposingBattler , & party [ i ] ) )
2022-09-15 22:57:29 -06:00
continue ;
2017-10-10 21:45:07 +02:00
2023-08-29 15:21:31 +02:00
dmg = AI_CalcPartyMonBestMoveDamage ( battler , opposingBattler , & party [ i ] , NULL ) ;
2023-07-18 11:36:09 +02:00
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
2023-08-29 15:21:31 +02:00
u8 GetMostSuitableMonToSwitchInto ( u32 battler )
2019-09-01 14:23:11 +02:00
{
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
2023-08-29 15:21:31 +02:00
if ( * ( gBattleStruct - > monToSwitchIntoId + battler ) ! = PARTY_SIZE )
return * ( gBattleStruct - > monToSwitchIntoId + battler ) ;
2019-09-01 14:23:11 +02:00
if ( gBattleTypeFlags & BATTLE_TYPE_ARENA )
2023-08-29 15:21:31 +02:00
return gBattlerPartyIndexes [ battler ] + 1 ;
2019-09-01 14:23:11 +02:00
if ( gBattleTypeFlags & BATTLE_TYPE_DOUBLE )
{
2023-08-29 15:21:31 +02:00
battlerIn1 = battler ;
if ( gAbsentBattlerFlags & gBitTable [ GetBattlerAtPosition ( BATTLE_PARTNER ( GetBattlerPosition ( battler ) ) ) ] )
battlerIn2 = battler ;
2019-09-01 14:23:11 +02:00
else
2023-08-29 15:21:31 +02:00
battlerIn2 = GetBattlerAtPosition ( BATTLE_PARTNER ( GetBattlerPosition ( battler ) ) ) ;
2019-09-01 14:23:11 +02:00
opposingBattler = BATTLE_OPPOSITE ( battlerIn1 ) ;
if ( gAbsentBattlerFlags & gBitTable [ opposingBattler ] )
opposingBattler ^ = BIT_FLANK ;
}
else
{
2023-08-29 15:21:31 +02:00
opposingBattler = GetBattlerAtPosition ( BATTLE_OPPOSITE ( GetBattlerPosition ( battler ) ) ) ;
battlerIn1 = battler ;
battlerIn2 = battler ;
2019-09-01 14:23:11 +02:00
}
2023-08-29 15:21:31 +02:00
GetAIPartyIndexes ( battler , & firstId , & lastId ) ;
2019-09-01 14:23:11 +02:00
2023-08-29 15:21:31 +02:00
if ( GetBattlerSide ( battler ) = = B_SIDE_PLAYER )
2019-09-01 14:23:11 +02:00
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-08-29 15:21:31 +02:00
| | ( GetMonAbility ( & party [ i ] ) = = ABILITY_TRUANT & & IsTruantMonVulnerable ( battler , opposingBattler ) ) ) // While not really invalid per say, not really wise to switch into this mon.)
2023-02-08 12:42:38 +01:00
{
invalidMons | = gBitTable [ i ] ;
}
2023-08-29 15:21:31 +02:00
else if ( IsAceMon ( battler , 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-08-29 15:21:31 +02:00
bestMonId = GetBestMonBatonPass ( party , firstId , lastId , invalidMons , aliveCount , battler , opposingBattler ) ;
2019-09-01 14:23:11 +02:00
if ( bestMonId ! = PARTY_SIZE )
return bestMonId ;
2023-08-29 15:21:31 +02:00
bestMonId = GetBestMonTypeMatchup ( party , firstId , lastId , invalidMons , battler , opposingBattler ) ;
2019-09-01 14:23:11 +02:00
if ( bestMonId ! = PARTY_SIZE )
return bestMonId ;
2023-08-29 15:21:31 +02:00
bestMonId = GetBestMonDmg ( party , firstId , lastId , invalidMons , battler , opposingBattler ) ;
2019-09-01 14:23:11 +02:00
if ( bestMonId ! = PARTY_SIZE )
return bestMonId ;
2023-06-21 17:11:06 +02:00
// 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 ;
}
2023-08-29 15:21:31 +02:00
static bool32 AiExpectsToFaintPlayer ( u32 battler )
2022-06-05 11:09:04 -04:00
{
bool32 canFaintPlayer ;
u32 i ;
2023-08-29 15:21:31 +02:00
u8 target = gBattleStruct - > aiChosenTarget [ battler ] ;
2022-09-13 16:26:36 -03:00
2023-08-29 15:21:31 +02:00
if ( gBattleStruct - > aiMoveOrAction [ battler ] > 3 )
2022-06-05 11:09:04 -04:00
return FALSE ; // AI not planning to use move
2022-09-13 16:26:36 -03:00
2023-08-29 15:21:31 +02:00
if ( GetBattlerSide ( target ) ! = GetBattlerSide ( battler )
& & CanIndexMoveFaintTarget ( battler , target , gBattleStruct - > aiMoveOrAction [ battler ] , 0 )
& & AI_WhoStrikesFirst ( battler , target , GetAIChosenMove ( battler ) ) = = AI_IS_FASTER ) {
2022-06-05 11:09:04 -04:00
// We expect to faint the target and move first -> dont use an item
return TRUE ;
}
2022-09-13 16:26:36 -03:00
2022-06-05 11:09:04 -04:00
return FALSE ;
}
2023-08-29 15:21:31 +02:00
static bool8 ShouldUseItem ( u32 battler )
2017-10-11 12:49:42 +02:00
{
struct Pokemon * party ;
s32 i ;
u8 validMons = 0 ;
bool8 shouldUse = FALSE ;
2023-08-09 15:57:22 +02:00
if ( IsAiVsAiBattle ( ) )
return FALSE ;
2022-04-02 18:09:51 -03:00
// If teaming up with player and Pokemon is on the right, or Pokemon is currently held by Sky Drop
2023-08-29 15:21:31 +02:00
if ( ( gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER & & GetBattlerPosition ( battler ) = = B_POSITION_PLAYER_RIGHT )
| | gStatuses3 [ battler ] & STATUS3_SKY_DROPPED )
2017-10-11 12:49:42 +02:00
return FALSE ;
2022-09-13 16:26:36 -03:00
2023-08-29 15:21:31 +02:00
if ( gStatuses3 [ battler ] & STATUS3_EMBARGO )
2021-07-25 07:39:02 -06:00
return FALSE ;
2022-09-13 16:26:36 -03:00
2023-08-29 15:21:31 +02:00
if ( AiExpectsToFaintPlayer ( battler ) )
2022-06-05 11:09:04 -04:00
return FALSE ;
2017-10-11 12:49:42 +02:00
2023-08-29 15:21:31 +02:00
if ( GetBattlerSide ( battler ) = = 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 ;
2023-04-14 11:25:50 -07:00
itemEffects = GetItemEffect ( item ) ;
if ( itemEffects = = NULL )
2017-10-11 12:49:42 +02:00
continue ;
2023-04-14 11:25:50 -07:00
switch ( ItemId_GetBattleUsage ( item ) )
2017-10-11 12:49:42 +02:00
{
2023-04-14 11:25:50 -07:00
case EFFECT_ITEM_HEAL_AND_CURE_STATUS :
2023-08-29 15:21:31 +02:00
shouldUse = AI_ShouldHeal ( battler , 0 ) ;
2017-10-11 12:49:42 +02:00
break ;
2023-04-14 11:25:50 -07:00
case EFFECT_ITEM_RESTORE_HP :
2023-09-04 09:40:48 +02:00
shouldUse = AI_ShouldHeal ( battler , itemEffects [ GetItemEffectParamOffset ( battler , item , 4 , ITEM4_HEAL_HP ) ] ) ;
2017-10-11 12:49:42 +02:00
break ;
2023-04-14 11:25:50 -07:00
case EFFECT_ITEM_CURE_STATUS :
2023-08-29 15:21:31 +02:00
if ( itemEffects [ 3 ] & ITEM3_SLEEP & & gBattleMons [ battler ] . status1 & STATUS1_SLEEP )
2017-10-11 12:49:42 +02:00
shouldUse = TRUE ;
2023-08-29 15:21:31 +02:00
if ( itemEffects [ 3 ] & ITEM3_POISON & & ( gBattleMons [ battler ] . status1 & STATUS1_POISON
| | gBattleMons [ battler ] . status1 & STATUS1_TOXIC_POISON ) )
2017-10-11 12:49:42 +02:00
shouldUse = TRUE ;
2023-08-29 15:21:31 +02:00
if ( itemEffects [ 3 ] & ITEM3_BURN & & gBattleMons [ battler ] . status1 & STATUS1_BURN )
2017-10-11 12:49:42 +02:00
shouldUse = TRUE ;
2023-08-29 15:21:31 +02:00
if ( itemEffects [ 3 ] & ITEM3_FREEZE & & ( gBattleMons [ battler ] . status1 & STATUS1_FREEZE | | gBattleMons [ battler ] . status1 & STATUS1_FROSTBITE ) )
2017-10-11 12:49:42 +02:00
shouldUse = TRUE ;
2023-08-29 15:21:31 +02:00
if ( itemEffects [ 3 ] & ITEM3_PARALYSIS & & gBattleMons [ battler ] . status1 & STATUS1_PARALYSIS )
2017-10-11 12:49:42 +02:00
shouldUse = TRUE ;
2023-08-29 15:21:31 +02:00
if ( itemEffects [ 3 ] & ITEM3_CONFUSION & & gBattleMons [ battler ] . status2 & STATUS2_CONFUSION )
2017-10-11 12:49:42 +02:00
shouldUse = TRUE ;
break ;
2023-04-14 11:25:50 -07:00
case EFFECT_ITEM_INCREASE_STAT :
case EFFECT_ITEM_INCREASE_ALL_STATS :
2023-08-29 15:21:31 +02:00
if ( ! gDisableStructs [ battler ] . isFirstTurn
| | AI_OpponentCanFaintAiWithMod ( battler , 0 ) )
2017-10-11 12:49:42 +02:00
break ;
shouldUse = TRUE ;
break ;
2023-04-14 11:25:50 -07:00
case EFFECT_ITEM_SET_FOCUS_ENERGY :
2023-08-29 15:21:31 +02:00
if ( ! gDisableStructs [ battler ] . isFirstTurn
| | gBattleMons [ battler ] . status2 & STATUS2_FOCUS_ENERGY
| | AI_OpponentCanFaintAiWithMod ( battler , 0 ) )
2023-04-14 11:25:50 -07:00
break ;
shouldUse = TRUE ;
break ;
case EFFECT_ITEM_SET_MIST :
2023-08-29 15:21:31 +02:00
battlerSide = GetBattlerSide ( battler ) ;
if ( gDisableStructs [ battler ] . isFirstTurn & & gSideTimers [ battlerSide ] . mistTimer = = 0 )
2023-04-14 11:25:50 -07:00
shouldUse = TRUE ;
break ;
case EFFECT_ITEM_REVIVE :
2023-08-29 15:21:31 +02:00
gBattleStruct - > itemPartyIndex [ battler ] = GetFirstFaintedPartyIndex ( battler ) ;
if ( gBattleStruct - > itemPartyIndex [ battler ] ! = PARTY_SIZE ) // Revive if possible.
2017-10-11 12:49:42 +02:00
shouldUse = TRUE ;
break ;
2023-04-14 11:25:50 -07:00
default :
2017-10-11 12:49:42 +02:00
return FALSE ;
}
if ( shouldUse )
{
2023-04-14 11:25:50 -07:00
// Set selected party ID to current battler if none chosen.
2023-08-29 15:21:31 +02:00
if ( gBattleStruct - > itemPartyIndex [ battler ] = = PARTY_SIZE )
gBattleStruct - > itemPartyIndex [ battler ] = gBattlerPartyIndexes [ battler ] ;
BtlController_EmitTwoReturnValues ( battler , BUFFER_B , B_ACTION_USE_ITEM , 0 ) ;
gBattleStruct - > chosenItem [ battler ] = item ;
2017-10-11 12:49:42 +02:00
gBattleResources - > battleHistory - > trainerItems [ i ] = 0 ;
return shouldUse ;
}
}
return FALSE ;
}
2022-06-05 11:09:04 -04:00
2023-08-29 15:21:31 +02:00
static bool32 AI_ShouldHeal ( u32 battler , u32 healAmount )
2022-06-05 11:09:04 -04:00
{
bool32 shouldHeal = FALSE ;
2022-09-13 16:26:36 -03:00
2023-08-29 15:21:31 +02:00
if ( gBattleMons [ battler ] . hp < gBattleMons [ battler ] . maxHP / 4
| | gBattleMons [ battler ] . hp = = 0
| | ( healAmount ! = 0 & & gBattleMons [ battler ] . maxHP - gBattleMons [ battler ] . hp > healAmount ) ) {
2022-06-05 11:09:04 -04:00
// We have low enough HP to consider healing
2023-08-29 15:21:31 +02:00
shouldHeal = ! AI_OpponentCanFaintAiWithMod ( battler , healAmount ) ; // if target can kill us even after we heal, why bother
2022-06-05 11:09:04 -04:00
}
2022-09-13 16:26:36 -03:00
2022-06-05 11:09:04 -04:00
return shouldHeal ;
}
2023-08-29 15:21:31 +02:00
static bool32 AI_OpponentCanFaintAiWithMod ( u32 battler , u32 healAmount )
2022-06-05 11:09:04 -04:00
{
u32 i ;
// Check special cases to NOT heal
for ( i = 0 ; i < gBattlersCount ; i + + ) {
if ( GetBattlerSide ( i ) = = B_SIDE_PLAYER ) {
2023-08-29 15:21:31 +02:00
if ( CanTargetFaintAiWithMod ( i , battler , healAmount , 0 ) ) {
2022-06-05 11:09:04 -04:00
// Target is expected to faint us
return TRUE ;
}
}
}
return FALSE ;
}
2022-09-15 22:57:29 -06:00
2023-08-29 15:21:31 +02:00
static bool32 IsAiPartyMonOHKOBy ( u32 battlerAi , 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 ) ;
2023-08-29 15:21:31 +02:00
s32 bestDmg = AI_CalcPartyMonBestMoveDamage ( battlerAtk , battlerAi , 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 ( ) ;
2023-08-29 15:21:31 +02:00
PokemonToBattleMon ( aiMon , & gBattleMons [ battlerAi ] ) ;
if ( AI_WhoStrikesFirst ( battlerAi , battlerAtk , 0 ) = = AI_IS_SLOWER )
2023-07-18 12:01:25 +02:00
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
}