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"
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
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 ) ;
2022-08-11 22:48:36 -06:00
static bool32 AiExpectsToFaintPlayer ( void ) ;
2022-06-05 11:09:04 -04:00
static bool32 AI_ShouldHeal ( u32 healAmount ) ;
static bool32 AI_OpponentCanFaintAiWithMod ( u32 healAmount ) ;
2017-10-10 18:01:45 +02:00
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 )
{
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
{
if ( ( battlerId & 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
}
}
2019-06-11 10:45:12 +02:00
static bool8 ShouldSwitchIfAllBadMoves ( void )
{
if ( gBattleResources - > ai - > switchMon )
{
gBattleResources - > ai - > switchMon = 0 ;
* ( gBattleStruct - > AI_monToSwitchIntoId + gActiveBattler ) = PARTY_SIZE ;
2021-11-21 10:40:26 -08:00
BtlController_EmitTwoReturnValues ( BUFFER_B , B_ACTION_SWITCH , 0 ) ;
2019-06-11 10:45:12 +02:00
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
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
{
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 + + )
{
if ( GetMonData ( & party [ i ] , MON_DATA_HP ) = = 0 )
continue ;
if ( GetMonData ( & party [ i ] , MON_DATA_SPECIES2 ) = = SPECIES_NONE )
continue ;
if ( GetMonData ( & party [ i ] , MON_DATA_SPECIES2 ) = = SPECIES_EGG )
continue ;
2018-02-06 13:48:02 -06:00
if ( i = = gBattlerPartyIndexes [ gActiveBattler ] )
2017-10-10 18:01:45 +02:00
continue ;
2022-08-23 19:49:54 -06:00
if ( AI_THINKING_STRUCT - > aiFlags & AI_FLAG_ACE_POKEMON
& & i = = ( CalculateEnemyPartyCount ( ) - 1 ) )
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
{
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 ;
2021-11-21 10:40:26 -08:00
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 ;
2020-05-18 14:54:12 -07:00
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 ;
2022-08-23 22:38:35 -04:00
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 ;
2022-08-13 00:39:44 -06:00
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 species ;
2020-05-18 14:54:12 -07:00
u16 monAbility ;
2017-10-10 18:01:45 +02:00
if ( GetMonData ( & party [ i ] , MON_DATA_HP ) = = 0 )
continue ;
if ( GetMonData ( & party [ i ] , MON_DATA_SPECIES2 ) = = SPECIES_NONE )
continue ;
if ( GetMonData ( & party [ i ] , MON_DATA_SPECIES2 ) = = SPECIES_EGG )
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 ;
2022-08-17 18:23:12 -06:00
if ( AI_THINKING_STRUCT - > aiFlags & AI_FLAG_ACE_POKEMON
& & i = = ( CalculateEnemyPartyCount ( ) - 1 ) )
continue ;
2017-10-10 18:01:45 +02:00
species = GetMonData ( & party [ i ] , MON_DATA_SPECIES ) ;
2019-05-14 15:22:16 +02:00
if ( GetMonData ( & party [ i ] , MON_DATA_ABILITY_NUM ) ! = 0 )
2019-05-14 15:42:55 +02:00
monAbility = gBaseStats [ species ] . abilities [ 1 ] ;
2017-10-10 18:01:45 +02:00
else
2019-05-14 15:42:55 +02:00
monAbility = gBaseStats [ species ] . abilities [ 0 ] ;
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 ;
2021-10-12 19:50:32 -04:00
BtlController_EmitTwoReturnValues ( BUFFER_B , B_ACTION_SWITCH , 0 ) ;
2017-10-10 18:01:45 +02:00
return TRUE ;
}
}
return FALSE ;
}
2022-08-11 22:48:36 -06:00
static bool8 ShouldSwitchIfGameStatePrompt ( void )
2017-10-10 18:01:45 +02:00
{
2022-08-11 22:48:36 -06:00
bool8 switchMon = FALSE ;
2022-08-12 15:21:25 -06:00
u16 monAbility = AI_DATA - > abilities [ gActiveBattler ] ;
u16 holdEffect = AI_DATA - > holdEffects [ gActiveBattler ] ;
2022-08-11 22:48:36 -06:00
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
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
if ( AnyStatIsRaised ( gActiveBattler ) )
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
if ( gStatuses3 [ gActiveBattler ] & STATUS3_PERISH_SONG
& & gDisableStructs [ gActiveBattler ] . perishSongTimer = = 0
& & monAbility ! = ABILITY_SOUNDPROOF )
switchMon = TRUE ;
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
if ( gStatuses3 [ gActiveBattler ] & STATUS3_YAWN
& & AI_CanSleep ( gActiveBattler , monAbility )
& & gBattleMons [ gActiveBattler ] . hp > gBattleMons [ gActiveBattler ] . maxHP / 3 )
{
switchMon = TRUE ;
2022-08-13 00:39:44 -06:00
//Double Battles
//Check if partner can prevent sleep
if ( IsDoubleBattle ( ) )
{
if ( IsBattlerAlive ( BATTLE_PARTNER ( gActiveBattler ) )
2022-08-23 19:49:54 -06:00
& & ( GetAIChosenMove ( BATTLE_PARTNER ( gActiveBattler ) ) = = MOVE_UPROAR )
2022-08-13 00:39:44 -06:00
)
switchMon = FALSE ;
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 ) ;
if ( GetBattlerSide ( gActiveBattler ) = = B_SIDE_PLAYER )
party = gPlayerParty ;
for ( i = firstId ; i < lastId ; i + + )
{
2022-08-17 18:23:12 -06:00
if ( AI_THINKING_STRUCT - > aiFlags & AI_FLAG_ACE_POKEMON
& & i = = ( CalculateEnemyPartyCount ( ) - 1 ) )
break ;
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
if ( GetMonData ( & party [ i ] , MON_DATA_HP ) ! = 0
& & GetMonData ( & party [ i ] , MON_DATA_SPECIES2 ) ! = SPECIES_NONE
& & GetMonData ( & party [ i ] , MON_DATA_SPECIES2 ) ! = SPECIES_EGG
& & i ! = gBattlerPartyIndexes [ gActiveBattler ]
& & i ! = gBattlerPartyIndexes [ BATTLE_PARTNER ( gActiveBattler ) ]
& & IsBattlerGrounded ( gActiveBattler )
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
{
* ( gBattleStruct - > AI_monToSwitchIntoId + BATTLE_PARTNER ( gActiveBattler ) ) = i ;
BtlController_EmitTwoReturnValues ( BUFFER_B , B_ACTION_SWITCH , 0 ) ;
switchMon = FALSE ;
break ;
}
}
}
}
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
if ( AiExpectsToFaintPlayer ( ) )
switchMon = FALSE ;
//Checks to see if active Pokemon can do something against sleep
if ( monAbility = = ( ABILITY_NATURAL_CURE | ABILITY_SHED_SKIN | 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
2022-08-11 22:48:36 -06:00
& & ! ( gBattleMons [ gActiveBattler ] . status2 & STATUS2_FORESIGHT )
& & ! ( 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 ) )
2022-08-11 22:48:36 -06:00
& & gBattleMons [ gActiveBattler ] . hp > = ( gBattleMons [ gActiveBattler ] . maxHP / 3 )
& & ( Random ( ) % ( moduloChance * chanceReducer ) ) = = 0 )
switchMon = TRUE ;
//Cursed
moduloChance = 2 ; //50%
if ( gBattleMons [ gActiveBattler ] . status2 & STATUS2_CURSED
& & ( Random ( ) % ( moduloChance * chanceReducer ) ) = = 0 )
switchMon = TRUE ;
//Nightmare
moduloChance = 3 ; //33.3%
2022-08-23 19:49:54 -06:00
if ( gBattleMons [ gActiveBattler ] . status2 & STATUS2_NIGHTMARE
2022-08-11 22:48:36 -06:00
& & ( 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
2022-08-11 22:48:36 -06:00
//Infatuation
if ( gBattleMons [ gActiveBattler ] . status2 & STATUS2_INFATUATION
& & ! AiExpectsToFaintPlayer ( ) )
switchMon = TRUE ;
//Todo
//Pass Wish Heal
//Semi-Invulnerable
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 ( )
2022-08-23 19:49:54 -06:00
& & ! WillAIStrikeFirst ( )
2022-08-11 22:48:36 -06:00
& & ! AI_OpponentCanFaintAiWithMod ( 0 ) )
switchMon = FALSE ;
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
{
2018-06-17 16:48:58 +02:00
* ( gBattleStruct - > AI_monToSwitchIntoId + gActiveBattler ) = PARTY_SIZE ;
2021-10-12 19:50:32 -04:00
BtlController_EmitTwoReturnValues ( 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
2022-08-11 22:48:36 -06: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
2022-08-11 22:48:36 -06: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 ] ) {
2022-08-11 22:48:36 -06:00
case ABILITY_NATURAL_CURE :
moduloChance = 4 ; //25%
//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
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 ;
return FALSE ;
default :
return FALSE ;
2017-10-10 18:01:45 +02:00
}
2022-08-11 22:48:36 -06: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
{
2018-12-25 12:50:15 -05: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
{
2018-12-25 12:50:15 -05: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 ;
2022-08-23 22:38:35 -04:00
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 + + )
{
u16 species ;
2020-05-18 14:54:12 -07:00
u16 monAbility ;
2017-10-10 18:01:45 +02:00
if ( GetMonData ( & party [ i ] , MON_DATA_HP ) = = 0 )
continue ;
if ( GetMonData ( & party [ i ] , MON_DATA_SPECIES2 ) = = SPECIES_NONE )
continue ;
if ( GetMonData ( & party [ i ] , MON_DATA_SPECIES2 ) = = SPECIES_EGG )
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 ;
2022-08-17 18:23:12 -06:00
if ( AI_THINKING_STRUCT - > aiFlags & AI_FLAG_ACE_POKEMON
& & i = = ( CalculateEnemyPartyCount ( ) - 1 ) )
continue ;
2017-10-10 18:01:45 +02:00
species = GetMonData ( & party [ i ] , MON_DATA_SPECIES ) ;
2019-05-14 15:22:16 +02:00
if ( GetMonData ( & party [ i ] , MON_DATA_ABILITY_NUM ) ! = 0 )
2019-05-14 15:42:55 +02:00
monAbility = gBaseStats [ species ] . abilities [ 1 ] ;
2017-10-10 18:01:45 +02:00
else
2019-05-14 15:42:55 +02:00
monAbility = gBaseStats [ species ] . abilities [ 0 ] ;
2017-10-10 18:01:45 +02:00
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
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 ;
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 ;
2021-10-12 19:50:32 -04:00
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 ;
2020-02-08 14:20:02 +01: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 ;
2020-02-08 14:20:02 +01:00
if ( IsAbilityPreventingEscape ( gActiveBattler ) )
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 )
{
2020-02-08 14:20:02 +01:00
battlerIn1 = gActiveBattler ;
2022-09-02 23:24:08 -04:00
if ( gAbsentBattlerFlags & gBitTable [ GetBattlerAtPosition ( BATTLE_PARTNER ( GetBattlerPosition ( gActiveBattler ) ) ) ] )
2020-02-08 14:20:02 +01:00
battlerIn2 = gActiveBattler ;
2017-10-10 18:01:45 +02:00
else
2022-09-02 23:24:08 -04:00
battlerIn2 = GetBattlerAtPosition ( BATTLE_PARTNER ( GetBattlerPosition ( gActiveBattler ) ) ) ;
2017-10-10 18:01:45 +02:00
}
else
{
2020-02-08 14:20:02 +01: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 + + )
{
if ( GetMonData ( & party [ i ] , MON_DATA_HP ) = = 0 )
continue ;
if ( GetMonData ( & party [ i ] , MON_DATA_SPECIES2 ) = = SPECIES_NONE )
continue ;
if ( GetMonData ( & party [ i ] , MON_DATA_SPECIES2 ) = = SPECIES_EGG )
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 ;
2022-08-17 18:23:12 -06:00
if ( AI_THINKING_STRUCT - > aiFlags & AI_FLAG_ACE_POKEMON
& & i = = ( CalculateEnemyPartyCount ( ) - 1 ) )
continue ;
2017-10-10 18:01:45 +02:00
availableToSwitch + + ;
}
if ( availableToSwitch = = 0 )
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
2017-10-10 18:01:45 +02:00
if ( ShouldSwitchIfWonderGuard ( ) )
return TRUE ;
2022-08-11 22:48:36 -06:00
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 ;
2022-08-23 19:49:54 -06:00
//These Functions can prompt switch to generic pary members
if ( ShouldSwitchIfAllBadMoves ( ) )
2017-10-10 18:01:45 +02:00
return TRUE ;
2022-08-11 22:48:36 -06:00
if ( ShouldSwitchIfAbilityBenefit ( ) )
2017-10-10 18:01:45 +02:00
return TRUE ;
2022-08-23 19:49:54 -06: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 ;
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
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
2022-08-17 18:23:12 -06:00
for ( monToSwitchId = ( lastId - 1 ) ; monToSwitchId > = firstId ; monToSwitchId - - )
2017-10-10 21:45:07 +02:00
{
if ( GetMonData ( & party [ monToSwitchId ] , MON_DATA_HP ) = = 0 )
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 ;
2022-08-17 18:23:12 -06:00
if ( AI_THINKING_STRUCT - > aiFlags & AI_FLAG_ACE_POKEMON
& & monToSwitchId = = ( CalculateEnemyPartyCount ( ) - 1 ) )
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.
static u32 GetBestMonBatonPass ( struct Pokemon * party , int firstId , int lastId , u8 invalidMons , int aliveCount )
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 ;
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 ;
}
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
{
2022-06-18 19:00:33 +03:00
u32 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 ) ;
2022-06-18 19:00:33 +03:00
u32 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 = gBaseStats [ species ] . type1 ;
u8 defType2 = gBaseStats [ species ] . type2 ;
2018-07-15 12:39:07 +02:00
2022-06-18 19:00:33 +03:00
typeEffectiveness * = GetTypeModifier ( atkType1 , defType1 ) ;
2018-07-15 12:39:07 +02:00
if ( atkType2 ! = atkType1 )
2022-06-18 19:00:33 +03:00
typeEffectiveness * = GetTypeModifier ( atkType2 , defType1 ) ;
2018-07-15 12:39:07 +02:00
if ( defType2 ! = defType1 )
{
2022-06-18 19:00:33 +03:00
typeEffectiveness * = GetTypeModifier ( atkType1 , defType2 ) ;
2018-07-15 12:39:07 +02:00
if ( atkType2 ! = atkType1 )
2022-06-18 19:00:33 +03:00
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 ) ;
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 ;
}
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
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 ;
int bestDmg = 0 ;
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 ;
2018-12-25 12:50:15 -05:00
for ( j = 0 ; j < MAX_MON_MOVES ; j + + )
2017-10-10 21:45:07 +02:00
{
2019-09-01 14:23:11 +02:00
u32 move = GetMonData ( & party [ i ] , MON_DATA_MOVE1 + j ) ;
2019-08-30 17:57:19 +02:00
if ( move ! = MOVE_NONE & & gBattleMoves [ move ] . power ! = 0 )
2017-10-10 21:45:07 +02:00
{
2019-08-30 17:57:19 +02:00
s32 dmg = AI_CalcPartyMonDamage ( move , gActiveBattler , opposingBattler , & party [ i ] ) ;
if ( bestDmg < dmg )
{
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 ;
u32 bestMonId = 0 ;
u8 battlerIn1 = 0 , battlerIn2 = 0 ;
s32 firstId = 0 ;
s32 lastId = 0 ; // + 1
struct Pokemon * party ;
s32 i , j , aliveCount = 0 ;
u8 invalidMons = 0 ;
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 ;
2022-09-02 23:24:08 -04:00
if ( gAbsentBattlerFlags & gBitTable [ GetBattlerAtPosition ( BATTLE_PARTNER ( GetBattlerPosition ( gActiveBattler ) ) ) ] )
2019-09-01 14:23:11 +02:00
battlerIn2 = gActiveBattler ;
else
2022-09-02 23:24:08 -04:00
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
{
2022-09-02 23:24:08 -04:00
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 + + )
{
if ( GetMonData ( & party [ i ] , MON_DATA_SPECIES ) = = SPECIES_NONE
| | GetMonData ( & party [ i ] , MON_DATA_HP ) = = 0
| | gBattlerPartyIndexes [ battlerIn1 ] = = i
| | gBattlerPartyIndexes [ battlerIn2 ] = = i
| | i = = * ( gBattleStruct - > monToSwitchIntoId + battlerIn1 )
| | i = = * ( gBattleStruct - > monToSwitchIntoId + battlerIn2 )
2022-08-17 18:23:12 -06:00
| | ( GetMonAbility ( & party [ i ] ) = = ABILITY_TRUANT & & IsTruantMonVulnerable ( gActiveBattler , opposingBattler ) ) // While not really invalid per say, not really wise to switch into this mon.
| | ( AI_THINKING_STRUCT - > aiFlags & AI_FLAG_ACE_POKEMON
& & i = = ( CalculateEnemyPartyCount ( ) - 1 ) ) ) //Save Ace Pokemon for last
2019-09-01 14:23:11 +02:00
invalidMons | = gBitTable [ i ] ;
else
aliveCount + + ;
}
bestMonId = GetBestMonBatonPass ( party , firstId , lastId , invalidMons , aliveCount ) ;
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 ;
return PARTY_SIZE ;
}
2018-07-15 12:39:07 +02:00
static u8 GetAI_ItemType ( u16 itemId , const u8 * itemEffect )
2017-10-11 12:49:42 +02:00
{
if ( itemId = = ITEM_FULL_RESTORE )
return AI_ITEM_FULL_RESTORE ;
2019-04-14 17:20:26 +02:00
else if ( itemEffect [ 4 ] & ITEM4_HEAL_HP )
2017-10-11 12:49:42 +02:00
return AI_ITEM_HEAL_HP ;
2019-04-14 17:20:26 +02:00
else if ( itemEffect [ 3 ] & ITEM3_STATUS_ALL )
2017-10-11 12:49:42 +02:00
return AI_ITEM_CURE_CONDITION ;
2020-12-08 17:45:11 -03:00
else if ( ( itemEffect [ 0 ] & ITEM0_DIRE_HIT ) | | itemEffect [ 1 ] )
2017-10-11 12:49:42 +02:00
return AI_ITEM_X_STAT ;
2019-12-21 19:01:38 -05:00
else if ( itemEffect [ 3 ] & ITEM3_GUARD_SPEC )
2021-04-03 15:18:17 -04:00
return AI_ITEM_GUARD_SPEC ;
2018-06-17 16:48:58 +02:00
else
return AI_ITEM_NOT_RECOGNIZABLE ;
2017-10-11 12:49:42 +02:00
}
2022-06-05 11:09:04 -04:00
static bool32 AiExpectsToFaintPlayer ( void )
{
bool32 canFaintPlayer ;
u32 i ;
u8 target = gBattleStruct - > aiChosenTarget [ gActiveBattler ] ;
if ( gBattleStruct - > aiMoveOrAction [ gActiveBattler ] > 3 )
return FALSE ; // AI not planning to use move
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 ;
}
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 ;
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
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 ;
2021-07-25 07:39:02 -06:00
if ( gStatuses3 [ gActiveBattler ] & STATUS3_EMBARGO )
return FALSE ;
2022-06-05 11:09:04 -04: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
{
if ( GetMonData ( & party [ i ] , MON_DATA_HP ) ! = 0
& & GetMonData ( & party [ i ] , MON_DATA_SPECIES2 ) ! = SPECIES_NONE
& & GetMonData ( & party [ i ] , MON_DATA_SPECIES2 ) ! = SPECIES_EGG )
{
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
if ( i ! = 0 & & validMons > ( gBattleResources - > battleHistory - > itemsNo - i ) + 1 )
continue ;
item = gBattleResources - > battleHistory - > trainerItems [ i ] ;
if ( item = = ITEM_NONE )
continue ;
2019-04-14 17:20:26 +02:00
if ( gItemEffectTable [ item - ITEM_POTION ] = = NULL )
2017-10-11 12:49:42 +02:00
continue ;
2021-12-10 09:41:54 -08:00
if ( item = = ITEM_ENIGMA_BERRY_E_READER )
2017-10-11 12:49:42 +02:00
itemEffects = gSaveBlock1Ptr - > enigmaBerry . itemEffect ;
else
2019-04-14 17:20:26 +02:00
itemEffects = gItemEffectTable [ item - ITEM_POTION ] ;
2017-10-11 12:49:42 +02:00
2018-02-05 19:46:59 -06:00
* ( gBattleStruct - > AI_itemType + gActiveBattler / 2 ) = GetAI_ItemType ( item , itemEffects ) ;
2017-10-11 12:49:42 +02:00
2018-02-05 19:46:59 -06:00
switch ( * ( gBattleStruct - > AI_itemType + gActiveBattler / 2 ) )
2017-10-11 12:49:42 +02:00
{
case AI_ITEM_FULL_RESTORE :
2022-06-05 11:09:04 -04:00
shouldUse = AI_ShouldHeal ( 0 ) ;
2017-10-11 12:49:42 +02:00
break ;
case AI_ITEM_HEAL_HP :
2022-06-05 11:09:04 -04:00
shouldUse = AI_ShouldHeal ( itemEffects [ GetItemEffectParamOffset ( item , 4 , 4 ) ] ) ;
2017-10-11 12:49:42 +02:00
break ;
case AI_ITEM_CURE_CONDITION :
2018-02-05 19:46:59 -06:00
* ( gBattleStruct - > AI_itemFlags + gActiveBattler / 2 ) = 0 ;
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
{
2021-04-02 02:27:12 -04:00
* ( gBattleStruct - > AI_itemFlags + gActiveBattler / 2 ) | = ( 1 < < AI_HEAL_SLEEP ) ;
2017-10-11 12:49:42 +02:00
shouldUse = TRUE ;
}
2021-08-24 19:59:32 -03:00
if ( itemEffects [ 3 ] & ITEM3_POISON & & ( gBattleMons [ gActiveBattler ] . status1 & STATUS1_POISON
2021-04-02 02:27:12 -04:00
| | gBattleMons [ gActiveBattler ] . status1 & STATUS1_TOXIC_POISON ) )
2017-10-11 12:49:42 +02:00
{
2021-04-02 02:27:12 -04:00
* ( gBattleStruct - > AI_itemFlags + gActiveBattler / 2 ) | = ( 1 < < AI_HEAL_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
{
2021-04-02 02:27:12 -04:00
* ( gBattleStruct - > AI_itemFlags + gActiveBattler / 2 ) | = ( 1 < < AI_HEAL_BURN ) ;
2017-10-11 12:49:42 +02:00
shouldUse = TRUE ;
}
2019-04-14 17:20:26 +02:00
if ( itemEffects [ 3 ] & ITEM3_FREEZE & & gBattleMons [ gActiveBattler ] . status1 & STATUS1_FREEZE )
2017-10-11 12:49:42 +02:00
{
2021-04-02 02:27:12 -04:00
* ( gBattleStruct - > AI_itemFlags + gActiveBattler / 2 ) | = ( 1 < < AI_HEAL_FREEZE ) ;
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
{
2021-04-02 02:27:12 -04:00
* ( gBattleStruct - > AI_itemFlags + gActiveBattler / 2 ) | = ( 1 < < AI_HEAL_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
{
2021-04-02 02:27:12 -04:00
* ( gBattleStruct - > AI_itemFlags + gActiveBattler / 2 ) | = ( 1 < < AI_HEAL_CONFUSION ) ;
2017-10-11 12:49:42 +02:00
shouldUse = TRUE ;
}
break ;
case AI_ITEM_X_STAT :
2018-02-05 19:46:59 -06:00
* ( gBattleStruct - > AI_itemFlags + gActiveBattler / 2 ) = 0 ;
if ( gDisableStructs [ gActiveBattler ] . isFirstTurn = = 0 )
2017-10-11 12:49:42 +02:00
break ;
2020-12-08 17:45:11 -03:00
if ( itemEffects [ 1 ] & ITEM1_X_ATTACK )
2021-04-27 18:36:37 -07:00
* ( gBattleStruct - > AI_itemFlags + gActiveBattler / 2 ) | = ( 1 < < AI_X_ATTACK ) ;
2021-01-10 01:58:02 -03:00
if ( itemEffects [ 1 ] & ITEM1_X_DEFENSE )
2021-04-27 18:36:37 -07:00
* ( gBattleStruct - > AI_itemFlags + gActiveBattler / 2 ) | = ( 1 < < AI_X_DEFEND ) ;
2020-12-08 17:45:11 -03:00
if ( itemEffects [ 1 ] & ITEM1_X_SPEED )
2021-04-27 18:36:37 -07:00
* ( gBattleStruct - > AI_itemFlags + gActiveBattler / 2 ) | = ( 1 < < AI_X_SPEED ) ;
2020-12-08 17:45:11 -03:00
if ( itemEffects [ 1 ] & ITEM1_X_SPATK )
2021-04-27 18:36:37 -07:00
* ( gBattleStruct - > AI_itemFlags + gActiveBattler / 2 ) | = ( 1 < < AI_X_SPATK ) ;
2020-12-08 17:45:11 -03:00
if ( itemEffects [ 1 ] & ITEM1_X_SPDEF )
2021-04-27 18:36:37 -07:00
* ( gBattleStruct - > AI_itemFlags + gActiveBattler / 2 ) | = ( 1 < < AI_X_SPDEF ) ;
2020-12-08 17:45:11 -03:00
if ( itemEffects [ 1 ] & ITEM1_X_ACCURACY )
2021-04-27 18:36:37 -07:00
* ( gBattleStruct - > AI_itemFlags + gActiveBattler / 2 ) | = ( 1 < < AI_X_ACCURACY ) ;
2020-12-08 17:45:11 -03:00
if ( itemEffects [ 0 ] & ITEM0_DIRE_HIT )
2021-04-27 18:36:37 -07:00
* ( gBattleStruct - > AI_itemFlags + gActiveBattler / 2 ) | = ( 1 < < AI_DIRE_HIT ) ;
2017-10-11 12:49:42 +02:00
shouldUse = TRUE ;
break ;
2021-04-03 15:18:17 -04:00
case AI_ITEM_GUARD_SPEC :
2018-06-17 16:48:58 +02:00
battlerSide = GetBattlerSide ( gActiveBattler ) ;
if ( gDisableStructs [ gActiveBattler ] . isFirstTurn ! = 0 & & gSideTimers [ battlerSide ] . mistTimer = = 0 )
2017-10-11 12:49:42 +02:00
shouldUse = TRUE ;
break ;
case AI_ITEM_NOT_RECOGNIZABLE :
return FALSE ;
}
if ( shouldUse )
{
2021-10-12 19:50:32 -04:00
BtlController_EmitTwoReturnValues ( BUFFER_B , B_ACTION_USE_ITEM , 0 ) ;
2018-02-05 19:46:59 -06:00
* ( gBattleStruct - > chosenItem + ( gActiveBattler / 2 ) * 2 ) = 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
static bool32 AI_ShouldHeal ( u32 healAmount )
{
bool32 shouldHeal = FALSE ;
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
}
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 ;
}