2020-12-11 06:10:21 +01:00
# include "global.h"
2021-05-24 18:46:53 +02:00
# include "battle_z_move.h"
2020-12-11 06:10:21 +01:00
# include "malloc.h"
# include "battle.h"
# include "battle_anim.h"
# include "battle_ai_util.h"
2020-12-20 22:47:20 +01:00
# include "battle_ai_main.h"
2020-12-20 05:58:23 +01:00
# include "battle_ai_switch_items.h"
2020-12-11 06:10:21 +01:00
# include "battle_factory.h"
# include "battle_setup.h"
# include "data.h"
# include "item.h"
# include "pokemon.h"
# include "random.h"
# include "recorded_battle.h"
# include "util.h"
# include "constants/abilities.h"
# include "constants/battle_ai.h"
# include "constants/battle_move_effects.h"
# include "constants/hold_effects.h"
# include "constants/moves.h"
2020-12-20 05:58:23 +01:00
# include "constants/items.h"
2020-12-11 06:10:21 +01:00
2022-07-03 23:20:39 +02:00
static u32 AI_GetEffectiveness ( u16 multiplier ) ;
2022-01-13 17:28:27 +01:00
2020-12-11 16:05:00 +01:00
// Const Data
2020-12-20 05:58:23 +01:00
static const s8 sAiAbilityRatings [ ABILITIES_COUNT ] =
{
[ ABILITY_ADAPTABILITY ] = 8 ,
[ ABILITY_AFTERMATH ] = 5 ,
[ ABILITY_AERILATE ] = 8 ,
[ ABILITY_AIR_LOCK ] = 5 ,
[ ABILITY_ANALYTIC ] = 5 ,
[ ABILITY_ANGER_POINT ] = 4 ,
[ ABILITY_ANTICIPATION ] = 2 ,
[ ABILITY_ARENA_TRAP ] = 9 ,
[ ABILITY_AROMA_VEIL ] = 3 ,
[ ABILITY_AURA_BREAK ] = 3 ,
[ ABILITY_BAD_DREAMS ] = 4 ,
[ ABILITY_BATTERY ] = 0 ,
[ ABILITY_BATTLE_ARMOR ] = 2 ,
[ ABILITY_BATTLE_BOND ] = 6 ,
[ ABILITY_BEAST_BOOST ] = 7 ,
[ ABILITY_BERSERK ] = 5 ,
[ ABILITY_BIG_PECKS ] = 1 ,
[ ABILITY_BLAZE ] = 5 ,
[ ABILITY_BULLETPROOF ] = 7 ,
[ ABILITY_CHEEK_POUCH ] = 4 ,
[ ABILITY_CHLOROPHYLL ] = 6 ,
[ ABILITY_CLEAR_BODY ] = 4 ,
[ ABILITY_CLOUD_NINE ] = 5 ,
[ ABILITY_COLOR_CHANGE ] = 2 ,
[ ABILITY_COMATOSE ] = 6 ,
[ ABILITY_COMPETITIVE ] = 5 ,
[ ABILITY_COMPOUND_EYES ] = 7 ,
[ ABILITY_CONTRARY ] = 8 ,
[ ABILITY_CORROSION ] = 5 ,
[ ABILITY_CURSED_BODY ] = 4 ,
[ ABILITY_CUTE_CHARM ] = 2 ,
[ ABILITY_DAMP ] = 2 ,
[ ABILITY_DANCER ] = 5 ,
[ ABILITY_DARK_AURA ] = 6 ,
[ ABILITY_DAZZLING ] = 5 ,
[ ABILITY_DEFEATIST ] = - 1 ,
[ ABILITY_DEFIANT ] = 5 ,
[ ABILITY_DELTA_STREAM ] = 10 ,
[ ABILITY_DESOLATE_LAND ] = 10 ,
[ ABILITY_DISGUISE ] = 8 ,
[ ABILITY_DOWNLOAD ] = 7 ,
[ ABILITY_DRIZZLE ] = 9 ,
[ ABILITY_DROUGHT ] = 9 ,
[ ABILITY_DRY_SKIN ] = 6 ,
[ ABILITY_EARLY_BIRD ] = 4 ,
[ ABILITY_EFFECT_SPORE ] = 4 ,
[ ABILITY_ELECTRIC_SURGE ] = 8 ,
[ ABILITY_EMERGENCY_EXIT ] = 3 ,
[ ABILITY_FAIRY_AURA ] = 6 ,
[ ABILITY_FILTER ] = 6 ,
[ ABILITY_FLAME_BODY ] = 4 ,
[ ABILITY_FLARE_BOOST ] = 5 ,
[ ABILITY_FLASH_FIRE ] = 6 ,
[ ABILITY_FLOWER_GIFT ] = 4 ,
[ ABILITY_FLOWER_VEIL ] = 0 ,
[ ABILITY_FLUFFY ] = 5 ,
[ ABILITY_FORECAST ] = 6 ,
[ ABILITY_FOREWARN ] = 2 ,
[ ABILITY_FRIEND_GUARD ] = 0 ,
[ ABILITY_FRISK ] = 3 ,
[ ABILITY_FULL_METAL_BODY ] = 4 ,
[ ABILITY_FUR_COAT ] = 7 ,
[ ABILITY_GALE_WINGS ] = 6 ,
[ ABILITY_GALVANIZE ] = 8 ,
[ ABILITY_GLUTTONY ] = 3 ,
[ ABILITY_GOOEY ] = 5 ,
[ ABILITY_GRASS_PELT ] = 2 ,
[ ABILITY_GRASSY_SURGE ] = 8 ,
[ ABILITY_GUTS ] = 6 ,
[ ABILITY_HARVEST ] = 5 ,
[ ABILITY_HEALER ] = 0 ,
[ ABILITY_HEATPROOF ] = 5 ,
[ ABILITY_HEAVY_METAL ] = - 1 ,
[ ABILITY_HONEY_GATHER ] = 0 ,
[ ABILITY_HUGE_POWER ] = 10 ,
[ ABILITY_HUSTLE ] = 7 ,
[ ABILITY_HYDRATION ] = 4 ,
[ ABILITY_HYPER_CUTTER ] = 3 ,
[ ABILITY_ICE_BODY ] = 3 ,
[ ABILITY_ILLUMINATE ] = 0 ,
[ ABILITY_ILLUSION ] = 8 ,
[ ABILITY_IMMUNITY ] = 4 ,
[ ABILITY_IMPOSTER ] = 9 ,
[ ABILITY_INFILTRATOR ] = 6 ,
[ ABILITY_INNARDS_OUT ] = 5 ,
[ ABILITY_INNER_FOCUS ] = 2 ,
[ ABILITY_INSOMNIA ] = 4 ,
[ ABILITY_INTIMIDATE ] = 7 ,
[ ABILITY_IRON_BARBS ] = 6 ,
[ ABILITY_IRON_FIST ] = 6 ,
[ ABILITY_JUSTIFIED ] = 4 ,
[ ABILITY_KEEN_EYE ] = 1 ,
[ ABILITY_KLUTZ ] = - 1 ,
[ ABILITY_LEAF_GUARD ] = 2 ,
[ ABILITY_LEVITATE ] = 7 ,
[ ABILITY_LIGHT_METAL ] = 2 ,
[ ABILITY_LIGHTNING_ROD ] = 7 ,
[ ABILITY_LIMBER ] = 3 ,
[ ABILITY_LIQUID_OOZE ] = 3 ,
[ ABILITY_LIQUID_VOICE ] = 5 ,
[ ABILITY_LONG_REACH ] = 3 ,
[ ABILITY_MAGIC_BOUNCE ] = 9 ,
[ ABILITY_MAGIC_GUARD ] = 9 ,
[ ABILITY_MAGICIAN ] = 3 ,
[ ABILITY_MAGMA_ARMOR ] = 1 ,
[ ABILITY_MAGNET_PULL ] = 9 ,
[ ABILITY_MARVEL_SCALE ] = 5 ,
[ ABILITY_MEGA_LAUNCHER ] = 7 ,
[ ABILITY_MERCILESS ] = 4 ,
[ ABILITY_MINUS ] = 0 ,
[ ABILITY_MISTY_SURGE ] = 8 ,
[ ABILITY_MOLD_BREAKER ] = 7 ,
[ ABILITY_MOODY ] = 10 ,
[ ABILITY_MOTOR_DRIVE ] = 6 ,
[ ABILITY_MOXIE ] = 7 ,
[ ABILITY_MULTISCALE ] = 8 ,
[ ABILITY_MULTITYPE ] = 8 ,
[ ABILITY_MUMMY ] = 5 ,
[ ABILITY_NATURAL_CURE ] = 7 ,
[ ABILITY_NEUROFORCE ] = 6 ,
[ ABILITY_NO_GUARD ] = 8 ,
[ ABILITY_NORMALIZE ] = - 1 ,
[ ABILITY_OBLIVIOUS ] = 2 ,
[ ABILITY_OVERCOAT ] = 5 ,
[ ABILITY_OVERGROW ] = 5 ,
[ ABILITY_OWN_TEMPO ] = 3 ,
[ ABILITY_PARENTAL_BOND ] = 10 ,
[ ABILITY_PICKUP ] = 1 ,
[ ABILITY_PICKPOCKET ] = 3 ,
[ ABILITY_PIXILATE ] = 8 ,
[ ABILITY_PLUS ] = 0 ,
[ ABILITY_POISON_HEAL ] = 8 ,
[ ABILITY_POISON_POINT ] = 4 ,
[ ABILITY_POISON_TOUCH ] = 4 ,
[ ABILITY_POWER_CONSTRUCT ] = 10 ,
[ ABILITY_POWER_OF_ALCHEMY ] = 0 ,
[ ABILITY_PRANKSTER ] = 8 ,
[ ABILITY_PRESSURE ] = 5 ,
[ ABILITY_PRIMORDIAL_SEA ] = 10 ,
[ ABILITY_PRISM_ARMOR ] = 6 ,
[ ABILITY_PROTEAN ] = 8 ,
[ ABILITY_PSYCHIC_SURGE ] = 8 ,
[ ABILITY_PURE_POWER ] = 10 ,
[ ABILITY_QUEENLY_MAJESTY ] = 6 ,
[ ABILITY_QUICK_FEET ] = 5 ,
[ ABILITY_RAIN_DISH ] = 3 ,
[ ABILITY_RATTLED ] = 3 ,
[ ABILITY_RECEIVER ] = 0 ,
[ ABILITY_RECKLESS ] = 6 ,
[ ABILITY_REFRIGERATE ] = 8 ,
[ ABILITY_REGENERATOR ] = 8 ,
[ ABILITY_RIVALRY ] = 1 ,
[ ABILITY_RKS_SYSTEM ] = 8 ,
[ ABILITY_ROCK_HEAD ] = 5 ,
[ ABILITY_ROUGH_SKIN ] = 6 ,
[ ABILITY_RUN_AWAY ] = 0 ,
[ ABILITY_SAND_FORCE ] = 4 ,
[ ABILITY_SAND_RUSH ] = 6 ,
[ ABILITY_SAND_STREAM ] = 9 ,
[ ABILITY_SAND_VEIL ] = 3 ,
[ ABILITY_SAP_SIPPER ] = 7 ,
[ ABILITY_SCHOOLING ] = 6 ,
[ ABILITY_SCRAPPY ] = 6 ,
[ ABILITY_SERENE_GRACE ] = 8 ,
[ ABILITY_SHADOW_SHIELD ] = 8 ,
[ ABILITY_SHADOW_TAG ] = 10 ,
[ ABILITY_SHED_SKIN ] = 7 ,
[ ABILITY_SHEER_FORCE ] = 8 ,
[ ABILITY_SHELL_ARMOR ] = 2 ,
[ ABILITY_SHIELD_DUST ] = 5 ,
[ ABILITY_SHIELDS_DOWN ] = 6 ,
[ ABILITY_SIMPLE ] = 8 ,
[ ABILITY_SKILL_LINK ] = 7 ,
[ ABILITY_SLOW_START ] = - 2 ,
[ ABILITY_SLUSH_RUSH ] = 5 ,
[ ABILITY_SNIPER ] = 3 ,
[ ABILITY_SNOW_CLOAK ] = 3 ,
[ ABILITY_SNOW_WARNING ] = 8 ,
[ ABILITY_SOLAR_POWER ] = 3 ,
[ ABILITY_SOLID_ROCK ] = 6 ,
[ ABILITY_SOUL_HEART ] = 7 ,
[ ABILITY_SOUNDPROOF ] = 4 ,
[ ABILITY_SPEED_BOOST ] = 9 ,
[ ABILITY_STAKEOUT ] = 6 ,
[ ABILITY_STALL ] = - 1 ,
[ ABILITY_STAMINA ] = 6 ,
[ ABILITY_STANCE_CHANGE ] = 10 ,
[ ABILITY_STATIC ] = 4 ,
[ ABILITY_STEADFAST ] = 2 ,
[ ABILITY_STEELWORKER ] = 6 ,
[ ABILITY_STENCH ] = 1 ,
[ ABILITY_STICKY_HOLD ] = 3 ,
[ ABILITY_STORM_DRAIN ] = 7 ,
[ ABILITY_STRONG_JAW ] = 6 ,
[ ABILITY_STURDY ] = 6 ,
[ ABILITY_SUCTION_CUPS ] = 2 ,
[ ABILITY_SUPER_LUCK ] = 3 ,
[ ABILITY_SURGE_SURFER ] = 4 ,
[ ABILITY_SWARM ] = 5 ,
[ ABILITY_SWEET_VEIL ] = 4 ,
[ ABILITY_SWIFT_SWIM ] = 6 ,
[ ABILITY_SYMBIOSIS ] = 0 ,
[ ABILITY_SYNCHRONIZE ] = 4 ,
[ ABILITY_TANGLED_FEET ] = 2 ,
[ ABILITY_TANGLING_HAIR ] = 5 ,
[ ABILITY_TECHNICIAN ] = 8 ,
[ ABILITY_TELEPATHY ] = 0 ,
[ ABILITY_TERAVOLT ] = 7 ,
[ ABILITY_THICK_FAT ] = 7 ,
[ ABILITY_TINTED_LENS ] = 7 ,
[ ABILITY_TORRENT ] = 5 ,
[ ABILITY_TOXIC_BOOST ] = 6 ,
[ ABILITY_TOUGH_CLAWS ] = 7 ,
[ ABILITY_TRACE ] = 6 ,
[ ABILITY_TRIAGE ] = 7 ,
[ ABILITY_TRUANT ] = - 2 ,
[ ABILITY_TURBOBLAZE ] = 7 ,
[ ABILITY_UNAWARE ] = 6 ,
[ ABILITY_UNBURDEN ] = 7 ,
[ ABILITY_UNNERVE ] = 3 ,
[ ABILITY_VICTORY_STAR ] = 6 ,
[ ABILITY_VITAL_SPIRIT ] = 4 ,
[ ABILITY_VOLT_ABSORB ] = 7 ,
[ ABILITY_WATER_ABSORB ] = 7 ,
[ ABILITY_WATER_BUBBLE ] = 8 ,
[ ABILITY_WATER_COMPACTION ] = 4 ,
[ ABILITY_WATER_VEIL ] = 4 ,
[ ABILITY_WEAK_ARMOR ] = 2 ,
[ ABILITY_WHITE_SMOKE ] = 4 ,
[ ABILITY_WIMP_OUT ] = 3 ,
[ ABILITY_WONDER_GUARD ] = 10 ,
[ ABILITY_WONDER_SKIN ] = 4 ,
[ ABILITY_ZEN_MODE ] = - 1 ,
[ ABILITY_INTREPID_SWORD ] = 3 ,
[ ABILITY_DAUNTLESS_SHIELD ] = 3 ,
[ ABILITY_BALL_FETCH ] = 0 ,
[ ABILITY_COTTON_DOWN ] = 3 ,
[ ABILITY_MIRROR_ARMOR ] = 6 ,
[ ABILITY_GULP_MISSILE ] = 3 ,
[ ABILITY_STALWART ] = 2 ,
[ ABILITY_PROPELLER_TAIL ] = 2 ,
[ ABILITY_STEAM_ENGINE ] = 3 ,
[ ABILITY_PUNK_ROCK ] = 2 ,
[ ABILITY_SAND_SPIT ] = 5 ,
[ ABILITY_ICE_SCALES ] = 7 ,
[ ABILITY_RIPEN ] = 4 ,
[ ABILITY_ICE_FACE ] = 4 ,
[ ABILITY_POWER_SPOT ] = 2 ,
[ ABILITY_MIMICRY ] = 2 ,
[ ABILITY_SCREEN_CLEANER ] = 3 ,
[ ABILITY_NEUTRALIZING_GAS ] = 5 ,
[ ABILITY_HUNGER_SWITCH ] = 2 ,
[ ABILITY_PASTEL_VEIL ] = 4 ,
[ ABILITY_STEELY_SPIRIT ] = 2 ,
[ ABILITY_PERISH_BODY ] = - 1 ,
[ ABILITY_WANDERING_SPIRIT ] = 2 ,
[ ABILITY_GORILLA_TACTICS ] = 4 ,
} ;
2021-06-10 12:55:05 +02:00
static const u16 sEncouragedEncoreEffects [ ] =
2020-12-20 05:58:23 +01:00
{
EFFECT_DREAM_EATER ,
EFFECT_ATTACK_UP ,
EFFECT_DEFENSE_UP ,
EFFECT_SPEED_UP ,
EFFECT_SPECIAL_ATTACK_UP ,
EFFECT_HAZE ,
EFFECT_ROAR ,
EFFECT_CONVERSION ,
EFFECT_TOXIC ,
EFFECT_LIGHT_SCREEN ,
EFFECT_REST ,
EFFECT_SUPER_FANG ,
EFFECT_SPECIAL_DEFENSE_UP_2 ,
EFFECT_CONFUSE ,
EFFECT_POISON ,
EFFECT_PARALYZE ,
EFFECT_LEECH_SEED ,
EFFECT_DO_NOTHING ,
EFFECT_ATTACK_UP_2 ,
EFFECT_ENCORE ,
EFFECT_CONVERSION_2 ,
EFFECT_LOCK_ON ,
EFFECT_HEAL_BELL ,
EFFECT_MEAN_LOOK ,
EFFECT_NIGHTMARE ,
EFFECT_PROTECT ,
EFFECT_SKILL_SWAP ,
EFFECT_FORESIGHT ,
EFFECT_PERISH_SONG ,
EFFECT_SANDSTORM ,
EFFECT_ENDURE ,
EFFECT_SWAGGER ,
EFFECT_ATTRACT ,
EFFECT_SAFEGUARD ,
EFFECT_RAIN_DANCE ,
EFFECT_SUNNY_DAY ,
EFFECT_BELLY_DRUM ,
EFFECT_PSYCH_UP ,
EFFECT_FUTURE_SIGHT ,
EFFECT_FAKE_OUT ,
EFFECT_STOCKPILE ,
EFFECT_SPIT_UP ,
EFFECT_SWALLOW ,
EFFECT_HAIL ,
EFFECT_TORMENT ,
EFFECT_WILL_O_WISP ,
EFFECT_FOLLOW_ME ,
EFFECT_CHARGE ,
EFFECT_TRICK ,
EFFECT_ROLE_PLAY ,
EFFECT_INGRAIN ,
EFFECT_RECYCLE ,
EFFECT_KNOCK_OFF ,
EFFECT_SKILL_SWAP ,
EFFECT_IMPRISON ,
EFFECT_REFRESH ,
EFFECT_GRUDGE ,
EFFECT_TEETER_DANCE ,
EFFECT_MUD_SPORT ,
EFFECT_WATER_SPORT ,
EFFECT_DRAGON_DANCE ,
EFFECT_CAMOUFLAGE ,
} ;
2021-08-12 01:33:10 +02:00
// For the purposes of determining the most powerful move in a moveset, these
// moves are treated the same as having a power of 0 or 1
# define IGNORED_MOVES_END 0xFFFF
static const u16 sIgnoredPowerfulMoveEffects [ ] =
2020-12-11 16:05:00 +01:00
{
EFFECT_EXPLOSION ,
EFFECT_DREAM_EATER ,
EFFECT_RECHARGE ,
EFFECT_SKULL_BASH ,
2021-11-21 19:40:26 +01:00
EFFECT_SOLAR_BEAM ,
2020-12-11 16:05:00 +01:00
EFFECT_SPIT_UP ,
EFFECT_FOCUS_PUNCH ,
EFFECT_SUPERPOWER ,
EFFECT_ERUPTION ,
EFFECT_OVERHEAT ,
EFFECT_MIND_BLOWN ,
2021-08-12 01:33:10 +02:00
IGNORED_MOVES_END
2020-12-11 16:05:00 +01:00
} ;
2021-06-10 12:55:05 +02:00
static const u16 sIgnoreMoldBreakerMoves [ ] =
2020-12-13 23:02:21 +01:00
{
MOVE_MOONGEIST_BEAM ,
MOVE_SUNSTEEL_STRIKE ,
MOVE_PHOTON_GEYSER ,
MOVE_LIGHT_THAT_BURNS_THE_SKY ,
MOVE_MENACING_MOONRAZE_MAELSTROM ,
MOVE_SEARING_SUNRAZE_SMASH ,
} ;
2021-06-10 12:55:05 +02:00
static const u16 sInstructBannedMoves [ ] =
2020-12-13 23:02:21 +01:00
{
MOVE_INSTRUCT ,
MOVE_BIDE ,
MOVE_FOCUS_PUNCH ,
MOVE_BEAK_BLAST ,
MOVE_SHELL_TRAP ,
MOVE_SKETCH ,
MOVE_TRANSFORM ,
MOVE_MIMIC ,
MOVE_KINGS_SHIELD ,
MOVE_STRUGGLE ,
MOVE_BOUNCE ,
MOVE_DIG ,
MOVE_DIVE ,
MOVE_FLY ,
MOVE_FREEZE_SHOCK ,
MOVE_GEOMANCY ,
MOVE_ICE_BURN ,
MOVE_PHANTOM_FORCE ,
MOVE_RAZOR_WIND ,
MOVE_SHADOW_FORCE ,
MOVE_SKULL_BASH ,
MOVE_SKY_ATTACK ,
MOVE_SKY_DROP ,
MOVE_SOLAR_BEAM ,
MOVE_SOLAR_BLADE ,
} ;
2021-06-10 12:55:05 +02:00
static const u16 sRechargeMoves [ ] =
2020-12-13 23:02:21 +01:00
{
MOVE_HYPER_BEAM ,
MOVE_BLAST_BURN ,
MOVE_HYDRO_CANNON ,
MOVE_FRENZY_PLANT ,
MOVE_GIGA_IMPACT ,
MOVE_ROCK_WRECKER ,
MOVE_ROAR_OF_TIME ,
MOVE_PRISMATIC_LASER ,
MOVE_METEOR_ASSAULT ,
MOVE_ETERNABEAM ,
} ;
2021-06-10 12:55:05 +02:00
static const u16 sOtherMoveCallingMoves [ ] =
2020-12-13 23:02:21 +01:00
{
MOVE_ASSIST ,
MOVE_COPYCAT ,
MOVE_ME_FIRST ,
MOVE_METRONOME ,
MOVE_MIRROR_MOVE ,
MOVE_NATURE_POWER ,
MOVE_SLEEP_TALK ,
} ;
2020-12-11 06:10:21 +01:00
// Functions
2022-06-05 17:09:04 +02:00
u16 GetAIChosenMove ( u8 battlerId )
{
return ( gBattleMons [ battlerId ] . moves [ gBattleStruct - > aiMoveOrAction [ battlerId ] ] ) ;
}
2021-09-28 03:03:27 +02:00
bool32 WillAIStrikeFirst ( void )
{
2022-06-05 17:09:04 +02:00
return ( AI_WhoStrikesFirst ( sBattler_AI , gBattlerTarget , AI_THINKING_STRUCT - > moveConsidered ) = = AI_IS_FASTER ) ;
2021-09-28 03:03:27 +02:00
}
2021-01-05 03:39:59 +01:00
bool32 AI_RandLessThan ( u8 val )
{
if ( ( Random ( ) % 0xFF ) < val )
return TRUE ;
return FALSE ;
}
2020-12-11 16:05:00 +01:00
void RecordLastUsedMoveByTarget ( void )
{
RecordKnownMove ( gBattlerTarget , gLastMoves [ gBattlerTarget ] ) ;
}
bool32 IsBattlerAIControlled ( u32 battlerId )
{
switch ( GetBattlerPosition ( battlerId ) )
{
case B_POSITION_PLAYER_LEFT :
default :
return FALSE ;
case B_POSITION_OPPONENT_LEFT :
return TRUE ;
case B_POSITION_PLAYER_RIGHT :
return ( ( gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER ) ! = 0 ) ;
case B_POSITION_OPPONENT_RIGHT :
return TRUE ;
}
}
void ClearBattlerMoveHistory ( u8 battlerId )
{
memset ( BATTLE_HISTORY - > usedMoves [ battlerId ] , 0 , sizeof ( BATTLE_HISTORY - > usedMoves [ battlerId ] ) ) ;
memset ( BATTLE_HISTORY - > moveHistory [ battlerId ] , 0 , sizeof ( BATTLE_HISTORY - > moveHistory [ battlerId ] ) ) ;
BATTLE_HISTORY - > moveHistoryIndex [ battlerId ] = 0 ;
}
void RecordLastUsedMoveBy ( u32 battlerId , u32 move )
{
u8 * index = & BATTLE_HISTORY - > moveHistoryIndex [ battlerId ] ;
if ( + + ( * index ) > = AI_MOVE_HISTORY_COUNT )
* index = 0 ;
BATTLE_HISTORY - > moveHistory [ battlerId ] [ * index ] = move ;
}
void RecordKnownMove ( u8 battlerId , u32 move )
{
s32 i ;
for ( i = 0 ; i < MAX_MON_MOVES ; i + + )
{
if ( BATTLE_HISTORY - > usedMoves [ battlerId ] [ i ] = = move )
break ;
if ( BATTLE_HISTORY - > usedMoves [ battlerId ] [ i ] = = MOVE_NONE )
{
BATTLE_HISTORY - > usedMoves [ battlerId ] [ i ] = move ;
2022-08-23 01:07:25 +02:00
AI_PARTY - > mons [ GetBattlerSide ( battlerId ) ] [ gBattlerPartyIndexes [ battlerId ] ] . moves [ i ] = move ;
2020-12-11 16:05:00 +01:00
break ;
}
}
}
void RecordAbilityBattle ( u8 battlerId , u16 abilityId )
{
BATTLE_HISTORY - > abilities [ battlerId ] = abilityId ;
2022-08-23 01:07:25 +02:00
AI_PARTY - > mons [ GetBattlerSide ( battlerId ) ] [ gBattlerPartyIndexes [ battlerId ] ] . ability = abilityId ;
2020-12-11 16:05:00 +01:00
}
void ClearBattlerAbilityHistory ( u8 battlerId )
{
BATTLE_HISTORY - > abilities [ battlerId ] = ABILITY_NONE ;
}
void RecordItemEffectBattle ( u8 battlerId , u8 itemEffect )
{
BATTLE_HISTORY - > itemEffects [ battlerId ] = itemEffect ;
2022-08-23 01:07:25 +02:00
AI_PARTY - > mons [ GetBattlerSide ( battlerId ) ] [ gBattlerPartyIndexes [ battlerId ] ] . heldEffect = itemEffect ;
2020-12-11 16:05:00 +01:00
}
void ClearBattlerItemEffectHistory ( u8 battlerId )
{
BATTLE_HISTORY - > itemEffects [ battlerId ] = 0 ;
}
void SaveBattlerData ( u8 battlerId )
{
if ( ! IsBattlerAIControlled ( battlerId ) )
{
u32 i ;
AI_THINKING_STRUCT - > saved [ battlerId ] . ability = gBattleMons [ battlerId ] . ability ;
AI_THINKING_STRUCT - > saved [ battlerId ] . heldItem = gBattleMons [ battlerId ] . item ;
AI_THINKING_STRUCT - > saved [ battlerId ] . species = gBattleMons [ battlerId ] . species ;
for ( i = 0 ; i < 4 ; i + + )
AI_THINKING_STRUCT - > saved [ battlerId ] . moves [ i ] = gBattleMons [ battlerId ] . moves [ i ] ;
}
}
void SetBattlerData ( u8 battlerId )
{
if ( ! IsBattlerAIControlled ( battlerId ) )
{
struct Pokemon * illusionMon ;
u32 i ;
// Use the known battler's ability.
if ( BATTLE_HISTORY - > abilities [ battlerId ] ! = ABILITY_NONE )
gBattleMons [ battlerId ] . ability = BATTLE_HISTORY - > abilities [ battlerId ] ;
// Check if mon can only have one ability.
else if ( gBaseStats [ gBattleMons [ battlerId ] . species ] . abilities [ 1 ] = = ABILITY_NONE
| | gBaseStats [ gBattleMons [ battlerId ] . species ] . abilities [ 1 ] = = gBaseStats [ gBattleMons [ battlerId ] . species ] . abilities [ 0 ] )
gBattleMons [ battlerId ] . ability = gBaseStats [ gBattleMons [ battlerId ] . species ] . abilities [ 0 ] ;
// The ability is unknown.
else
gBattleMons [ battlerId ] . ability = ABILITY_NONE ;
if ( BATTLE_HISTORY - > itemEffects [ battlerId ] = = 0 )
gBattleMons [ battlerId ] . item = 0 ;
for ( i = 0 ; i < 4 ; i + + )
{
if ( BATTLE_HISTORY - > usedMoves [ battlerId ] [ i ] = = 0 )
gBattleMons [ battlerId ] . moves [ i ] = 0 ;
}
// Simulate Illusion
if ( ( illusionMon = GetIllusionMonPtr ( battlerId ) ) ! = NULL )
gBattleMons [ battlerId ] . species = GetMonData ( illusionMon , MON_DATA_SPECIES2 ) ;
}
}
void RestoreBattlerData ( u8 battlerId )
{
if ( ! IsBattlerAIControlled ( battlerId ) )
{
u32 i ;
gBattleMons [ battlerId ] . ability = AI_THINKING_STRUCT - > saved [ battlerId ] . ability ;
gBattleMons [ battlerId ] . item = AI_THINKING_STRUCT - > saved [ battlerId ] . heldItem ;
gBattleMons [ battlerId ] . species = AI_THINKING_STRUCT - > saved [ battlerId ] . species ;
for ( i = 0 ; i < 4 ; i + + )
gBattleMons [ battlerId ] . moves [ i ] = AI_THINKING_STRUCT - > saved [ battlerId ] . moves [ i ] ;
}
}
2020-12-11 06:10:21 +01:00
u32 GetHealthPercentage ( u8 battlerId )
{
2020-12-13 23:02:21 +01:00
return ( u32 ) ( ( 100 * gBattleMons [ battlerId ] . hp ) / gBattleMons [ battlerId ] . maxHP ) ;
2020-12-20 05:58:23 +01:00
}
bool32 AtMaxHp ( u8 battlerId )
{
2022-01-13 17:28:27 +01:00
if ( AI_DATA - > hpPercents [ battlerId ] = = 100 )
2020-12-20 05:58:23 +01:00
return TRUE ;
return FALSE ;
}
2020-12-11 06:10:21 +01:00
2020-12-13 23:02:21 +01:00
bool32 IsBattlerTrapped ( u8 battler , bool8 checkSwitch )
2020-12-11 06:10:21 +01:00
{
2022-01-13 17:28:27 +01:00
u8 holdEffect = AI_DATA - > holdEffects [ battler ] ;
2022-08-24 03:00:08 +02:00
# if B_GHOSTS_ESCAPE >= GEN_6
if ( IS_BATTLER_OF_TYPE ( battler , TYPE_GHOST ) )
2020-12-13 23:02:21 +01:00
return FALSE ;
2022-08-24 03:00:08 +02:00
# endif
if ( checkSwitch & & holdEffect = = HOLD_EFFECT_SHED_SHELL )
return FALSE ;
else if ( ! checkSwitch & & GetBattlerAbility ( battler ) = = ABILITY_RUN_AWAY )
return FALSE ;
else if ( ! checkSwitch & & holdEffect = = HOLD_EFFECT_CAN_ALWAYS_RUN )
return FALSE ;
else if ( gBattleMons [ battler ] . status2 & ( STATUS2_ESCAPE_PREVENTION | STATUS2_WRAPPED ) )
return TRUE ;
else if ( gStatuses3 [ battler ] & ( STATUS3_ROOTED | STATUS3_SKY_DROPPED ) )
return TRUE ;
else if ( gFieldStatuses & STATUS_FIELD_FAIRY_LOCK )
return TRUE ;
else if ( IsAbilityPreventingEscape ( battler ) )
return TRUE ;
2020-12-11 06:10:21 +01:00
2020-12-13 23:02:21 +01:00
return FALSE ;
2020-12-11 16:05:00 +01:00
}
2020-12-20 22:47:20 +01:00
u32 GetTotalBaseStat ( u32 species )
{
return gBaseStats [ species ] . baseHP
+ gBaseStats [ species ] . baseAttack
+ gBaseStats [ species ] . baseDefense
+ gBaseStats [ species ] . baseSpeed
+ gBaseStats [ species ] . baseSpAttack
+ gBaseStats [ species ] . baseSpDefense ;
}
bool32 IsTruantMonVulnerable ( u32 battlerAI , u32 opposingBattler )
{
int i ;
for ( i = 0 ; i < MAX_MON_MOVES ; i + + )
{
u32 move = gBattleResources - > battleHistory - > usedMoves [ opposingBattler ] [ i ] ;
if ( gBattleMoves [ move ] . effect = = EFFECT_PROTECT & & move ! = MOVE_ENDURE )
return TRUE ;
2022-06-05 17:09:04 +02:00
if ( gBattleMoves [ move ] . effect = = EFFECT_SEMI_INVULNERABLE & & AI_WhoStrikesFirst ( battlerAI , opposingBattler , GetAIChosenMove ( battlerAI ) ) = = AI_IS_SLOWER )
2020-12-20 22:47:20 +01:00
return TRUE ;
}
return FALSE ;
}
2020-12-16 05:57:33 +01:00
// move checks
2020-12-20 05:58:23 +01:00
bool32 IsAffectedByPowder ( u8 battler , u16 ability , u16 holdEffect )
{
2022-08-24 03:00:08 +02:00
if ( ability = = ABILITY_OVERCOAT
# if B_POWDER_GRASS >= GEN_6
| | IS_BATTLER_OF_TYPE ( battler , TYPE_GRASS )
# endif
| | holdEffect = = HOLD_EFFECT_SAFETY_GOGGLES )
2020-12-20 05:58:23 +01:00
return FALSE ;
return TRUE ;
}
2020-12-17 06:56:10 +01:00
// This function checks if all physical/special moves are either unusable or unreasonable to use.
// Consider a pokemon boosting their attack against a ghost pokemon having only normal-type physical attacks.
bool32 MovesWithSplitUnusable ( u32 attacker , u32 target , u32 split )
{
s32 i , moveType ;
u32 usable = 0 ;
2022-01-13 17:28:27 +01:00
u32 unusable = AI_DATA - > moveLimitations [ attacker ] ;
2020-12-17 06:56:10 +01:00
u16 * moves = GetMovesArray ( attacker ) ;
for ( i = 0 ; i < MAX_MON_MOVES ; i + + )
{
if ( moves [ i ] ! = MOVE_NONE
2021-11-08 11:57:16 +01:00
& & moves [ i ] ! = 0xFFFF
& & GetBattleMoveSplit ( moves [ i ] ) = = split
& & ! ( unusable & gBitTable [ i ] ) )
2020-12-17 06:56:10 +01:00
{
SetTypeBeforeUsingMove ( moves [ i ] , attacker ) ;
GET_MOVE_TYPE ( moves [ i ] , moveType ) ;
if ( CalcTypeEffectivenessMultiplier ( moves [ i ] , moveType , attacker , target , FALSE ) ! = 0 )
usable | = gBitTable [ i ] ;
}
}
return ( usable = = 0 ) ;
}
2020-12-16 05:57:33 +01:00
static bool32 AI_GetIfCrit ( u32 move , u8 battlerAtk , u8 battlerDef )
{
bool32 isCrit ;
switch ( CalcCritChanceStage ( battlerAtk , battlerDef , move , FALSE ) )
{
case - 1 :
case 0 :
default :
isCrit = FALSE ;
break ;
case 1 :
if ( gBattleMoves [ move ] . flags & FLAG_HIGH_CRIT & & ( Random ( ) % 5 = = 0 ) )
isCrit = TRUE ;
else
isCrit = FALSE ;
break ;
case 2 :
if ( gBattleMoves [ move ] . flags & FLAG_HIGH_CRIT & & ( Random ( ) % 2 = = 0 ) )
isCrit = TRUE ;
else if ( ! ( gBattleMoves [ move ] . flags & FLAG_HIGH_CRIT ) & & ( Random ( ) % 4 ) = = 0 )
isCrit = TRUE ;
else
isCrit = FALSE ;
break ;
case - 2 :
case 3 :
case 4 :
isCrit = TRUE ;
break ;
}
return isCrit ;
}
2022-07-17 18:40:43 +02:00
s32 AI_CalcDamage ( u16 move , u8 battlerAtk , u8 battlerDef , u8 * typeEffectiveness , bool32 considerZPower )
2020-12-16 05:57:33 +01:00
{
2021-09-22 13:15:43 +02:00
s32 dmg , moveType , critDmg , normalDmg ;
s8 critChance ;
2022-01-13 17:28:27 +01:00
u16 effectivenessMultiplier ;
2020-12-16 05:57:33 +01:00
2021-05-24 18:46:53 +02:00
if ( considerZPower & & IsViableZMove ( battlerAtk , move ) )
{
2022-07-17 18:40:43 +02:00
//temporarily enable z moves for damage calcs
2021-05-24 18:46:53 +02:00
gBattleStruct - > zmove . baseMoves [ battlerAtk ] = move ;
2022-07-17 18:40:43 +02:00
gBattleStruct - > zmove . active = TRUE ;
2021-05-24 18:46:53 +02:00
}
2020-12-16 05:57:33 +01:00
SaveBattlerData ( battlerAtk ) ;
SaveBattlerData ( battlerDef ) ;
SetBattlerData ( battlerAtk ) ;
SetBattlerData ( battlerDef ) ;
gBattleStruct - > dynamicMoveType = 0 ;
SetTypeBeforeUsingMove ( move , battlerAtk ) ;
GET_MOVE_TYPE ( move , moveType ) ;
2022-07-03 23:23:59 +02:00
if ( gBattleMoves [ move ] . power )
{
critChance = GetInverseCritChance ( battlerAtk , battlerDef , move ) ;
normalDmg = CalculateMoveDamageAndEffectiveness ( move , battlerAtk , battlerDef , moveType , & effectivenessMultiplier ) ;
critDmg = CalculateMoveDamage ( move , battlerAtk , battlerDef , moveType , 0 , TRUE , FALSE , FALSE ) ;
2021-09-22 13:15:43 +02:00
2022-07-03 23:23:59 +02:00
if ( critChance = = - 1 )
dmg = normalDmg ;
else
dmg = ( critDmg + normalDmg * ( critChance - 1 ) ) / critChance ;
2020-12-16 05:57:33 +01:00
2022-07-03 23:23:59 +02:00
// Handle dynamic move damage
switch ( gBattleMoves [ move ] . effect )
{
case EFFECT_LEVEL_DAMAGE :
case EFFECT_PSYWAVE :
dmg = gBattleMons [ battlerAtk ] . level * ( AI_DATA - > abilities [ battlerAtk ] = = ABILITY_PARENTAL_BOND ? 2 : 1 ) ;
break ;
case EFFECT_DRAGON_RAGE :
dmg = 40 * ( AI_DATA - > abilities [ battlerAtk ] = = ABILITY_PARENTAL_BOND ? 2 : 1 ) ;
break ;
case EFFECT_SONICBOOM :
dmg = 20 * ( AI_DATA - > abilities [ battlerAtk ] = = ABILITY_PARENTAL_BOND ? 2 : 1 ) ;
break ;
case EFFECT_MULTI_HIT :
dmg * = ( AI_DATA - > abilities [ battlerAtk ] = = ABILITY_SKILL_LINK ? 5 : 3 ) ;
break ;
case EFFECT_TRIPLE_KICK :
dmg * = ( AI_DATA - > abilities [ battlerAtk ] = = ABILITY_SKILL_LINK ? 6 : 5 ) ;
break ;
case EFFECT_ENDEAVOR :
// If target has less HP than user, Endeavor does no damage
dmg = max ( 0 , gBattleMons [ battlerDef ] . hp - gBattleMons [ battlerAtk ] . hp ) ;
break ;
case EFFECT_SUPER_FANG :
dmg = ( AI_DATA - > abilities [ battlerAtk ] = = ABILITY_PARENTAL_BOND
? max ( 2 , gBattleMons [ battlerDef ] . hp * 3 / 4 )
: max ( 1 , gBattleMons [ battlerDef ] . hp / 2 ) ) ;
break ;
case EFFECT_FINAL_GAMBIT :
dmg = gBattleMons [ battlerAtk ] . hp ;
break ;
}
2020-12-16 05:57:33 +01:00
2022-07-03 23:23:59 +02:00
// Handle other multi-strike moves
if ( gBattleMoves [ move ] . flags & FLAG_TWO_STRIKES )
dmg * = 2 ;
else if ( move = = MOVE_SURGING_STRIKES | | ( move = = MOVE_WATER_SHURIKEN & & gBattleMons [ battlerAtk ] . species = = SPECIES_GRENINJA_ASH ) )
dmg * = 3 ;
2022-08-23 01:07:25 +02:00
2022-07-03 23:23:59 +02:00
if ( dmg = = 0 )
dmg = 1 ;
}
else
2021-02-07 20:39:23 +01:00
{
2022-07-03 23:23:59 +02:00
dmg = 0 ;
2021-02-07 20:39:23 +01:00
}
2020-12-16 05:57:33 +01:00
RestoreBattlerData ( battlerAtk ) ;
RestoreBattlerData ( battlerDef ) ;
2022-08-23 01:07:25 +02:00
2022-01-13 17:28:27 +01:00
// convert multiper to AI_EFFECTIVENESS_xX
* typeEffectiveness = AI_GetEffectiveness ( effectivenessMultiplier ) ;
2020-12-16 05:57:33 +01:00
2021-05-24 18:46:53 +02:00
gBattleStruct - > zmove . active = FALSE ;
gBattleStruct - > zmove . baseMoves [ battlerAtk ] = MOVE_NONE ;
2020-12-16 05:57:33 +01:00
return dmg ;
}
2020-12-11 16:05:00 +01:00
// Checks if one of the moves has side effects or perks
static u32 WhichMoveBetter ( u32 move1 , u32 move2 )
{
2022-01-13 17:28:27 +01:00
s32 defAbility = AI_DATA - > abilities [ gBattlerTarget ] ;
2020-12-11 16:05:00 +01:00
// Check if physical moves hurt.
2022-01-13 17:28:27 +01:00
if ( AI_DATA - > holdEffects [ sBattler_AI ] ! = HOLD_EFFECT_PROTECTIVE_PADS
2020-12-11 16:05:00 +01:00
& & ( BATTLE_HISTORY - > itemEffects [ gBattlerTarget ] = = HOLD_EFFECT_ROCKY_HELMET
| | defAbility = = ABILITY_IRON_BARBS | | defAbility = = ABILITY_ROUGH_SKIN ) )
{
if ( IS_MOVE_PHYSICAL ( move1 ) & & ! IS_MOVE_PHYSICAL ( move2 ) )
return 1 ;
if ( IS_MOVE_PHYSICAL ( move2 ) & & ! IS_MOVE_PHYSICAL ( move1 ) )
return 0 ;
}
// Check recoil
if ( GetBattlerAbility ( sBattler_AI ) ! = ABILITY_ROCK_HEAD )
{
if ( ( ( gBattleMoves [ move1 ] . effect = = EFFECT_RECOIL_25
| | gBattleMoves [ move1 ] . effect = = EFFECT_RECOIL_IF_MISS
| | gBattleMoves [ move1 ] . effect = = EFFECT_RECOIL_50
| | gBattleMoves [ move1 ] . effect = = EFFECT_RECOIL_33
| | gBattleMoves [ move1 ] . effect = = EFFECT_RECOIL_33_STATUS )
& & ( gBattleMoves [ move2 ] . effect ! = EFFECT_RECOIL_25
& & gBattleMoves [ move2 ] . effect ! = EFFECT_RECOIL_IF_MISS
& & gBattleMoves [ move2 ] . effect ! = EFFECT_RECOIL_50
& & gBattleMoves [ move2 ] . effect ! = EFFECT_RECOIL_33
& & gBattleMoves [ move2 ] . effect ! = EFFECT_RECOIL_33_STATUS
& & gBattleMoves [ move2 ] . effect ! = EFFECT_RECHARGE ) ) )
return 1 ;
if ( ( ( gBattleMoves [ move2 ] . effect = = EFFECT_RECOIL_25
| | gBattleMoves [ move2 ] . effect = = EFFECT_RECOIL_IF_MISS
| | gBattleMoves [ move2 ] . effect = = EFFECT_RECOIL_50
| | gBattleMoves [ move2 ] . effect = = EFFECT_RECOIL_33
| | gBattleMoves [ move2 ] . effect = = EFFECT_RECOIL_33_STATUS )
& & ( gBattleMoves [ move1 ] . effect ! = EFFECT_RECOIL_25
& & gBattleMoves [ move1 ] . effect ! = EFFECT_RECOIL_IF_MISS
& & gBattleMoves [ move1 ] . effect ! = EFFECT_RECOIL_50
& & gBattleMoves [ move1 ] . effect ! = EFFECT_RECOIL_33
& & gBattleMoves [ move1 ] . effect ! = EFFECT_RECOIL_33_STATUS
& & gBattleMoves [ move1 ] . effect ! = EFFECT_RECHARGE ) ) )
return 0 ;
}
// Check recharge
if ( gBattleMoves [ move1 ] . effect = = EFFECT_RECHARGE & & gBattleMoves [ move2 ] . effect ! = EFFECT_RECHARGE )
return 1 ;
if ( gBattleMoves [ move2 ] . effect = = EFFECT_RECHARGE & & gBattleMoves [ move1 ] . effect ! = EFFECT_RECHARGE )
return 0 ;
// Check additional effect.
if ( gBattleMoves [ move1 ] . effect = = 0 & & gBattleMoves [ move2 ] . effect ! = 0 )
return 1 ;
if ( gBattleMoves [ move2 ] . effect = = 0 & & gBattleMoves [ move1 ] . effect ! = 0 )
return 0 ;
return 2 ;
}
2020-12-16 05:57:33 +01:00
u8 GetMoveDamageResult ( u16 move )
2020-12-11 16:05:00 +01:00
{
s32 i , checkedMove , bestId , currId , hp ;
s32 moveDmgs [ MAX_MON_MOVES ] ;
u8 result ;
2021-08-12 01:33:10 +02:00
for ( i = 0 ; sIgnoredPowerfulMoveEffects [ i ] ! = IGNORED_MOVES_END ; i + + )
2020-12-11 16:05:00 +01:00
{
2021-08-12 01:33:10 +02:00
if ( gBattleMoves [ move ] . effect = = sIgnoredPowerfulMoveEffects [ i ] )
2020-12-11 16:05:00 +01:00
break ;
}
2021-08-12 01:33:10 +02:00
if ( gBattleMoves [ move ] . power ! = 0 & & sIgnoredPowerfulMoveEffects [ i ] = = IGNORED_MOVES_END )
2020-12-11 16:05:00 +01:00
{
2021-08-12 01:33:10 +02:00
// Considered move has power and is not in sIgnoredPowerfulMoveEffects
// Check all other moves and calculate their power
2020-12-11 16:05:00 +01:00
for ( checkedMove = 0 ; checkedMove < MAX_MON_MOVES ; checkedMove + + )
{
2021-08-12 01:33:10 +02:00
for ( i = 0 ; sIgnoredPowerfulMoveEffects [ i ] ! = IGNORED_MOVES_END ; i + + )
2020-12-11 16:05:00 +01:00
{
2021-08-12 01:33:10 +02:00
if ( gBattleMoves [ gBattleMons [ sBattler_AI ] . moves [ checkedMove ] ] . effect = = sIgnoredPowerfulMoveEffects [ i ] )
2020-12-11 16:05:00 +01:00
break ;
}
if ( gBattleMons [ sBattler_AI ] . moves [ checkedMove ] ! = MOVE_NONE
2021-08-12 01:33:10 +02:00
& & sIgnoredPowerfulMoveEffects [ i ] = = IGNORED_MOVES_END
2020-12-11 16:05:00 +01:00
& & gBattleMoves [ gBattleMons [ sBattler_AI ] . moves [ checkedMove ] ] . power ! = 0 )
{
2022-01-13 17:28:27 +01:00
moveDmgs [ checkedMove ] = AI_DATA - > simulatedDmg [ sBattler_AI ] [ gBattlerTarget ] [ checkedMove ] ;
2020-12-11 16:05:00 +01:00
}
else
{
moveDmgs [ checkedMove ] = 0 ;
}
}
hp = gBattleMons [ gBattlerTarget ] . hp + ( 20 * gBattleMons [ gBattlerTarget ] . hp / 100 ) ; // 20 % add to make sure the battler is always fainted
// If a move can faint battler, it doesn't matter how much damage it does
for ( i = 0 ; i < MAX_MON_MOVES ; i + + )
{
if ( moveDmgs [ i ] > hp )
moveDmgs [ i ] = hp ;
}
for ( bestId = 0 , i = 1 ; i < MAX_MON_MOVES ; i + + )
{
if ( moveDmgs [ i ] > moveDmgs [ bestId ] )
bestId = i ;
if ( moveDmgs [ i ] = = moveDmgs [ bestId ] )
{
switch ( WhichMoveBetter ( gBattleMons [ sBattler_AI ] . moves [ bestId ] , gBattleMons [ sBattler_AI ] . moves [ i ] ) )
{
case 2 :
if ( Random ( ) & 1 )
break ;
case 1 :
bestId = i ;
break ;
}
}
}
currId = AI_THINKING_STRUCT - > movesetIndex ;
if ( currId = = bestId )
AI_THINKING_STRUCT - > funcResult = MOVE_POWER_BEST ;
// Compare percentage difference.
else if ( ( moveDmgs [ currId ] > = hp | | moveDmgs [ bestId ] < hp ) // If current move can faint as well, or if neither can
& & ( moveDmgs [ bestId ] * 100 / hp ) - ( moveDmgs [ currId ] * 100 / hp ) < = 30
& & WhichMoveBetter ( gBattleMons [ sBattler_AI ] . moves [ bestId ] , gBattleMons [ sBattler_AI ] . moves [ currId ] ) ! = 0 )
AI_THINKING_STRUCT - > funcResult = MOVE_POWER_GOOD ;
else
AI_THINKING_STRUCT - > funcResult = MOVE_POWER_WEAK ;
}
else
{
2021-08-12 01:33:10 +02:00
// Move has a power of 0/1, or is in the group sIgnoredPowerfulMoveEffects
AI_THINKING_STRUCT - > funcResult = MOVE_POWER_OTHER ;
2020-12-11 16:05:00 +01:00
}
return AI_THINKING_STRUCT - > funcResult ;
}
2021-01-05 03:39:59 +01:00
u32 GetCurrDamageHpPercent ( u8 battlerAtk , u8 battlerDef )
{
2022-01-13 17:28:27 +01:00
int bestDmg = AI_DATA - > simulatedDmg [ battlerAtk ] [ battlerDef ] [ AI_THINKING_STRUCT - > movesetIndex ] ;
2021-01-05 03:39:59 +01:00
return ( bestDmg * 100 ) / gBattleMons [ battlerDef ] . maxHP ;
}
2020-12-11 16:05:00 +01:00
u16 AI_GetTypeEffectiveness ( u16 move , u8 battlerAtk , u8 battlerDef )
{
2021-01-05 04:23:09 +01:00
u16 typeEffectiveness , moveType ;
2020-12-11 16:05:00 +01:00
2021-01-05 04:23:09 +01:00
SaveBattlerData ( battlerAtk ) ;
SaveBattlerData ( battlerDef ) ;
2020-12-11 16:05:00 +01:00
2021-01-05 04:23:09 +01:00
SetBattlerData ( battlerAtk ) ;
SetBattlerData ( battlerDef ) ;
gBattleStruct - > dynamicMoveType = 0 ;
SetTypeBeforeUsingMove ( move , battlerAtk ) ;
GET_MOVE_TYPE ( move , moveType ) ;
typeEffectiveness = CalcTypeEffectivenessMultiplier ( move , moveType , battlerAtk , battlerDef , FALSE ) ;
RestoreBattlerData ( battlerAtk ) ;
RestoreBattlerData ( battlerDef ) ;
return typeEffectiveness ;
2020-12-11 16:05:00 +01:00
}
2022-05-29 03:41:59 +02:00
u32 AI_GetMoveEffectiveness ( u16 move , u8 battlerAtk , u8 battlerDef )
2020-12-11 16:05:00 +01:00
{
gMoveResultFlags = 0 ;
2022-01-13 17:28:27 +01:00
return AI_GetEffectiveness ( AI_GetTypeEffectiveness ( move , battlerAtk , battlerDef ) ) ;
}
2021-06-10 12:55:05 +02:00
2022-07-03 22:20:05 +02:00
static u32 AI_GetEffectiveness ( u16 multiplier )
2022-01-13 17:28:27 +01:00
{
switch ( multiplier )
2020-12-11 16:05:00 +01:00
{
case UQ_4_12 ( 0.0 ) :
2022-01-13 17:28:27 +01:00
return AI_EFFECTIVENESS_x0 ;
2022-05-29 03:41:59 +02:00
case UQ_4_12 ( 0.125 ) :
2022-07-03 22:20:05 +02:00
return AI_EFFECTIVENESS_x0_125 ;
2020-12-11 16:05:00 +01:00
case UQ_4_12 ( 0.25 ) :
2022-01-13 17:28:27 +01:00
return AI_EFFECTIVENESS_x0_25 ;
2020-12-11 16:05:00 +01:00
case UQ_4_12 ( 0.5 ) :
2022-01-13 17:28:27 +01:00
return AI_EFFECTIVENESS_x0_5 ;
2020-12-11 16:05:00 +01:00
case UQ_4_12 ( 1.0 ) :
2022-07-03 22:20:05 +02:00
default :
2022-01-13 17:28:27 +01:00
return AI_EFFECTIVENESS_x1 ;
2020-12-11 16:05:00 +01:00
case UQ_4_12 ( 2.0 ) :
2022-01-13 17:28:27 +01:00
return AI_EFFECTIVENESS_x2 ;
2020-12-11 16:05:00 +01:00
case UQ_4_12 ( 4.0 ) :
2022-01-13 17:28:27 +01:00
return AI_EFFECTIVENESS_x4 ;
2022-05-29 03:41:59 +02:00
case UQ_4_12 ( 8.0 ) :
2022-07-03 22:20:05 +02:00
return AI_EFFECTIVENESS_x8 ;
2020-12-11 16:05:00 +01:00
}
}
2021-09-28 03:03:27 +02:00
/* Checks to see if AI will move ahead of another battler
* Output :
* AI_IS_FASTER : is user ( ai ) faster
* AI_IS_SLOWER : is target faster
*/
2022-06-05 17:09:04 +02:00
u8 AI_WhoStrikesFirst ( u8 battlerAI , u8 battler2 , u16 moveConsidered )
2020-12-11 16:05:00 +01:00
{
u32 fasterAI = 0 , fasterPlayer = 0 , i ;
2021-09-28 03:03:27 +02:00
s8 prioAI = 0 ;
s8 prioPlayer = 0 ;
2022-06-05 17:09:04 +02:00
s8 prioBattler2 = 0 ;
u16 * battler2Moves = GetMovesArray ( battler2 ) ;
2020-12-11 16:05:00 +01:00
// Check move priorities first.
2022-06-05 17:09:04 +02:00
prioAI = GetMovePriority ( battlerAI , moveConsidered ) ;
2020-12-11 16:05:00 +01:00
for ( i = 0 ; i < MAX_MON_MOVES ; i + + )
{
2022-06-05 17:09:04 +02:00
if ( battler2Moves [ i ] = = 0 | | battler2Moves [ i ] = = 0xFFFF )
2020-12-11 16:05:00 +01:00
continue ;
2022-06-05 17:09:04 +02:00
prioBattler2 = GetMovePriority ( battler2 , battler2Moves [ i ] ) ;
if ( prioAI > prioBattler2 )
2020-12-11 16:05:00 +01:00
fasterAI + + ;
2022-06-05 17:09:04 +02:00
else if ( prioBattler2 > prioAI )
2020-12-11 16:05:00 +01:00
fasterPlayer + + ;
}
if ( fasterAI > fasterPlayer )
{
2021-09-28 03:03:27 +02:00
return AI_IS_FASTER ;
2020-12-11 16:05:00 +01:00
}
else if ( fasterAI < fasterPlayer )
{
2021-09-28 03:03:27 +02:00
return AI_IS_SLOWER ;
2020-12-11 16:05:00 +01:00
}
else
{
2022-06-05 17:09:04 +02:00
if ( prioAI > prioBattler2 )
return AI_IS_FASTER ; // if we didn't know any of battler 2's moves to compare priorities, assume they don't have a prio+ move
2020-12-11 16:05:00 +01:00
// Priorities are the same(at least comparing to moves the AI is aware of), decide by speed.
2021-09-28 03:03:27 +02:00
if ( GetWhoStrikesFirst ( battlerAI , battler2 , TRUE ) = = 0 )
2022-02-09 17:23:30 +01:00
return AI_IS_FASTER ;
2020-12-11 16:05:00 +01:00
else
2022-02-09 17:23:30 +01:00
return AI_IS_SLOWER ;
2020-12-11 16:05:00 +01:00
}
}
// Check if target has means to faint ai mon.
2020-12-13 23:02:21 +01:00
bool32 CanTargetFaintAi ( u8 battlerDef , u8 battlerAtk )
2020-12-11 16:05:00 +01:00
{
s32 i , dmg ;
2022-01-13 17:28:27 +01:00
u32 unusable = AI_DATA - > moveLimitations [ battlerDef ] ;
2020-12-13 23:02:21 +01:00
u16 * moves = gBattleResources - > battleHistory - > usedMoves [ battlerDef ] ;
2020-12-11 16:05:00 +01:00
for ( i = 0 ; i < MAX_MON_MOVES ; i + + )
{
if ( moves [ i ] ! = MOVE_NONE & & moves [ i ] ! = 0xFFFF & & ! ( unusable & gBitTable [ i ] )
2022-01-13 17:28:27 +01:00
& & AI_DATA - > simulatedDmg [ battlerDef ] [ battlerAtk ] [ moves [ i ] ] > = gBattleMons [ battlerAtk ] . hp )
2020-12-11 16:05:00 +01:00
{
return TRUE ;
}
}
return FALSE ;
}
2021-11-08 08:37:28 +01:00
// Check if AI mon has the means to faint the target with any of its moves.
// If numHits > 1, check if the target will be KO'ed by that number of hits (ignoring healing effects)
bool32 CanAIFaintTarget ( u8 battlerAtk , u8 battlerDef , u8 numHits )
{
s32 i , dmg ;
2022-01-13 17:28:27 +01:00
u32 moveLimitations = AI_DATA - > moveLimitations [ battlerAtk ] ;
2021-11-08 08:37:28 +01:00
u16 * moves = gBattleMons [ battlerAtk ] . moves ;
for ( i = 0 ; i < MAX_MON_MOVES ; i + + )
{
2021-11-08 11:57:16 +01:00
if ( moves [ i ] ! = MOVE_NONE & & moves [ i ] ! = 0xFFFF & & ! ( moveLimitations & gBitTable [ i ] ) )
2021-11-08 08:37:28 +01:00
{
2021-11-08 11:57:16 +01:00
// Use the pre-calculated value in simulatedDmg instead of re-calculating it
2022-01-13 17:28:27 +01:00
dmg = AI_DATA - > simulatedDmg [ battlerAtk ] [ battlerDef ] [ i ] ;
2021-11-08 08:37:28 +01:00
2021-11-08 11:57:16 +01:00
if ( numHits )
dmg * = numHits ;
if ( gBattleMons [ battlerDef ] . hp < = dmg )
return TRUE ;
}
2021-11-08 08:37:28 +01:00
}
return FALSE ;
}
2020-12-20 05:58:23 +01:00
bool32 CanMoveFaintBattler ( u16 move , u8 battlerDef , u8 battlerAtk , u8 nHits )
{
s32 i , dmg ;
2022-01-13 17:28:27 +01:00
u8 effectiveness ;
u32 unusable = AI_DATA - > moveLimitations [ battlerDef ] ;
2020-12-20 05:58:23 +01:00
2022-01-13 17:28:27 +01:00
if ( move ! = MOVE_NONE
& & move ! = 0xFFFF
& & ! ( unusable & gBitTable [ i ] )
2022-07-17 18:40:43 +02:00
& & AI_CalcDamage ( move , battlerDef , battlerAtk , & effectiveness , FALSE ) > = gBattleMons [ battlerAtk ] . hp )
2020-12-20 05:58:23 +01:00
return TRUE ;
return FALSE ;
}
2020-12-16 05:57:33 +01:00
// Check if target has means to faint ai mon after modding hp/dmg
bool32 CanTargetFaintAiWithMod ( u8 battlerDef , u8 battlerAtk , s32 hpMod , s32 dmgMod )
{
u32 i ;
2022-01-13 17:28:27 +01:00
u32 unusable = AI_DATA - > moveLimitations [ battlerDef ] ;
2021-11-11 22:16:26 +01:00
s32 dmg ;
2020-12-16 05:57:33 +01:00
u16 * moves = gBattleResources - > battleHistory - > usedMoves [ battlerDef ] ;
2021-09-28 03:03:27 +02:00
u32 hpCheck = gBattleMons [ battlerAtk ] . hp + hpMod ;
if ( hpCheck > gBattleMons [ battlerAtk ] . maxHP )
hpCheck = gBattleMons [ battlerAtk ] . maxHP ;
2020-12-16 05:57:33 +01:00
for ( i = 0 ; i < MAX_MON_MOVES ; i + + )
{
2022-01-13 17:28:27 +01:00
dmg = AI_DATA - > simulatedDmg [ battlerAtk ] [ battlerDef ] [ i ] ;
2020-12-20 05:58:23 +01:00
if ( dmgMod )
dmg * = dmgMod ;
2021-06-10 12:55:05 +02:00
2020-12-16 05:57:33 +01:00
if ( moves [ i ] ! = MOVE_NONE & & moves [ i ] ! = 0xFFFF & & ! ( unusable & gBitTable [ i ] ) & & dmg > = hpCheck )
{
return TRUE ;
}
}
return FALSE ;
}
2021-11-04 15:43:33 +01:00
bool32 AI_IsAbilityOnSide ( u32 battlerId , u32 ability )
2021-11-04 02:36:33 +01:00
{
2022-01-13 17:28:27 +01:00
if ( IsBattlerAlive ( battlerId ) & & AI_DATA - > abilities [ battlerId ] = = ability )
2021-11-04 15:43:33 +01:00
return TRUE ;
2022-01-13 17:28:27 +01:00
else if ( IsBattlerAlive ( BATTLE_PARTNER ( battlerId ) ) & & AI_DATA - > abilities [ BATTLE_PARTNER ( battlerId ) ] = = ability )
2021-11-04 15:43:33 +01:00
return TRUE ;
2021-11-04 02:36:33 +01:00
else
2021-11-04 15:43:33 +01:00
return FALSE ;
2021-11-04 02:36:33 +01:00
}
2020-12-13 23:02:21 +01:00
// does NOT include ability suppression checks
s32 AI_GetAbility ( u32 battlerId )
2020-12-11 16:05:00 +01:00
{
2021-10-31 22:19:30 +01:00
u32 knownAbility = GetBattlerAbility ( battlerId ) ;
2022-08-23 01:07:25 +02:00
2022-09-24 18:12:31 +02:00
// We've had ability overwritten by e.g. Worry Seed. It is not part of AI_PARTY in case of switching
if ( gBattleStruct - > overwrittenAbilities [ battlerId ] )
return gBattleStruct - > overwrittenAbilities [ battlerId ] ;
2020-12-11 16:05:00 +01:00
// The AI knows its own ability.
if ( IsBattlerAIControlled ( battlerId ) )
2021-10-31 22:19:30 +01:00
return knownAbility ;
2022-08-23 01:07:25 +02:00
2021-10-31 22:19:30 +01:00
// Check neutralizing gas, gastro acid
if ( knownAbility = = ABILITY_NONE )
return knownAbility ;
2020-12-11 16:05:00 +01:00
2020-12-13 23:02:21 +01:00
if ( BATTLE_HISTORY - > abilities [ battlerId ] ! = ABILITY_NONE )
2020-12-11 16:05:00 +01:00
return BATTLE_HISTORY - > abilities [ battlerId ] ;
2021-10-31 22:19:30 +01:00
// Abilities that prevent fleeing - treat as always known
if ( knownAbility = = ABILITY_SHADOW_TAG | | knownAbility = = ABILITY_MAGNET_PULL | | knownAbility = = ABILITY_ARENA_TRAP )
return knownAbility ;
2020-12-11 16:05:00 +01:00
2021-10-31 22:19:30 +01:00
// Else, guess the ability
2020-12-11 16:05:00 +01:00
if ( gBaseStats [ gBattleMons [ battlerId ] . species ] . abilities [ 0 ] ! = ABILITY_NONE )
{
2021-11-15 16:45:23 +01:00
u16 abilityGuess = ABILITY_NONE ;
while ( abilityGuess = = ABILITY_NONE )
2020-12-11 16:05:00 +01:00
{
2021-11-15 16:45:23 +01:00
abilityGuess = gBaseStats [ gBattleMons [ battlerId ] . species ] . abilities [ Random ( ) % NUM_ABILITY_SLOTS ] ;
2020-12-11 16:05:00 +01:00
}
2022-08-23 01:07:25 +02:00
2021-11-15 16:45:23 +01:00
return abilityGuess ;
2020-12-11 16:05:00 +01:00
}
2022-08-23 01:07:25 +02:00
2020-12-13 23:02:21 +01:00
return ABILITY_NONE ; // Unknown.
}
u16 AI_GetHoldEffect ( u32 battlerId )
{
u32 holdEffect ;
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
if ( ! IsBattlerAIControlled ( battlerId ) )
holdEffect = BATTLE_HISTORY - > itemEffects [ battlerId ] ;
else
holdEffect = GetBattlerHoldEffect ( battlerId , FALSE ) ;
2021-06-10 12:55:05 +02:00
2020-12-20 22:47:20 +01:00
if ( AI_THINKING_STRUCT - > aiFlags & AI_FLAG_NEGATE_UNAWARE )
return holdEffect ;
2021-06-10 12:55:05 +02:00
2020-12-20 22:47:20 +01:00
if ( gStatuses3 [ battlerId ] & STATUS3_EMBARGO )
return HOLD_EFFECT_NONE ;
if ( gFieldStatuses & STATUS_FIELD_MAGIC_ROOM )
return HOLD_EFFECT_NONE ;
2022-01-13 17:28:27 +01:00
if ( AI_DATA - > abilities [ battlerId ] = = ABILITY_KLUTZ & & ! ( gStatuses3 [ battlerId ] & STATUS3_GASTRO_ACID ) )
2020-12-20 22:47:20 +01:00
return HOLD_EFFECT_NONE ;
2021-06-10 12:55:05 +02:00
2021-01-19 20:58:58 +01:00
return holdEffect ;
2020-12-13 23:02:21 +01:00
}
2021-07-11 00:41:41 +02:00
bool32 AI_IsTerrainAffected ( u8 battlerId , u32 flags )
{
if ( gStatuses3 [ battlerId ] & STATUS3_SEMI_INVULNERABLE )
return FALSE ;
else if ( ! ( gFieldStatuses & flags ) )
return FALSE ;
return AI_IsBattlerGrounded ( battlerId ) ;
}
2020-12-13 23:02:21 +01:00
// different from IsBattlerGrounded in that we don't always know battler's hold effect or ability
bool32 AI_IsBattlerGrounded ( u8 battlerId )
{
2022-01-13 17:28:27 +01:00
u32 holdEffect = AI_DATA - > holdEffects [ battlerId ] ;
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
if ( holdEffect = = HOLD_EFFECT_IRON_BALL )
return TRUE ;
else if ( gFieldStatuses & STATUS_FIELD_GRAVITY )
return TRUE ;
else if ( gStatuses3 [ battlerId ] & STATUS3_ROOTED )
return TRUE ;
else if ( gStatuses3 [ battlerId ] & STATUS3_SMACKED_DOWN )
return TRUE ;
else if ( gStatuses3 [ battlerId ] & STATUS3_TELEKINESIS )
return FALSE ;
else if ( gStatuses3 [ battlerId ] & STATUS3_MAGNET_RISE )
return FALSE ;
else if ( holdEffect = = HOLD_EFFECT_AIR_BALLOON )
return FALSE ;
2022-01-13 17:28:27 +01:00
else if ( AI_DATA - > abilities [ battlerId ] = = ABILITY_LEVITATE )
2020-12-13 23:02:21 +01:00
return FALSE ;
else if ( IS_BATTLER_OF_TYPE ( battlerId , TYPE_FLYING ) )
return FALSE ;
else
return TRUE ;
}
bool32 DoesBattlerIgnoreAbilityChecks ( u16 atkAbility , u16 move )
{
u32 i ;
2021-06-10 12:55:05 +02:00
2020-12-20 22:47:20 +01:00
if ( AI_THINKING_STRUCT - > aiFlags & AI_FLAG_NEGATE_UNAWARE )
2021-01-04 23:30:02 +01:00
return FALSE ; // AI handicap flag: doesn't understand ability suppression concept
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
for ( i = 0 ; i < ARRAY_COUNT ( sIgnoreMoldBreakerMoves ) ; i + + )
{
if ( move = = sIgnoreMoldBreakerMoves [ i ] )
return TRUE ;
}
if ( atkAbility = = ABILITY_MOLD_BREAKER
| | atkAbility = = ABILITY_TERAVOLT
| | atkAbility = = ABILITY_TURBOBLAZE )
return TRUE ;
return FALSE ;
}
bool32 AI_WeatherHasEffect ( void )
{
2021-01-10 16:58:41 +01:00
u32 i ;
2020-12-20 22:47:20 +01:00
if ( AI_THINKING_STRUCT - > aiFlags & AI_FLAG_NEGATE_UNAWARE )
2021-01-16 05:29:39 +01:00
return TRUE ; // AI doesn't understand weather supression (handicap)
2021-06-10 12:55:05 +02:00
2022-01-13 17:28:27 +01:00
return WEATHER_HAS_EFFECT ; // weather damping abilities are announced
2020-12-13 23:02:21 +01:00
}
2022-04-27 12:23:20 +02:00
u32 AI_GetBattlerMoveTargetType ( u8 battlerId , u16 move )
{
u32 target ;
if ( gBattleMoves [ move ] . effect = = EFFECT_EXPANDING_FORCE & & AI_IsTerrainAffected ( battlerId , STATUS_FIELD_PSYCHIC_TERRAIN ) )
return MOVE_TARGET_BOTH ;
else
return gBattleMoves [ move ] . target ;
2020-12-13 23:02:21 +01:00
}
bool32 IsAromaVeilProtectedMove ( u16 move )
{
u32 i ;
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
switch ( move )
{
case MOVE_DISABLE :
case MOVE_ATTRACT :
case MOVE_ENCORE :
case MOVE_TORMENT :
case MOVE_TAUNT :
case MOVE_HEAL_BLOCK :
return TRUE ;
default :
return FALSE ;
}
}
bool32 IsNonVolatileStatusMoveEffect ( u16 moveEffect )
{
switch ( moveEffect )
{
case EFFECT_SLEEP :
case EFFECT_TOXIC :
case EFFECT_POISON :
case EFFECT_PARALYZE :
case EFFECT_WILL_O_WISP :
case EFFECT_YAWN :
return TRUE ;
default :
return FALSE ;
}
}
bool32 IsConfusionMoveEffect ( u16 moveEffect )
{
switch ( moveEffect )
{
2022-10-13 16:58:57 +02:00
case EFFECT_CONFUSE :
2020-12-13 23:02:21 +01:00
case EFFECT_SWAGGER :
case EFFECT_FLATTER :
case EFFECT_TEETER_DANCE :
return TRUE ;
default :
return FALSE ;
}
}
bool32 IsStatLoweringMoveEffect ( u16 moveEffect )
{
switch ( moveEffect )
{
case EFFECT_ATTACK_DOWN :
case EFFECT_DEFENSE_DOWN :
case EFFECT_SPEED_DOWN :
case EFFECT_SPECIAL_ATTACK_DOWN :
case EFFECT_SPECIAL_DEFENSE_DOWN :
case EFFECT_ACCURACY_DOWN :
case EFFECT_EVASION_DOWN :
case EFFECT_ATTACK_DOWN_2 :
case EFFECT_DEFENSE_DOWN_2 :
case EFFECT_SPEED_DOWN_2 :
case EFFECT_SPECIAL_ATTACK_DOWN_2 :
case EFFECT_SPECIAL_DEFENSE_DOWN_2 :
case EFFECT_ACCURACY_DOWN_2 :
case EFFECT_EVASION_DOWN_2 :
return TRUE ;
default :
return FALSE ;
}
}
bool32 IsHazardMoveEffect ( u16 moveEffect )
{
switch ( moveEffect )
{
case EFFECT_SPIKES :
case EFFECT_TOXIC_SPIKES :
case EFFECT_STICKY_WEB :
case EFFECT_STEALTH_ROCK :
return TRUE ;
default :
return FALSE ;
}
}
bool32 IsMoveRedirectionPrevented ( u16 move , u16 atkAbility )
{
2020-12-20 22:47:20 +01:00
if ( AI_THINKING_STRUCT - > aiFlags & AI_FLAG_NEGATE_UNAWARE )
2020-12-13 23:02:21 +01:00
return FALSE ;
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
if ( move = = MOVE_SKY_DROP
| | move = = MOVE_SNIPE_SHOT
| | atkAbility = = ABILITY_PROPELLER_TAIL
| | atkAbility = = ABILITY_STALWART )
return TRUE ;
return FALSE ;
}
2022-01-13 17:28:27 +01:00
u32 AI_GetMoveAccuracy ( u8 battlerAtk , u8 battlerDef , u16 move )
2020-12-13 23:02:21 +01:00
{
2022-01-13 17:28:27 +01:00
return GetTotalAccuracy ( battlerAtk , battlerDef , move , AI_DATA - > abilities [ battlerAtk ] , AI_DATA - > abilities [ battlerDef ] ,
AI_DATA - > holdEffects [ battlerAtk ] , AI_DATA - > holdEffects [ battlerDef ] ) ;
2020-12-13 23:02:21 +01:00
}
2021-02-12 16:57:57 +01:00
bool32 IsSemiInvulnerable ( u8 battlerDef , u16 move )
2020-12-13 23:02:21 +01:00
{
2021-02-12 16:57:57 +01:00
if ( gStatuses3 [ battlerDef ] & STATUS3_PHANTOM_FORCE )
return TRUE ;
2021-06-05 02:39:03 +02:00
else if ( ! TestMoveFlags ( move , FLAG_DMG_IN_AIR ) & & gStatuses3 [ battlerDef ] & STATUS3_ON_AIR )
2021-02-12 16:57:57 +01:00
return TRUE ;
else if ( ! TestMoveFlags ( move , FLAG_DMG_UNDERWATER ) & & gStatuses3 [ battlerDef ] & STATUS3_UNDERWATER )
return TRUE ;
else if ( ! TestMoveFlags ( move , FLAG_DMG_UNDERGROUND ) & & gStatuses3 [ battlerDef ] & STATUS3_UNDERGROUND )
return TRUE ;
else
2020-12-13 23:02:21 +01:00
return FALSE ;
2021-02-12 16:57:57 +01:00
}
bool32 IsMoveEncouragedToHit ( u8 battlerAtk , u8 battlerDef , u16 move )
{
if ( IsSemiInvulnerable ( battlerDef , move ) )
2020-12-13 23:02:21 +01:00
return FALSE ;
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
//TODO - anticipate protect move?
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
// always hits
if ( gStatuses3 [ battlerDef ] & STATUS3_ALWAYS_HITS | | gDisableStructs [ battlerDef ] . battlerWithSureHit = = battlerAtk )
return TRUE ;
2021-06-10 12:55:05 +02:00
2022-01-13 17:28:27 +01:00
if ( AI_DATA - > abilities [ battlerDef ] = = ABILITY_NO_GUARD | | AI_DATA - > abilities [ battlerAtk ] = = ABILITY_NO_GUARD )
2020-12-13 23:02:21 +01:00
return TRUE ;
2021-06-10 12:55:05 +02:00
2022-08-24 03:00:08 +02:00
# if B_TOXIC_NEVER_MISS >= GEN_6
if ( gBattleMoves [ move ] . effect = = EFFECT_TOXIC & & IS_BATTLER_OF_TYPE ( battlerAtk , TYPE_POISON ) )
2020-12-13 23:02:21 +01:00
return TRUE ;
2022-08-24 03:00:08 +02:00
# endif
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
// discouraged from hitting
2021-11-21 19:40:26 +01:00
if ( AI_WeatherHasEffect ( ) & & ( gBattleWeather & B_WEATHER_SUN )
2020-12-13 23:02:21 +01:00
& & ( gBattleMoves [ move ] . effect = = EFFECT_THUNDER | | gBattleMoves [ move ] . effect = = EFFECT_HURRICANE ) )
return FALSE ;
2021-06-10 12:55:05 +02:00
// increased accuracy but don't always hit
2020-12-13 23:02:21 +01:00
if ( ( AI_WeatherHasEffect ( ) & &
2021-11-21 19:40:26 +01:00
( ( ( gBattleWeather & B_WEATHER_RAIN ) & & ( gBattleMoves [ move ] . effect = = EFFECT_THUNDER | | gBattleMoves [ move ] . effect = = EFFECT_HURRICANE ) )
2022-08-24 03:00:08 +02:00
| | ( ( ( gBattleWeather & B_WEATHER_HAIL ) & & move = = MOVE_BLIZZARD ) ) ) )
| | ( gBattleMoves [ move ] . effect = = EFFECT_VITAL_THROW )
# if B_MINIMIZE_DMG_ACC >= GEN_6
| | ( ( gStatuses3 [ battlerDef ] & STATUS3_MINIMIZED ) & & ( gBattleMoves [ move ] . flags & FLAG_DMG_MINIMIZE ) )
# endif
| | ( gBattleMoves [ move ] . accuracy = = 0 ) )
2020-12-13 23:02:21 +01:00
{
return TRUE ;
}
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
return FALSE ;
}
2022-01-13 17:28:27 +01:00
bool32 ShouldTryOHKO ( u8 battlerAtk , u8 battlerDef , u16 atkAbility , u16 defAbility , u16 move )
2020-12-13 23:02:21 +01:00
{
2022-01-13 17:28:27 +01:00
u32 holdEffect = AI_DATA - > holdEffects [ battlerDef ] ;
u32 accuracy = AI_GetMoveAccuracy ( battlerAtk , battlerDef , move ) ;
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
gPotentialItemEffectBattler = battlerDef ;
2022-01-13 17:28:27 +01:00
if ( holdEffect = = HOLD_EFFECT_FOCUS_BAND & & ( Random ( ) % 100 ) < AI_DATA - > holdEffectParams [ battlerDef ] )
2020-12-13 23:02:21 +01:00
return FALSE ; //probabilistically speaking, focus band should activate so dont OHKO
2020-12-20 05:58:23 +01:00
else if ( holdEffect = = HOLD_EFFECT_FOCUS_SASH & & AtMaxHp ( battlerDef ) )
2020-12-13 23:02:21 +01:00
return FALSE ;
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
if ( ! DoesBattlerIgnoreAbilityChecks ( atkAbility , move ) & & defAbility = = ABILITY_STURDY )
return FALSE ;
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
if ( ( ( ( gStatuses3 [ battlerDef ] & STATUS3_ALWAYS_HITS )
& & gDisableStructs [ battlerDef ] . battlerWithSureHit = = battlerAtk )
| | atkAbility = = ABILITY_NO_GUARD | | defAbility = = ABILITY_NO_GUARD )
& & gBattleMons [ battlerAtk ] . level > = gBattleMons [ battlerDef ] . level )
{
return TRUE ;
}
else // test the odds
{
u16 odds = accuracy + ( gBattleMons [ battlerAtk ] . level - gBattleMons [ battlerDef ] . level ) ;
2022-08-24 03:00:08 +02:00
# if B_SHEER_COLD_ACC >= GEN_7
if ( gCurrentMove = = MOVE_SHEER_COLD & & ! IS_BATTLER_OF_TYPE ( gBattlerAttacker , TYPE_ICE ) )
odds - = 10 ;
# endif
2020-12-13 23:02:21 +01:00
if ( Random ( ) % 100 + 1 < odds & & gBattleMons [ battlerAtk ] . level > = gBattleMons [ battlerDef ] . level )
return TRUE ;
}
return FALSE ;
}
2020-12-20 05:58:23 +01:00
bool32 ShouldSetSandstorm ( u8 battler , u16 ability , u16 holdEffect )
{
if ( ! AI_WeatherHasEffect ( ) )
return FALSE ;
2021-11-21 19:40:26 +01:00
else if ( gBattleWeather & B_WEATHER_SANDSTORM )
2020-12-20 05:58:23 +01:00
return FALSE ;
2021-06-10 12:55:05 +02:00
2020-12-20 05:58:23 +01:00
if ( ability = = ABILITY_SAND_VEIL
| | ability = = ABILITY_SAND_RUSH
| | ability = = ABILITY_SAND_FORCE
| | ability = = ABILITY_OVERCOAT
| | ability = = ABILITY_MAGIC_GUARD
2021-10-09 04:05:01 +02:00
| | holdEffect = = HOLD_EFFECT_SAFETY_GOGGLES
2020-12-20 05:58:23 +01:00
| | IS_BATTLER_OF_TYPE ( battler , TYPE_ROCK )
| | IS_BATTLER_OF_TYPE ( battler , TYPE_STEEL )
| | IS_BATTLER_OF_TYPE ( battler , TYPE_GROUND )
| | HasMoveEffect ( battler , EFFECT_SHORE_UP )
| | HasMoveEffect ( battler , EFFECT_WEATHER_BALL ) )
{
return TRUE ;
}
return FALSE ;
}
bool32 ShouldSetHail ( u8 battler , u16 ability , u16 holdEffect )
{
if ( ! AI_WeatherHasEffect ( ) )
return FALSE ;
2021-11-21 19:40:26 +01:00
else if ( gBattleWeather & B_WEATHER_HAIL )
2020-12-20 05:58:23 +01:00
return FALSE ;
2021-06-10 12:55:05 +02:00
2020-12-20 05:58:23 +01:00
if ( ability = = ABILITY_SNOW_CLOAK
| | ability = = ABILITY_ICE_BODY
| | ability = = ABILITY_FORECAST
| | ability = = ABILITY_SLUSH_RUSH
| | ability = = ABILITY_MAGIC_GUARD
| | ability = = ABILITY_OVERCOAT
2021-10-09 04:05:01 +02:00
| | holdEffect = = HOLD_EFFECT_SAFETY_GOGGLES
2020-12-20 05:58:23 +01:00
| | IS_BATTLER_OF_TYPE ( battler , TYPE_ICE )
| | HasMove ( battler , MOVE_BLIZZARD )
| | HasMoveEffect ( battler , EFFECT_AURORA_VEIL )
| | HasMoveEffect ( battler , EFFECT_WEATHER_BALL ) )
{
return TRUE ;
2021-06-10 12:55:05 +02:00
}
2020-12-20 05:58:23 +01:00
return FALSE ;
}
bool32 ShouldSetRain ( u8 battlerAtk , u16 atkAbility , u16 holdEffect )
{
if ( ! AI_WeatherHasEffect ( ) )
return FALSE ;
2021-11-21 19:40:26 +01:00
else if ( gBattleWeather & B_WEATHER_RAIN )
2020-12-20 05:58:23 +01:00
return FALSE ;
2021-06-10 12:55:05 +02:00
2020-12-20 05:58:23 +01:00
if ( holdEffect ! = HOLD_EFFECT_UTILITY_UMBRELLA
& & ( atkAbility = = ABILITY_SWIFT_SWIM
| | atkAbility = = ABILITY_FORECAST
| | atkAbility = = ABILITY_HYDRATION
| | atkAbility = = ABILITY_RAIN_DISH
| | atkAbility = = ABILITY_DRY_SKIN
| | HasMoveEffect ( battlerAtk , EFFECT_THUNDER )
| | HasMoveEffect ( battlerAtk , EFFECT_HURRICANE )
| | HasMoveEffect ( battlerAtk , EFFECT_WEATHER_BALL )
| | HasMoveWithType ( battlerAtk , TYPE_WATER ) ) )
{
return TRUE ;
}
return FALSE ;
}
bool32 ShouldSetSun ( u8 battlerAtk , u16 atkAbility , u16 holdEffect )
{
if ( ! AI_WeatherHasEffect ( ) )
return FALSE ;
2021-11-21 19:40:26 +01:00
else if ( gBattleWeather & B_WEATHER_SUN )
2020-12-20 05:58:23 +01:00
return FALSE ;
2021-06-10 12:55:05 +02:00
2020-12-20 05:58:23 +01:00
if ( holdEffect ! = HOLD_EFFECT_UTILITY_UMBRELLA
& & ( atkAbility = = ABILITY_CHLOROPHYLL
| | atkAbility = = ABILITY_FLOWER_GIFT
| | atkAbility = = ABILITY_FORECAST
| | atkAbility = = ABILITY_LEAF_GUARD
| | atkAbility = = ABILITY_SOLAR_POWER
| | atkAbility = = ABILITY_HARVEST
2021-11-21 19:40:26 +01:00
| | HasMoveEffect ( battlerAtk , EFFECT_SOLAR_BEAM )
2020-12-20 05:58:23 +01:00
| | HasMoveEffect ( battlerAtk , EFFECT_MORNING_SUN )
| | HasMoveEffect ( battlerAtk , EFFECT_SYNTHESIS )
| | HasMoveEffect ( battlerAtk , EFFECT_MOONLIGHT )
| | HasMoveEffect ( battlerAtk , EFFECT_WEATHER_BALL )
| | HasMoveEffect ( battlerAtk , EFFECT_GROWTH )
| | HasMoveWithType ( battlerAtk , TYPE_FIRE ) ) )
{
return TRUE ;
}
return FALSE ;
}
void ProtectChecks ( u8 battlerAtk , u8 battlerDef , u16 move , u16 predictedMove , s16 * score )
{
// TODO more sophisticated logic
u16 predictedEffect = gBattleMoves [ predictedMove ] . effect ;
2022-01-13 17:28:27 +01:00
u8 defAbility = AI_DATA - > abilities [ battlerDef ] ;
2020-12-20 05:58:23 +01:00
u32 uses = gDisableStructs [ battlerAtk ] . protectUses ;
2021-06-10 12:55:05 +02:00
2020-12-20 05:58:23 +01:00
/*if (GetMoveResultFlags(predictedMove) & (MOVE_RESULT_NO_EFFECT | MOVE_RESULT_MISSED))
{
( * score ) - = 5 ;
return ;
} */
2021-06-10 12:55:05 +02:00
2020-12-20 05:58:23 +01:00
if ( uses = = 0 )
{
if ( predictedMove ! = MOVE_NONE & & predictedMove ! = 0xFFFF & & ! IS_MOVE_STATUS ( predictedMove ) )
( * score ) + = 2 ;
else if ( Random ( ) % 256 < 100 )
( * score ) + + ;
}
else
{
if ( IsDoubleBattle ( ) )
( * score ) - = 2 * min ( uses , 3 ) ;
else
( * score ) - = min ( uses , 3 ) ;
}
if ( gBattleMons [ battlerAtk ] . status1 & ( STATUS1_PSN_ANY | STATUS1_BURN )
| | gBattleMons [ battlerAtk ] . status2 & ( STATUS2_CURSED | STATUS2_INFATUATION )
| | gStatuses3 [ battlerAtk ] & ( STATUS3_PERISH_SONG | STATUS3_LEECHSEED | STATUS3_YAWN ) )
{
( * score ) - - ;
}
2021-06-10 12:55:05 +02:00
2020-12-20 05:58:23 +01:00
if ( gBattleMons [ battlerDef ] . status1 & STATUS1_TOXIC_POISON
| | gBattleMons [ battlerDef ] . status2 & ( STATUS2_CURSED | STATUS2_INFATUATION )
| | gStatuses3 [ battlerDef ] & ( STATUS3_PERISH_SONG | STATUS3_LEECHSEED | STATUS3_YAWN ) )
( * score ) + = 2 ;
}
2020-12-13 23:02:21 +01:00
// stat stages
2021-01-04 23:30:02 +01:00
bool32 ShouldLowerStat ( u8 battler , u16 battlerAbility , u8 stat )
2020-12-13 23:02:21 +01:00
{
if ( ( gBattleMons [ battler ] . statStages [ stat ] > MIN_STAT_STAGE & & battlerAbility ! = ABILITY_CONTRARY )
| | ( battlerAbility = = ABILITY_CONTRARY & & gBattleMons [ battler ] . statStages [ stat ] < MAX_STAT_STAGE ) )
2021-01-04 23:30:02 +01:00
{
if ( battlerAbility = = ABILITY_CLEAR_BODY
| | battlerAbility = = ABILITY_WHITE_SMOKE
| | battlerAbility = = ABILITY_FULL_METAL_BODY )
return FALSE ;
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
return TRUE ;
2021-01-04 23:30:02 +01:00
}
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
return FALSE ;
}
bool32 BattlerStatCanRise ( u8 battler , u16 battlerAbility , u8 stat )
{
if ( ( gBattleMons [ battler ] . statStages [ stat ] < MAX_STAT_STAGE & & battlerAbility ! = ABILITY_CONTRARY )
| | ( battlerAbility = = ABILITY_CONTRARY & & gBattleMons [ battler ] . statStages [ stat ] > MIN_STAT_STAGE ) )
return TRUE ;
return FALSE ;
}
bool32 AreBattlersStatsMaxed ( u8 battlerId )
{
2021-06-10 12:55:05 +02:00
u32 i ;
2020-12-13 23:02:21 +01:00
for ( i = STAT_ATK ; i < NUM_BATTLE_STATS ; i + + )
{
if ( gBattleMons [ battlerId ] . statStages [ i ] < MAX_STAT_STAGE )
return FALSE ;
}
return TRUE ;
}
bool32 AnyStatIsRaised ( u8 battlerId )
{
u32 i ;
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
for ( i = STAT_ATK ; i < NUM_BATTLE_STATS ; i + + )
{
if ( gBattleMons [ battlerId ] . statStages [ i ] > DEFAULT_STAT_STAGE )
return TRUE ;
}
return FALSE ;
}
u32 CountPositiveStatStages ( u8 battlerId )
{
u32 count = 0 ;
u32 i ;
for ( i = STAT_ATK ; i < NUM_BATTLE_STATS ; i + + )
{
if ( gBattleMons [ battlerId ] . statStages [ i ] > DEFAULT_STAT_STAGE )
count + + ;
}
return count ;
}
u32 CountNegativeStatStages ( u8 battlerId )
{
u32 count = 0 ;
u32 i ;
for ( i = STAT_ATK ; i < NUM_BATTLE_STATS ; i + + )
{
if ( gBattleMons [ battlerId ] . statStages [ i ] < DEFAULT_STAT_STAGE )
count + + ;
}
return count ;
}
2021-01-05 03:39:59 +01:00
bool32 ShouldLowerAttack ( u8 battlerAtk , u8 battlerDef , u16 defAbility )
2020-12-17 06:56:10 +01:00
{
2021-11-21 18:46:09 +01:00
if ( WillAIStrikeFirst ( ) & & ( AI_THINKING_STRUCT - > aiFlags & AI_FLAG_TRY_TO_FAINT ) & & CanAIFaintTarget ( battlerAtk , battlerDef , 0 ) )
2020-12-20 05:58:23 +01:00
return FALSE ; // Don't bother lowering stats if can kill enemy.
2020-12-17 06:56:10 +01:00
2020-12-20 05:58:23 +01:00
if ( gBattleMons [ battlerDef ] . statStages [ STAT_ATK ] > 4
& & HasMoveWithSplit ( battlerDef , SPLIT_PHYSICAL )
2020-12-17 06:56:10 +01:00
& & defAbility ! = ABILITY_CONTRARY
& & defAbility ! = ABILITY_CLEAR_BODY
& & defAbility ! = ABILITY_WHITE_SMOKE
2021-11-08 07:28:09 +01:00
& & defAbility ! = ABILITY_FULL_METAL_BODY
2020-12-17 06:56:10 +01:00
& & defAbility ! = ABILITY_HYPER_CUTTER )
return TRUE ;
return FALSE ;
}
2021-01-05 03:39:59 +01:00
bool32 ShouldLowerDefense ( u8 battlerAtk , u8 battlerDef , u16 defAbility )
2020-12-20 05:58:23 +01:00
{
2021-11-21 18:46:09 +01:00
if ( WillAIStrikeFirst ( ) & & ( AI_THINKING_STRUCT - > aiFlags & AI_FLAG_TRY_TO_FAINT ) & & CanAIFaintTarget ( battlerAtk , battlerDef , 0 ) )
2020-12-20 05:58:23 +01:00
return FALSE ; // Don't bother lowering stats if can kill enemy.
if ( gBattleMons [ battlerDef ] . statStages [ STAT_DEF ] > 4
& & HasMoveWithSplit ( battlerAtk , SPLIT_PHYSICAL )
& & defAbility ! = ABILITY_CONTRARY
& & defAbility ! = ABILITY_CLEAR_BODY
& & defAbility ! = ABILITY_WHITE_SMOKE
2021-11-08 07:28:09 +01:00
& & defAbility ! = ABILITY_FULL_METAL_BODY
2020-12-20 05:58:23 +01:00
& & defAbility ! = ABILITY_BIG_PECKS )
return TRUE ;
return FALSE ;
}
2021-01-05 03:39:59 +01:00
bool32 ShouldLowerSpeed ( u8 battlerAtk , u8 battlerDef , u16 defAbility )
2020-12-20 05:58:23 +01:00
{
2021-11-21 18:46:09 +01:00
if ( WillAIStrikeFirst ( ) & & ( AI_THINKING_STRUCT - > aiFlags & AI_FLAG_TRY_TO_FAINT ) & & CanAIFaintTarget ( battlerAtk , battlerDef , 0 ) )
2020-12-20 05:58:23 +01:00
return FALSE ; // Don't bother lowering stats if can kill enemy.
2021-09-28 03:03:27 +02:00
if ( ! WillAIStrikeFirst ( )
2020-12-20 05:58:23 +01:00
& & defAbility ! = ABILITY_CONTRARY
& & defAbility ! = ABILITY_CLEAR_BODY
2021-11-08 07:28:09 +01:00
& & defAbility ! = ABILITY_FULL_METAL_BODY
2020-12-20 05:58:23 +01:00
& & defAbility ! = ABILITY_WHITE_SMOKE )
return TRUE ;
return FALSE ;
}
2021-01-05 03:39:59 +01:00
bool32 ShouldLowerSpAtk ( u8 battlerAtk , u8 battlerDef , u16 defAbility )
2020-12-20 05:58:23 +01:00
{
2021-11-21 18:46:09 +01:00
if ( WillAIStrikeFirst ( ) & & ( AI_THINKING_STRUCT - > aiFlags & AI_FLAG_TRY_TO_FAINT ) & & CanAIFaintTarget ( battlerAtk , battlerDef , 0 ) )
2020-12-20 05:58:23 +01:00
return FALSE ; // Don't bother lowering stats if can kill enemy.
if ( gBattleMons [ battlerDef ] . statStages [ STAT_SPATK ] > 4
& & HasMoveWithSplit ( battlerDef , SPLIT_SPECIAL )
& & defAbility ! = ABILITY_CONTRARY
& & defAbility ! = ABILITY_CLEAR_BODY
2021-11-08 07:28:09 +01:00
& & defAbility ! = ABILITY_FULL_METAL_BODY
2020-12-20 05:58:23 +01:00
& & defAbility ! = ABILITY_WHITE_SMOKE )
return TRUE ;
return FALSE ;
}
2021-01-05 03:39:59 +01:00
bool32 ShouldLowerSpDef ( u8 battlerAtk , u8 battlerDef , u16 defAbility )
2020-12-20 05:58:23 +01:00
{
2021-11-21 18:46:09 +01:00
if ( WillAIStrikeFirst ( ) & & ( AI_THINKING_STRUCT - > aiFlags & AI_FLAG_TRY_TO_FAINT ) & & CanAIFaintTarget ( battlerAtk , battlerDef , 0 ) )
2020-12-20 05:58:23 +01:00
return FALSE ; // Don't bother lowering stats if can kill enemy.
if ( gBattleMons [ battlerDef ] . statStages [ STAT_SPDEF ] > 4
& & HasMoveWithSplit ( battlerAtk , SPLIT_SPECIAL )
& & defAbility ! = ABILITY_CONTRARY
& & defAbility ! = ABILITY_CLEAR_BODY
2021-11-08 07:28:09 +01:00
& & defAbility ! = ABILITY_FULL_METAL_BODY
2020-12-20 05:58:23 +01:00
& & defAbility ! = ABILITY_WHITE_SMOKE )
return TRUE ;
return FALSE ;
}
2021-01-05 03:39:59 +01:00
bool32 ShouldLowerAccuracy ( u8 battlerAtk , u8 battlerDef , u16 defAbility )
2020-12-20 05:58:23 +01:00
{
2021-11-21 18:46:09 +01:00
if ( WillAIStrikeFirst ( ) & & ( AI_THINKING_STRUCT - > aiFlags & AI_FLAG_TRY_TO_FAINT ) & & CanAIFaintTarget ( battlerAtk , battlerDef , 0 ) )
2020-12-20 05:58:23 +01:00
return FALSE ; // Don't bother lowering stats if can kill enemy.
if ( defAbility ! = ABILITY_CONTRARY
& & defAbility ! = ABILITY_CLEAR_BODY
& & defAbility ! = ABILITY_WHITE_SMOKE
2021-11-08 07:28:09 +01:00
& & defAbility ! = ABILITY_FULL_METAL_BODY
2020-12-20 05:58:23 +01:00
& & defAbility ! = ABILITY_KEEN_EYE )
return TRUE ;
return FALSE ;
}
2021-01-05 03:39:59 +01:00
bool32 ShouldLowerEvasion ( u8 battlerAtk , u8 battlerDef , u16 defAbility )
2020-12-20 05:58:23 +01:00
{
2021-11-21 18:46:09 +01:00
if ( WillAIStrikeFirst ( ) & & ( AI_THINKING_STRUCT - > aiFlags & AI_FLAG_TRY_TO_FAINT ) & & CanAIFaintTarget ( battlerAtk , battlerDef , 0 ) )
2020-12-20 05:58:23 +01:00
return FALSE ; // Don't bother lowering stats if can kill enemy.
if ( gBattleMons [ battlerDef ] . statStages [ STAT_EVASION ] > DEFAULT_STAT_STAGE
& & defAbility ! = ABILITY_CONTRARY
& & defAbility ! = ABILITY_CLEAR_BODY
2021-11-08 07:28:09 +01:00
& & defAbility ! = ABILITY_FULL_METAL_BODY
2020-12-20 05:58:23 +01:00
& & defAbility ! = ABILITY_WHITE_SMOKE )
return TRUE ;
return FALSE ;
}
2021-11-08 08:37:28 +01:00
bool32 CanIndexMoveFaintTarget ( u8 battlerAtk , u8 battlerDef , u8 index , u8 numHits )
2020-12-13 23:02:21 +01:00
{
2022-01-13 17:28:27 +01:00
s32 dmg = AI_DATA - > simulatedDmg [ battlerAtk ] [ battlerDef ] [ index ] ;
2021-06-10 12:55:05 +02:00
2020-12-20 05:58:23 +01:00
if ( numHits )
dmg * = numHits ;
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
if ( gBattleMons [ battlerDef ] . hp < = dmg )
return TRUE ;
return FALSE ;
}
u16 * GetMovesArray ( u32 battler )
{
if ( IsBattlerAIControlled ( battler ) | | IsBattlerAIControlled ( BATTLE_PARTNER ( battler ) ) )
return gBattleMons [ battler ] . moves ;
else
return gBattleResources - > battleHistory - > usedMoves [ battler ] ;
}
2021-02-14 17:37:04 +01:00
bool32 HasOnlyMovesWithSplit ( u32 battlerId , u32 split , bool32 onlyOffensive )
{
u32 i ;
u16 * moves = GetMovesArray ( battlerId ) ;
for ( i = 0 ; i < MAX_MON_MOVES ; i + + )
{
if ( onlyOffensive & & IS_MOVE_STATUS ( moves [ i ] ) )
continue ;
if ( moves [ i ] ! = MOVE_NONE & & moves [ i ] ! = 0xFFFF & & GetBattleMoveSplit ( moves [ i ] ) ! = split )
return FALSE ;
}
2021-06-10 12:55:05 +02:00
2021-02-14 17:37:04 +01:00
return TRUE ;
}
2020-12-13 23:02:21 +01:00
bool32 HasMoveWithSplit ( u32 battler , u32 split )
{
u32 i ;
u16 * moves = GetMovesArray ( battler ) ;
for ( i = 0 ; i < MAX_MON_MOVES ; i + + )
{
if ( moves [ i ] ! = MOVE_NONE & & moves [ i ] ! = 0xFFFF & & GetBattleMoveSplit ( moves [ i ] ) = = split )
return TRUE ;
}
return FALSE ;
}
bool32 HasMoveWithType ( u32 battler , u8 type )
{
s32 i ;
u16 * moves = GetMovesArray ( battler ) ;
for ( i = 0 ; i < MAX_MON_MOVES ; i + + )
{
if ( moves [ i ] ! = MOVE_NONE & & moves [ i ] ! = 0xFFFF & & gBattleMoves [ moves [ i ] ] . type = = type )
return TRUE ;
}
return FALSE ;
}
2020-12-16 05:57:33 +01:00
bool32 HasMoveEffect ( u32 battlerId , u16 moveEffect )
{
s32 i ;
u16 * moves = GetMovesArray ( battlerId ) ;
for ( i = 0 ; i < MAX_MON_MOVES ; i + + )
{
if ( moves [ i ] ! = MOVE_NONE & & moves [ i ] ! = 0xFFFF & & gBattleMoves [ moves [ i ] ] . effect = = moveEffect )
return TRUE ;
}
return FALSE ;
}
2020-12-20 05:58:23 +01:00
bool32 HasMove ( u32 battlerId , u32 move )
{
s32 i ;
u16 * moves = GetMovesArray ( battlerId ) ;
for ( i = 0 ; i < MAX_MON_MOVES ; i + + )
{
if ( moves [ i ] ! = MOVE_NONE & & moves [ i ] ! = 0xFFFF & & moves [ i ] = = move )
return TRUE ;
}
return FALSE ;
}
bool32 HasMoveWithLowAccuracy ( u8 battlerAtk , u8 battlerDef , u8 accCheck , bool32 ignoreStatus , u16 atkAbility , u16 defAbility , u16 atkHoldEffect , u16 defHoldEffect )
2020-12-17 06:56:10 +01:00
{
s32 i ;
u16 * moves = GetMovesArray ( battlerAtk ) ;
2022-01-13 17:28:27 +01:00
u8 moveLimitations = AI_DATA - > moveLimitations [ battlerAtk ] ;
2021-06-10 12:55:05 +02:00
2020-12-17 06:56:10 +01:00
for ( i = 0 ; i < MAX_MON_MOVES ; i + + )
{
if ( moves [ i ] = = MOVE_NONE | | moves [ i ] = = 0xFFFF )
continue ;
2021-06-10 12:55:05 +02:00
2020-12-17 06:56:10 +01:00
if ( ! ( gBitTable [ i ] & moveLimitations ) )
{
if ( ignoreStatus & & IS_MOVE_STATUS ( moves [ i ] ) )
continue ;
2020-12-20 05:58:23 +01:00
else if ( ( ! IS_MOVE_STATUS ( moves [ i ] ) & & gBattleMoves [ moves [ i ] ] . accuracy = = 0 )
2022-04-27 12:23:20 +02:00
| | AI_GetBattlerMoveTargetType ( battlerAtk , moves [ i ] ) & ( MOVE_TARGET_USER | MOVE_TARGET_OPPONENTS_FIELD ) )
2020-12-17 06:56:10 +01:00
continue ;
2021-06-10 12:55:05 +02:00
2022-01-13 17:28:27 +01:00
if ( AI_GetMoveAccuracy ( battlerAtk , battlerDef , moves [ i ] ) < = accCheck )
2020-12-20 05:58:23 +01:00
return TRUE ;
}
}
2021-06-10 12:55:05 +02:00
2020-12-20 05:58:23 +01:00
return FALSE ;
}
bool32 HasSleepMoveWithLowAccuracy ( u8 battlerAtk , u8 battlerDef )
{
2022-01-13 17:28:27 +01:00
u8 moveLimitations = AI_DATA - > moveLimitations [ battlerAtk ] ;
2020-12-20 05:58:23 +01:00
u32 i ;
u16 * moves = GetMovesArray ( battlerAtk ) ;
2021-06-10 12:55:05 +02:00
2020-12-20 05:58:23 +01:00
for ( i = 0 ; i < MAX_MON_MOVES ; i + + )
{
if ( moves [ i ] = = MOVE_NONE )
break ;
if ( ! ( gBitTable [ i ] & moveLimitations ) )
{
if ( gBattleMoves [ moves [ i ] ] . effect = = EFFECT_SLEEP
2022-01-13 17:28:27 +01:00
& & AI_GetMoveAccuracy ( battlerAtk , battlerDef , moves [ i ] ) < 85 )
2020-12-17 06:56:10 +01:00
return TRUE ;
}
}
2020-12-20 05:58:23 +01:00
return FALSE ;
}
bool32 IsHealingMoveEffect ( u16 effect )
{
switch ( effect )
{
case EFFECT_RESTORE_HP :
case EFFECT_MORNING_SUN :
case EFFECT_SYNTHESIS :
case EFFECT_MOONLIGHT :
case EFFECT_SOFTBOILED :
case EFFECT_ROOST :
case EFFECT_SWALLOW :
case EFFECT_WISH :
case EFFECT_HEALING_WISH :
case EFFECT_HEAL_PULSE :
case EFFECT_REST :
return TRUE ;
default :
return FALSE ;
}
}
bool32 HasHealingEffect ( u32 battlerId )
{
s32 i ;
u16 * moves = GetMovesArray ( battlerId ) ;
for ( i = 0 ; i < MAX_MON_MOVES ; i + + )
{
if ( moves [ i ] ! = MOVE_NONE & & moves [ i ] ! = 0xFFFF & & IsHealingMoveEffect ( gBattleMoves [ moves [ i ] ] . effect ) )
return TRUE ;
}
return FALSE ;
}
2021-01-13 19:08:43 +01:00
bool32 IsTrappingMoveEffect ( u16 effect )
{
switch ( effect )
{
case EFFECT_MEAN_LOOK :
case EFFECT_TRAP :
case EFFECT_HIT_PREVENT_ESCAPE :
case EFFECT_FAIRY_LOCK :
//case EFFECT_NO_RETREAT: // TODO
return TRUE ;
default :
return FALSE ;
}
}
bool32 HasTrappingMoveEffect ( u8 battler )
{
s32 i ;
u16 * moves = GetMovesArray ( battler ) ;
for ( i = 0 ; i < MAX_MON_MOVES ; i + + )
{
if ( moves [ i ] ! = MOVE_NONE & & moves [ i ] ! = 0xFFFF & & IsTrappingMoveEffect ( gBattleMoves [ moves [ i ] ] . effect ) )
return TRUE ;
}
return FALSE ;
}
2020-12-20 05:58:23 +01:00
bool32 HasThawingMove ( u8 battlerId )
{
s32 i ;
u16 * moves = GetMovesArray ( battlerId ) ;
for ( i = 0 ; i < MAX_MON_MOVES ; i + + )
{
2021-06-10 13:48:18 +02:00
if ( moves [ i ] ! = MOVE_NONE & & moves [ i ] ! = 0xFFFF & & TestMoveFlags ( moves [ i ] , FLAG_THAW_USER ) )
2020-12-20 05:58:23 +01:00
return TRUE ;
}
return FALSE ;
}
2021-01-13 19:08:43 +01:00
bool32 IsUngroundingEffect ( u16 effect )
{
switch ( effect )
{
case EFFECT_MAGNET_RISE :
return TRUE ;
default :
return FALSE ;
}
}
// for anger point
2020-12-20 22:47:20 +01:00
bool32 IsAttackBoostMoveEffect ( u16 effect )
{
switch ( effect )
{
case EFFECT_ATTACK_UP :
case EFFECT_ATTACK_UP_2 :
case EFFECT_ATTACK_ACCURACY_UP :
case EFFECT_ATTACK_SPATK_UP :
case EFFECT_DRAGON_DANCE :
case EFFECT_COIL :
case EFFECT_BELLY_DRUM :
case EFFECT_BULK_UP :
2021-01-13 19:08:43 +01:00
case EFFECT_GROWTH :
2020-12-20 22:47:20 +01:00
return TRUE ;
default :
return FALSE ;
}
}
bool32 IsStatRaisingEffect ( u16 effect )
{
switch ( effect )
{
case EFFECT_ATTACK_UP :
case EFFECT_ATTACK_UP_2 :
case EFFECT_DEFENSE_UP :
case EFFECT_DEFENSE_UP_2 :
case EFFECT_DEFENSE_UP_3 :
case EFFECT_SPEED_UP :
case EFFECT_SPEED_UP_2 :
case EFFECT_SPECIAL_ATTACK_UP :
case EFFECT_SPECIAL_ATTACK_UP_2 :
case EFFECT_SPECIAL_ATTACK_UP_3 :
case EFFECT_SPECIAL_DEFENSE_UP :
case EFFECT_SPECIAL_DEFENSE_UP_2 :
case EFFECT_ACCURACY_UP :
case EFFECT_ACCURACY_UP_2 :
case EFFECT_EVASION_UP :
case EFFECT_EVASION_UP_2 :
case EFFECT_MINIMIZE :
case EFFECT_DEFENSE_CURL :
2022-07-20 20:44:57 +02:00
# if B_CHARGE_SPDEF_RAISE >= GEN_5
2020-12-20 22:47:20 +01:00
case EFFECT_CHARGE :
2022-07-20 20:44:57 +02:00
# endif
2020-12-20 22:47:20 +01:00
case EFFECT_CALM_MIND :
case EFFECT_COSMIC_POWER :
case EFFECT_DRAGON_DANCE :
case EFFECT_ACUPRESSURE :
case EFFECT_SHELL_SMASH :
case EFFECT_SHIFT_GEAR :
case EFFECT_ATTACK_ACCURACY_UP :
case EFFECT_ATTACK_SPATK_UP :
case EFFECT_GROWTH :
case EFFECT_COIL :
case EFFECT_QUIVER_DANCE :
case EFFECT_BULK_UP :
case EFFECT_GEOMANCY :
case EFFECT_STOCKPILE :
2022-11-18 22:23:34 +01:00
case EFFECT_VICTORY_DANCE :
2020-12-20 22:47:20 +01:00
return TRUE ;
default :
return FALSE ;
}
}
bool32 IsStatLoweringEffect ( u16 effect )
{
// ignore other potentially-beneficial effects like defog, gravity
switch ( effect )
{
case EFFECT_ATTACK_DOWN :
case EFFECT_DEFENSE_DOWN :
case EFFECT_SPEED_DOWN :
case EFFECT_SPECIAL_ATTACK_DOWN :
case EFFECT_SPECIAL_DEFENSE_DOWN :
case EFFECT_ACCURACY_DOWN :
case EFFECT_EVASION_DOWN :
case EFFECT_ATTACK_DOWN_2 :
case EFFECT_DEFENSE_DOWN_2 :
case EFFECT_SPEED_DOWN_2 :
case EFFECT_SPECIAL_ATTACK_DOWN_2 :
case EFFECT_SPECIAL_DEFENSE_DOWN_2 :
case EFFECT_ACCURACY_DOWN_2 :
case EFFECT_EVASION_DOWN_2 :
case EFFECT_TICKLE :
case EFFECT_CAPTIVATE :
case EFFECT_NOBLE_ROAR :
return TRUE ;
default :
return FALSE ;
}
}
2020-12-20 05:58:23 +01:00
bool32 HasDamagingMove ( u8 battlerId )
{
u32 i ;
u16 * moves = GetMovesArray ( battlerId ) ;
2021-06-10 12:55:05 +02:00
2020-12-20 05:58:23 +01:00
for ( i = 0 ; i < MAX_MON_MOVES ; i + + )
{
if ( moves [ i ] ! = MOVE_NONE & & moves [ i ] ! = 0xFFFF & & gBattleMoves [ moves [ i ] ] . power ! = 0 )
return TRUE ;
}
return FALSE ;
}
bool32 HasDamagingMoveOfType ( u8 battlerId , u8 type )
{
s32 i ;
u16 * moves = GetMovesArray ( battlerId ) ;
for ( i = 0 ; i < MAX_MON_MOVES ; i + + )
{
if ( moves [ i ] ! = MOVE_NONE & & moves [ i ] ! = 0xFFFF
& & gBattleMoves [ moves [ i ] ] . type = = type & & gBattleMoves [ moves [ i ] ] . power ! = 0 )
return TRUE ;
}
2020-12-17 06:56:10 +01:00
return FALSE ;
}
2020-12-13 23:02:21 +01:00
bool32 IsInstructBannedMove ( u16 move )
{
u32 i ;
for ( i = 0 ; i < ARRAY_COUNT ( sInstructBannedMoves ) ; i + + )
{
if ( move = = sInstructBannedMoves [ i ] )
return TRUE ;
}
return FALSE ;
}
2020-12-20 05:58:23 +01:00
bool32 IsEncoreEncouragedEffect ( u16 moveEffect )
{
u32 i ;
2021-06-10 12:55:05 +02:00
2020-12-20 05:58:23 +01:00
for ( i = 0 ; i < ARRAY_COUNT ( sEncouragedEncoreEffects ) ; i + + )
{
if ( moveEffect = = sEncouragedEncoreEffects [ i ] )
return TRUE ;
}
return FALSE ;
}
2020-12-13 23:02:21 +01:00
bool32 MoveRequiresRecharging ( u16 move )
{
u32 i ;
for ( i = 0 ; i < ARRAY_COUNT ( sRechargeMoves ) ; i + + )
{
if ( move = = sRechargeMoves [ i ] )
return TRUE ;
}
return FALSE ;
}
bool32 MoveCallsOtherMove ( u16 move )
{
u32 i ;
for ( i = 0 ; i < ARRAY_COUNT ( sOtherMoveCallingMoves ) ; i + + )
{
if ( move = = sOtherMoveCallingMoves [ i ] )
return TRUE ;
}
return FALSE ;
}
bool32 TestMoveFlagsInMoveset ( u8 battler , u32 flags )
{
s32 i ;
u16 * moves = GetMovesArray ( battler ) ;
for ( i = 0 ; i < MAX_MON_MOVES ; i + + )
{
if ( moves [ i ] ! = MOVE_NONE & & moves [ i ] ! = 0xFFFF & & TestMoveFlags ( moves [ i ] , flags ) )
return TRUE ;
}
return FALSE ;
}
static u32 GetLeechSeedDamage ( u8 battlerId )
{
u32 damage = 0 ;
if ( ( gStatuses3 [ battlerId ] & STATUS3_LEECHSEED )
& & gBattleMons [ gStatuses3 [ battlerId ] & STATUS3_LEECHSEED_BATTLER ] . hp ! = 0 )
{
damage = gBattleMons [ battlerId ] . maxHP / 8 ;
if ( damage = = 0 )
damage = 1 ;
}
return damage ;
}
static u32 GetNightmareDamage ( u8 battlerId )
{
u32 damage = 0 ;
if ( ( gBattleMons [ battlerId ] . status2 & STATUS2_NIGHTMARE ) & & gBattleMons [ battlerId ] . status1 & STATUS1_SLEEP )
{
damage = gBattleMons [ battlerId ] . maxHP / 4 ;
if ( damage = = 0 )
damage = 1 ;
}
return damage ;
}
static u32 GetCurseDamage ( u8 battlerId )
{
u32 damage = 0 ;
if ( gBattleMons [ battlerId ] . status2 & STATUS2_CURSED )
{
damage = gBattleMons [ battlerId ] . maxHP / 4 ;
if ( damage = = 0 )
damage = 1 ;
}
return damage ;
}
static u32 GetTrapDamage ( u8 battlerId )
{
// ai has no knowledge about turns remaining
u32 damage = 0 ;
2022-01-13 17:28:27 +01:00
u32 holdEffect = AI_DATA - > holdEffects [ gBattleStruct - > wrappedBy [ battlerId ] ] ;
2020-12-13 23:02:21 +01:00
if ( gBattleMons [ battlerId ] . status2 & STATUS2_WRAPPED )
{
if ( holdEffect = = HOLD_EFFECT_BINDING_BAND )
2022-08-24 03:00:08 +02:00
# if B_BINDING_DAMAGE >= GEN_6
damage = gBattleMons [ battlerId ] . maxHP / 6 ;
2020-12-13 23:02:21 +01:00
else
2022-08-24 03:00:08 +02:00
damage = gBattleMons [ battlerId ] . maxHP / 8 ;
# else
damage = gBattleMons [ battlerId ] . maxHP / 8 ;
else
damage = gBattleMons [ battlerId ] . maxHP / 16 ;
# endif
2020-12-13 23:02:21 +01:00
if ( damage = = 0 )
damage = 1 ;
}
return damage ;
}
static u32 GetPoisonDamage ( u8 battlerId )
{
u32 damage = 0 ;
2021-06-10 12:55:05 +02:00
2022-01-13 17:28:27 +01:00
if ( AI_DATA - > abilities [ battlerId ] = = ABILITY_POISON_HEAL )
2020-12-13 23:02:21 +01:00
return damage ;
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
if ( gBattleMons [ battlerId ] . status1 & STATUS1_POISON )
{
damage = gBattleMons [ battlerId ] . maxHP / 8 ;
if ( damage = = 0 )
damage = 1 ;
}
else if ( gBattleMons [ battlerId ] . status1 & STATUS1_TOXIC_POISON )
{
damage = gBattleMons [ battlerId ] . maxHP / 16 ;
if ( damage = = 0 )
damage = 1 ;
if ( ( gBattleMons [ battlerId ] . status1 & STATUS1_TOXIC_COUNTER ) ! = STATUS1_TOXIC_TURN ( 15 ) ) // not 16 turns
gBattleMons [ battlerId ] . status1 + = STATUS1_TOXIC_TURN ( 1 ) ;
damage * = ( gBattleMons [ battlerId ] . status1 & STATUS1_TOXIC_COUNTER ) > > 8 ;
}
return damage ;
}
static bool32 BattlerAffectedBySandstorm ( u8 battlerId , u16 ability )
{
if ( ! IS_BATTLER_OF_TYPE ( battlerId , TYPE_ROCK )
& & ! IS_BATTLER_OF_TYPE ( battlerId , TYPE_GROUND )
& & ! IS_BATTLER_OF_TYPE ( battlerId , TYPE_STEEL )
& & ability ! = ABILITY_SAND_VEIL
& & ability ! = ABILITY_SAND_FORCE
& & ability ! = ABILITY_SAND_RUSH
& & ability ! = ABILITY_OVERCOAT )
return TRUE ;
return FALSE ;
}
static bool32 BattlerAffectedByHail ( u8 battlerId , u16 ability )
{
if ( ! IS_BATTLER_OF_TYPE ( battlerId , TYPE_ICE )
& & ability ! = ABILITY_SNOW_CLOAK
& & ability ! = ABILITY_OVERCOAT
& & ability ! = ABILITY_ICE_BODY )
return TRUE ;
return FALSE ;
}
static u32 GetWeatherDamage ( u8 battlerId )
{
2022-01-13 17:28:27 +01:00
u32 ability = AI_DATA - > abilities [ battlerId ] ;
u32 holdEffect = AI_DATA - > holdEffects [ battlerId ] ;
2020-12-13 23:02:21 +01:00
u32 damage = 0 ;
if ( ! AI_WeatherHasEffect ( ) )
return 0 ;
2021-06-10 12:55:05 +02:00
2021-11-21 19:40:26 +01:00
if ( gBattleWeather & B_WEATHER_SANDSTORM )
2020-12-13 23:02:21 +01:00
{
if ( BattlerAffectedBySandstorm ( battlerId , ability )
& & ! ( gStatuses3 [ battlerId ] & ( STATUS3_UNDERGROUND | STATUS3_UNDERWATER ) )
2021-10-09 04:05:01 +02:00
& & holdEffect ! = HOLD_EFFECT_SAFETY_GOGGLES )
2020-12-13 23:02:21 +01:00
{
damage = gBattleMons [ battlerId ] . maxHP / 16 ;
if ( damage = = 0 )
damage = 1 ;
}
}
2021-11-21 19:40:26 +01:00
if ( ( gBattleWeather & B_WEATHER_HAIL ) & & ability ! = ABILITY_ICE_BODY )
2020-12-13 23:02:21 +01:00
{
if ( BattlerAffectedByHail ( battlerId , ability )
& & ! ( gStatuses3 [ battlerId ] & ( STATUS3_UNDERGROUND | STATUS3_UNDERWATER ) )
2021-10-09 04:05:01 +02:00
& & holdEffect ! = HOLD_EFFECT_SAFETY_GOGGLES )
2020-12-13 23:02:21 +01:00
{
damage = gBattleMons [ battlerId ] . maxHP / 16 ;
if ( damage = = 0 )
damage = 1 ;
}
}
return damage ;
}
2020-12-20 05:58:23 +01:00
u32 GetBattlerSecondaryDamage ( u8 battlerId )
2020-12-13 23:02:21 +01:00
{
u32 secondaryDamage ;
2021-06-10 12:55:05 +02:00
2022-01-13 17:28:27 +01:00
if ( AI_DATA - > abilities [ battlerId ] = = ABILITY_MAGIC_GUARD )
2020-12-13 23:02:21 +01:00
return FALSE ;
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
secondaryDamage = GetLeechSeedDamage ( battlerId )
+ GetNightmareDamage ( battlerId )
+ GetCurseDamage ( battlerId )
+ GetTrapDamage ( battlerId )
+ GetPoisonDamage ( battlerId )
+ GetWeatherDamage ( battlerId ) ;
2021-06-10 12:55:05 +02:00
2020-12-20 05:58:23 +01:00
return secondaryDamage ;
2020-12-13 23:02:21 +01:00
}
bool32 BattlerWillFaintFromWeather ( u8 battler , u16 ability )
{
if ( ( BattlerAffectedBySandstorm ( battler , ability ) | | BattlerAffectedByHail ( battler , ability ) )
& & gBattleMons [ battler ] . hp < = gBattleMons [ battler ] . maxHP / 16 )
return TRUE ;
return FALSE ;
}
2020-12-20 05:58:23 +01:00
bool32 BattlerWillFaintFromSecondaryDamage ( u8 battler , u16 ability )
{
if ( GetBattlerSecondaryDamage ( battler ) ! = 0
& & gBattleMons [ battler ] . hp < = gBattleMons [ battler ] . maxHP / 16 )
return TRUE ;
return FALSE ;
}
static bool32 AnyUsefulStatIsRaised ( u8 battler )
{
u8 statId ;
2021-06-10 12:55:05 +02:00
2020-12-20 05:58:23 +01:00
for ( statId = STAT_ATK ; statId < NUM_BATTLE_STATS ; statId + + )
{
if ( gBattleMons [ battler ] . statStages [ statId ] > DEFAULT_STAT_STAGE )
{
switch ( statId )
{
case STAT_ATK :
if ( HasMoveWithSplit ( battler , SPLIT_PHYSICAL ) )
return TRUE ;
break ;
case STAT_SPATK :
if ( HasMoveWithSplit ( battler , SPLIT_SPECIAL ) )
return TRUE ;
break ;
case STAT_SPEED :
return TRUE ;
}
}
}
return FALSE ;
}
2021-11-15 16:48:44 +01:00
struct Pokemon * GetPartyBattlerPartyData ( u8 battlerId , u8 switchBattler )
{
struct Pokemon * mon ;
if ( GetBattlerSide ( battlerId ) = = B_SIDE_PLAYER )
mon = & gPlayerParty [ switchBattler ] ;
else
mon = & gEnemyParty [ switchBattler ] ;
return mon ;
}
2020-12-20 05:58:23 +01:00
static bool32 PartyBattlerShouldAvoidHazards ( u8 currBattler , u8 switchBattler )
{
2021-11-15 16:48:44 +01:00
struct Pokemon * mon = GetPartyBattlerPartyData ( currBattler , switchBattler ) ;
2020-12-20 05:58:23 +01:00
u16 ability = GetMonAbility ( mon ) ; // we know our own party data
u16 holdEffect = GetBattlerHoldEffect ( GetMonData ( mon , MON_DATA_HELD_ITEM ) , TRUE ) ;
u32 flags = gSideStatuses [ GetBattlerSide ( currBattler ) ] & ( SIDE_STATUS_SPIKES | SIDE_STATUS_STEALTH_ROCK | SIDE_STATUS_STICKY_WEB | SIDE_STATUS_TOXIC_SPIKES ) ;
2021-06-10 12:55:05 +02:00
2020-12-20 05:58:23 +01:00
if ( flags = = 0 )
return FALSE ;
2021-06-10 12:55:05 +02:00
2020-12-20 05:58:23 +01:00
if ( ability = = ABILITY_MAGIC_GUARD | | ability = = ABILITY_LEVITATE
| | holdEffect = = HOLD_EFFECT_HEAVY_DUTY_BOOTS )
return FALSE ;
2021-06-10 12:55:05 +02:00
2020-12-20 05:58:23 +01:00
if ( flags & ( SIDE_STATUS_SPIKES | SIDE_STATUS_STEALTH_ROCK ) & & GetMonData ( mon , MON_DATA_HP ) < ( GetMonData ( mon , MON_DATA_MAX_HP ) / 8 ) )
return TRUE ;
2021-06-10 12:55:05 +02:00
2020-12-20 05:58:23 +01:00
return FALSE ;
}
enum {
DONT_PIVOT ,
CAN_TRY_PIVOT ,
PIVOT ,
} ;
bool32 ShouldPivot ( u8 battlerAtk , u8 battlerDef , u16 defAbility , u16 move , u8 moveIndex )
{
bool8 hasStatBoost = AnyUsefulStatIsRaised ( battlerAtk ) | | gBattleMons [ battlerDef ] . statStages [ STAT_EVASION ] > = 9 ; //Significant boost in evasion for any class
u8 backupBattler = gActiveBattler ;
bool32 shouldSwitch ;
u8 battlerToSwitch ;
2021-06-10 12:55:05 +02:00
2020-12-20 05:58:23 +01:00
gActiveBattler = battlerAtk ;
2021-06-10 12:55:05 +02:00
shouldSwitch = ShouldSwitch ( ) ;
2020-12-20 05:58:23 +01:00
battlerToSwitch = * ( gBattleStruct - > AI_monToSwitchIntoId + gActiveBattler ) ;
gActiveBattler = backupBattler ;
if ( PartyBattlerShouldAvoidHazards ( battlerAtk , battlerToSwitch ) )
return DONT_PIVOT ;
if ( ! IsDoubleBattle ( ) )
{
if ( CountUsablePartyMons ( battlerAtk ) = = 0 )
return CAN_TRY_PIVOT ; // can't switch, but attack might still be useful
//TODO - predict opponent switching
/*if (IsPredictedToSwitch(battlerDef, battlerAtk) && !hasStatBoost)
return PIVOT ; // Try pivoting so you can switch to a better matchup to counter your new opponent*/
2022-06-05 17:09:04 +02:00
if ( AI_WhoStrikesFirst ( battlerAtk , battlerDef , move ) = = AI_IS_FASTER ) // Attacker goes first
2020-12-20 05:58:23 +01:00
{
2021-11-08 08:37:28 +01:00
if ( ! CanAIFaintTarget ( battlerAtk , battlerDef , 0 ) ) // Can't KO foe otherwise
2020-12-20 05:58:23 +01:00
{
2021-11-08 08:37:28 +01:00
if ( CanAIFaintTarget ( battlerAtk , battlerDef , 2 ) )
2020-12-20 05:58:23 +01:00
{
// attacker can kill target in two hits (theoretically)
if ( CanTargetFaintAi ( battlerDef , battlerAtk ) )
return PIVOT ; // Won't get the two turns, pivot
if ( ! IS_MOVE_STATUS ( move ) & & ( shouldSwitch
2022-08-24 03:00:08 +02:00
| | ( AtMaxHp ( battlerDef ) & & ( AI_DATA - > holdEffects [ battlerDef ] = = HOLD_EFFECT_FOCUS_SASH
# if B_STURDY >= GEN_5
| | defAbility = = ABILITY_STURDY
# endif
| | defAbility = = ABILITY_MULTISCALE
| | defAbility = = ABILITY_SHADOW_SHIELD ) ) ) )
2020-12-20 05:58:23 +01:00
return PIVOT ; // pivot to break sash/sturdy/multiscale
}
else if ( ! hasStatBoost )
{
2022-01-13 17:28:27 +01:00
if ( ! IS_MOVE_STATUS ( move ) & & ( AtMaxHp ( battlerDef ) & & ( AI_DATA - > holdEffects [ battlerDef ] = = HOLD_EFFECT_FOCUS_SASH
2022-08-24 03:00:08 +02:00
# if B_STURDY >= GEN_5
| | ( defAbility = = ABILITY_STURDY )
# endif
| | defAbility = = ABILITY_MULTISCALE
| | defAbility = = ABILITY_SHADOW_SHIELD ) ) )
2020-12-20 05:58:23 +01:00
return PIVOT ; // pivot to break sash/sturdy/multiscale
if ( shouldSwitch )
return PIVOT ;
/* TODO - check if switchable mon unafffected by/will remove hazards
if ( gSideStatuses [ battlerAtk ] & SIDE_STATUS_SPIKES & & switchScore > = SWITCHING_INCREASE_CAN_REMOVE_HAZARDS )
return PIVOT ; */
2022-01-13 17:28:27 +01:00
/*if (BattlerWillFaintFromSecondaryDamage(battlerAtk, AI_DATA->abilities[battlerAtk]) && switchScore >= SWITCHING_INCREASE_WALLS_FOE)
2020-12-20 05:58:23 +01:00
return PIVOT ; */
/*if (IsClassDamager(class) && switchScore >= SWITCHING_INCREASE_HAS_SUPER_EFFECTIVE_MOVE)
{
bool8 physMoveInMoveset = PhysicalMoveInMoveset ( battlerAtk ) ;
bool8 specMoveInMoveset = SpecialMoveInMoveset ( battlerAtk ) ;
//Pivot if attacking stats are bad
if ( physMoveInMoveset & & ! specMoveInMoveset )
{
if ( STAT_STAGE_ATK < 6 )
return PIVOT ;
}
else if ( ! physMoveInMoveset & & specMoveInMoveset )
{
if ( STAT_STAGE_SPATK < 6 )
return PIVOT ;
}
else if ( physMoveInMoveset & & specMoveInMoveset )
{
if ( STAT_STAGE_ATK < 6 & & STAT_STAGE_SPATK < 6 )
return PIVOT ;
}
return CAN_TRY_PIVOT ;
} */
}
}
}
else // Opponent Goes First
{
if ( CanTargetFaintAi ( battlerDef , battlerAtk ) )
{
if ( gBattleMoves [ move ] . effect = = EFFECT_TELEPORT )
return DONT_PIVOT ; // If you're going to faint because you'll go second, use a different move
else
return CAN_TRY_PIVOT ; // You're probably going to faint anyways so if for some reason you don't, better switch
}
else if ( CanTargetFaintAiWithMod ( battlerDef , battlerAtk , 0 , 2 ) ) // Foe can 2HKO AI
{
2021-11-08 08:37:28 +01:00
if ( CanAIFaintTarget ( battlerAtk , battlerDef , 0 ) )
2020-12-20 05:58:23 +01:00
{
2022-01-13 17:28:27 +01:00
if ( ! BattlerWillFaintFromSecondaryDamage ( battlerAtk , AI_DATA - > abilities [ battlerAtk ] ) )
2020-12-20 05:58:23 +01:00
return CAN_TRY_PIVOT ; // Use this move to KO if you must
}
else // Can't KO the foe
{
return PIVOT ;
}
}
else // Foe can 3HKO+ AI
{
2021-11-08 08:37:28 +01:00
if ( CanAIFaintTarget ( battlerAtk , battlerDef , 0 ) )
2020-12-20 05:58:23 +01:00
{
2022-01-13 17:28:27 +01:00
if ( ! BattlerWillFaintFromSecondaryDamage ( battlerAtk , AI_DATA - > abilities [ battlerAtk ] ) // This is the only move that can KO
2020-12-20 05:58:23 +01:00
& & ! hasStatBoost ) //You're not wasting a valuable stat boost
{
return CAN_TRY_PIVOT ;
}
}
2021-11-08 08:37:28 +01:00
else if ( CanAIFaintTarget ( battlerAtk , battlerDef , 2 ) )
2020-12-20 05:58:23 +01:00
{
// can knock out foe in 2 hits
if ( IS_MOVE_STATUS ( move ) & & ( shouldSwitch //Damaging move
//&& (switchScore >= SWITCHING_INCREASE_RESIST_ALL_MOVES + SWITCHING_INCREASE_KO_FOE //remove hazards
2022-01-13 17:28:27 +01:00
| | ( AI_DATA - > holdEffects [ battlerDef ] = = HOLD_EFFECT_FOCUS_SASH & & AtMaxHp ( battlerDef ) ) ) )
2020-12-20 05:58:23 +01:00
return DONT_PIVOT ; // Pivot to break the sash
else
return CAN_TRY_PIVOT ;
}
else
{
//if (IsClassDamager(class) && switchScore >= SWITCHING_INCREASE_KO_FOE)
//return PIVOT; //Only switch if way better matchup
if ( ! hasStatBoost )
{
// TODO - check if switching prevents/removes hazards
//if (gSideStatuses[battlerAtk] & SIDE_STATUS_SPIKES && switchScore >= SWITCHING_INCREASE_CAN_REMOVE_HAZARDS)
//return PIVOT;
// TODO - not always a good idea
//if (BattlerWillFaintFromSecondaryDamage(battlerAtk) && switchScore >= SWITCHING_INCREASE_HAS_SUPER_EFFECTIVE_MOVE)
//return PIVOT;
/*if (IsClassDamager(class) && switchScore >= SWITCHING_INCREASE_HAS_SUPER_EFFECTIVE_MOVE)
{
bool8 physMoveInMoveset = PhysicalMoveInMoveset ( battlerAtk ) ;
bool8 specMoveInMoveset = SpecialMoveInMoveset ( battlerAtk ) ;
//Pivot if attacking stats are bad
if ( physMoveInMoveset & & ! specMoveInMoveset )
{
if ( STAT_STAGE_ATK < 6 )
return PIVOT ;
}
else if ( ! physMoveInMoveset & & specMoveInMoveset )
{
if ( STAT_STAGE_SPATK < 6 )
return PIVOT ;
}
else if ( physMoveInMoveset & & specMoveInMoveset )
{
if ( STAT_STAGE_ATK < 6 & & STAT_STAGE_SPATK < 6 )
return PIVOT ;
}
} */
return CAN_TRY_PIVOT ;
}
}
}
}
}
return DONT_PIVOT ;
}
bool32 CanKnockOffItem ( u8 battler , u16 item )
{
if ( item = = ITEM_NONE )
return FALSE ;
if ( ! ( gBattleTypeFlags & ( BATTLE_TYPE_EREADER_TRAINER
| BATTLE_TYPE_FRONTIER
| BATTLE_TYPE_LINK
2021-02-12 19:02:10 +01:00
| BATTLE_TYPE_RECORDED_LINK
2020-12-20 05:58:23 +01:00
| BATTLE_TYPE_SECRET_BASE
2022-08-24 03:00:08 +02:00
# if B_TRAINERS_KNOCK_OFF_ITEMS == TRUE
2020-12-20 05:58:23 +01:00
| BATTLE_TYPE_TRAINER
# endif
) ) & & GetBattlerSide ( battler ) = = B_SIDE_PLAYER )
return FALSE ;
2021-06-10 12:55:05 +02:00
2022-01-13 17:28:27 +01:00
if ( AI_DATA - > abilities [ battler ] = = ABILITY_STICKY_HOLD )
2020-12-20 05:58:23 +01:00
return FALSE ;
if ( ! CanBattlerGetOrLoseItem ( battler , item ) )
return FALSE ;
return TRUE ;
}
2020-12-13 23:02:21 +01:00
// status checks
2020-12-20 05:58:23 +01:00
bool32 IsBattlerIncapacitated ( u8 battler , u16 ability )
{
if ( ( gBattleMons [ battler ] . status1 & STATUS1_FREEZE ) & & ! HasThawingMove ( battler ) )
return TRUE ; // if battler has thawing move we assume they will definitely use it, and thus being frozen should be neglected
2021-06-10 12:55:05 +02:00
2020-12-20 05:58:23 +01:00
if ( gBattleMons [ battler ] . status1 & STATUS1_SLEEP )
return TRUE ;
if ( gBattleMons [ battler ] . status2 & STATUS2_RECHARGE | | ( ability = = ABILITY_TRUANT & & gDisableStructs [ battler ] . truantCounter ! = 0 ) )
return TRUE ;
return FALSE ;
}
2021-08-12 21:37:53 +02:00
bool32 AI_CanSleep ( u8 battler , u16 ability )
2020-12-20 05:58:23 +01:00
{
if ( ability = = ABILITY_INSOMNIA
| | ability = = ABILITY_VITAL_SPIRIT
| | gBattleMons [ battler ] . status1 & STATUS1_ANY
| | gSideStatuses [ GetBattlerSide ( battler ) ] & SIDE_STATUS_SAFEGUARD
| | ( gFieldStatuses & ( STATUS_FIELD_MISTY_TERRAIN | STATUS_FIELD_ELECTRIC_TERRAIN ) )
| | IsAbilityStatusProtected ( battler ) )
return FALSE ;
return TRUE ;
}
2020-12-16 05:57:33 +01:00
bool32 AI_CanPutToSleep ( u8 battlerAtk , u8 battlerDef , u16 defAbility , u16 move , u16 partnerMove )
2020-12-13 23:02:21 +01:00
{
2021-08-12 21:37:53 +02:00
if ( ! AI_CanSleep ( battlerDef , defAbility )
2020-12-13 23:02:21 +01:00
| | DoesSubstituteBlockMove ( battlerAtk , battlerDef , move )
| | PartnerMoveEffectIsStatusSameTarget ( BATTLE_PARTNER ( battlerAtk ) , battlerDef , partnerMove ) ) // shouldn't try to sleep mon that partner is trying to make sleep
return FALSE ;
return TRUE ;
}
2021-11-04 02:36:33 +01:00
static bool32 AI_CanPoisonType ( u8 battlerAttacker , u8 battlerTarget )
2020-12-20 05:58:23 +01:00
{
2022-01-13 17:28:27 +01:00
return ( ( AI_DATA - > abilities [ battlerAttacker ] = = ABILITY_CORROSION & & gBattleMoves [ gCurrentMove ] . split = = SPLIT_STATUS )
2021-11-04 02:36:33 +01:00
| | ! ( IS_BATTLER_OF_TYPE ( battlerTarget , TYPE_POISON ) | | IS_BATTLER_OF_TYPE ( battlerTarget , TYPE_STEEL ) ) ) ;
}
static bool32 AI_CanBePoisoned ( u8 battlerAtk , u8 battlerDef )
{
2022-01-13 17:28:27 +01:00
u16 ability = AI_DATA - > abilities [ battlerDef ] ;
2022-08-23 01:07:25 +02:00
2021-11-04 02:36:33 +01:00
if ( ! ( AI_CanPoisonType ( battlerAtk , battlerDef ) )
| | gSideStatuses [ GetBattlerSide ( battlerDef ) ] & SIDE_STATUS_SAFEGUARD
| | gBattleMons [ battlerDef ] . status1 & STATUS1_ANY
| | ability = = ABILITY_IMMUNITY
| | ability = = ABILITY_COMATOSE
| | AI_IsAbilityOnSide ( battlerDef , ABILITY_PASTEL_VEIL )
| | gBattleMons [ battlerDef ] . status1 & STATUS1_ANY
| | IsAbilityStatusProtected ( battlerDef )
| | AI_IsTerrainAffected ( battlerDef , STATUS_FIELD_MISTY_TERRAIN ) )
2020-12-20 05:58:23 +01:00
return FALSE ;
return TRUE ;
}
bool32 ShouldPoisonSelf ( u8 battler , u16 ability )
{
2021-11-04 02:36:33 +01:00
if ( AI_CanBePoisoned ( battler , battler ) & & (
2020-12-20 05:58:23 +01:00
ability = = ABILITY_MARVEL_SCALE
| | ability = = ABILITY_POISON_HEAL
| | ability = = ABILITY_QUICK_FEET
| | ability = = ABILITY_MAGIC_GUARD
| | ( ability = = ABILITY_TOXIC_BOOST & & HasMoveWithSplit ( battler , SPLIT_PHYSICAL ) )
| | ( ability = = ABILITY_GUTS & & HasMoveWithSplit ( battler , SPLIT_PHYSICAL ) )
| | HasMoveEffect ( battler , EFFECT_FACADE )
| | HasMoveEffect ( battler , EFFECT_PSYCHO_SHIFT ) ) )
2021-01-04 23:30:02 +01:00
return TRUE ; // battler can be poisoned and has move/ability that synergizes with being poisoned
2020-12-20 05:58:23 +01:00
return FALSE ;
}
2020-12-16 05:57:33 +01:00
bool32 AI_CanPoison ( u8 battlerAtk , u8 battlerDef , u16 defAbility , u16 move , u16 partnerMove )
2020-12-13 23:02:21 +01:00
{
2021-11-04 02:36:33 +01:00
if ( ! AI_CanBePoisoned ( battlerAtk , battlerDef )
2021-01-26 23:41:59 +01:00
| | AI_GetMoveEffectiveness ( move , battlerAtk , battlerDef ) = = AI_EFFECTIVENESS_x0
2020-12-13 23:02:21 +01:00
| | DoesSubstituteBlockMove ( battlerAtk , battlerDef , move )
| | PartnerMoveEffectIsStatusSameTarget ( BATTLE_PARTNER ( battlerAtk ) , battlerDef , partnerMove ) )
return FALSE ;
else if ( defAbility ! = ABILITY_CORROSION & & ( IS_BATTLER_OF_TYPE ( battlerDef , TYPE_POISON ) | | IS_BATTLER_OF_TYPE ( battlerDef , TYPE_STEEL ) ) )
return FALSE ;
2022-01-13 17:28:27 +01:00
else if ( IsValidDoubleBattle ( battlerAtk ) & & AI_DATA - > abilities [ BATTLE_PARTNER ( battlerDef ) ] = = ABILITY_PASTEL_VEIL )
2020-12-13 23:02:21 +01:00
return FALSE ;
return TRUE ;
}
2021-10-24 07:49:17 +02:00
static bool32 AI_CanBeParalyzed ( u8 battler , u16 ability )
2020-12-13 23:02:21 +01:00
{
2021-01-15 17:23:23 +01:00
if ( ability = = ABILITY_LIMBER
| | IS_BATTLER_OF_TYPE ( battler , TYPE_ELECTRIC )
| | gBattleMons [ battler ] . status1 & STATUS1_ANY
| | IsAbilityStatusProtected ( battler ) )
2021-01-04 23:30:02 +01:00
return FALSE ;
return TRUE ;
}
bool32 AI_CanParalyze ( u8 battlerAtk , u8 battlerDef , u16 defAbility , u16 move , u16 partnerMove )
{
2021-10-24 07:49:17 +02:00
if ( ! AI_CanBeParalyzed ( battlerDef , defAbility )
2021-01-26 23:41:59 +01:00
| | AI_GetMoveEffectiveness ( move , battlerAtk , battlerDef ) = = AI_EFFECTIVENESS_x0
2020-12-20 05:58:23 +01:00
| | gSideStatuses [ GetBattlerSide ( battlerDef ) ] & SIDE_STATUS_SAFEGUARD
2020-12-13 23:02:21 +01:00
| | DoesSubstituteBlockMove ( battlerAtk , battlerDef , move )
| | PartnerMoveEffectIsStatusSameTarget ( BATTLE_PARTNER ( battlerAtk ) , battlerDef , partnerMove ) )
return FALSE ;
return TRUE ;
}
2021-08-12 21:37:53 +02:00
bool32 AI_CanBeConfused ( u8 battler , u16 ability )
2020-12-20 22:47:20 +01:00
{
if ( ( gBattleMons [ battler ] . status2 & STATUS2_CONFUSION )
| | ( ability = = ABILITY_OWN_TEMPO )
| | ( IsBattlerGrounded ( battler ) & & ( gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN ) ) )
return FALSE ;
return TRUE ;
}
2020-12-13 23:02:21 +01:00
bool32 AI_CanConfuse ( u8 battlerAtk , u8 battlerDef , u16 defAbility , u8 battlerAtkPartner , u16 move , u16 partnerMove )
{
2021-08-12 21:37:53 +02:00
if ( ! AI_CanBeConfused ( battlerDef , defAbility )
2021-01-26 23:41:59 +01:00
| | AI_GetMoveEffectiveness ( move , battlerAtk , battlerDef ) = = AI_EFFECTIVENESS_x0
2020-12-20 05:58:23 +01:00
| | gSideStatuses [ GetBattlerSide ( battlerDef ) ] & SIDE_STATUS_SAFEGUARD
2020-12-13 23:02:21 +01:00
| | DoesSubstituteBlockMove ( battlerAtk , battlerDef , move )
| | DoesPartnerHaveSameMoveEffect ( battlerAtkPartner , battlerDef , move , partnerMove ) )
{
return FALSE ;
}
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
return TRUE ;
}
2021-08-12 21:37:53 +02:00
bool32 AI_CanBeBurned ( u8 battler , u16 ability )
2020-12-20 05:58:23 +01:00
{
if ( ability = = ABILITY_WATER_VEIL
| | ability = = ABILITY_WATER_BUBBLE
| | IS_BATTLER_OF_TYPE ( battler , TYPE_FIRE )
| | gBattleMons [ battler ] . status1 & STATUS1_ANY
| | IsAbilityStatusProtected ( battler )
| | gSideStatuses [ GetBattlerSide ( battler ) ] & SIDE_STATUS_SAFEGUARD )
return FALSE ;
return TRUE ;
}
bool32 ShouldBurnSelf ( u8 battler , u16 ability )
{
2021-08-12 21:37:53 +02:00
if ( AI_CanBeBurned ( battler , ability ) & & (
2020-12-20 05:58:23 +01:00
ability = = ABILITY_QUICK_FEET
| | ability = = ABILITY_HEATPROOF
| | ability = = ABILITY_MAGIC_GUARD
| | ( ability = = ABILITY_FLARE_BOOST & & HasMoveWithSplit ( battler , SPLIT_SPECIAL ) )
| | ( ability = = ABILITY_GUTS & & HasMoveWithSplit ( battler , SPLIT_PHYSICAL ) )
| | HasMoveEffect ( battler , EFFECT_FACADE )
| | HasMoveEffect ( battler , EFFECT_PSYCHO_SHIFT ) ) )
return TRUE ;
return FALSE ;
}
2020-12-13 23:02:21 +01:00
bool32 AI_CanBurn ( u8 battlerAtk , u8 battlerDef , u16 defAbility , u8 battlerAtkPartner , u16 move , u16 partnerMove )
{
2021-08-12 21:37:53 +02:00
if ( ! AI_CanBeBurned ( battlerDef , defAbility )
2021-01-26 23:41:59 +01:00
| | AI_GetMoveEffectiveness ( move , battlerAtk , battlerDef ) = = AI_EFFECTIVENESS_x0
2020-12-13 23:02:21 +01:00
| | DoesSubstituteBlockMove ( battlerAtk , battlerDef , move )
| | PartnerMoveEffectIsStatusSameTarget ( battlerAtkPartner , battlerDef , partnerMove ) )
{
return FALSE ;
}
return TRUE ;
}
bool32 AI_CanBeInfatuated ( u8 battlerAtk , u8 battlerDef , u16 defAbility , u8 atkGender , u8 defGender )
{
2021-01-04 23:30:02 +01:00
if ( ( gBattleMons [ battlerDef ] . status2 & STATUS2_INFATUATION )
2021-01-26 23:41:59 +01:00
| | AI_GetMoveEffectiveness ( AI_THINKING_STRUCT - > moveConsidered , battlerAtk , battlerDef ) = = AI_EFFECTIVENESS_x0
2021-01-04 23:30:02 +01:00
| | defAbility = = ABILITY_OBLIVIOUS
| | atkGender = = defGender
| | atkGender = = MON_GENDERLESS
| | defGender = = MON_GENDERLESS
2021-11-04 02:36:33 +01:00
| | AI_IsAbilityOnSide ( battlerDef , ABILITY_AROMA_VEIL ) )
2020-12-13 23:02:21 +01:00
return FALSE ;
return TRUE ;
}
2020-12-20 05:58:23 +01:00
u32 ShouldTryToFlinch ( u8 battlerAtk , u8 battlerDef , u16 atkAbility , u16 defAbility , u16 move )
{
if ( defAbility = = ABILITY_INNER_FOCUS
| | DoesSubstituteBlockMove ( battlerAtk , battlerDef , move )
2022-06-05 17:09:04 +02:00
| | AI_WhoStrikesFirst ( battlerAtk , battlerDef , move ) = = AI_IS_SLOWER ) // Opponent goes first
2020-12-20 05:58:23 +01:00
{
return 0 ; // don't try to flinch
}
else if ( ( gBattleMons [ battlerDef ] . status1 & STATUS1_SLEEP ) & & ! HasMoveEffect ( battlerDef , EFFECT_SLEEP_TALK ) & & ! HasMoveEffect ( battlerDef , EFFECT_SNORE ) )
{
return 0 ; // don't try to flinch sleeping pokemon
}
else if ( atkAbility = = ABILITY_SERENE_GRACE
| | gBattleMons [ battlerDef ] . status1 & STATUS1_PARALYSIS
| | gBattleMons [ battlerDef ] . status2 & STATUS2_INFATUATION
| | gBattleMons [ battlerDef ] . status2 & STATUS2_CONFUSION )
{
return 2 ; // good idea to flinch
}
return 1 ; // decent idea to flinch
}
bool32 ShouldTrap ( u8 battlerAtk , u8 battlerDef , u16 move )
{
2022-01-13 17:28:27 +01:00
if ( BattlerWillFaintFromSecondaryDamage ( battlerDef , AI_DATA - > abilities [ battlerDef ] ) )
2020-12-20 05:58:23 +01:00
return TRUE ; // battler is taking secondary damage with low HP
2021-06-10 12:55:05 +02:00
2020-12-20 05:58:23 +01:00
if ( AI_THINKING_STRUCT - > aiFlags & AI_FLAG_STALL )
{
if ( ! CanTargetFaintAi ( battlerDef , battlerAtk ) )
return TRUE ; // attacker goes first and opponent can't kill us
}
return FALSE ;
}
bool32 ShouldFakeOut ( u8 battlerAtk , u8 battlerDef , u16 move )
{
2022-01-13 17:28:27 +01:00
if ( AI_DATA - > holdEffects [ battlerAtk ] = = HOLD_EFFECT_CHOICE_BAND & & CountUsablePartyMons ( battlerAtk ) = = 0 )
2020-12-20 05:58:23 +01:00
return FALSE ; // don't lock attacker into fake out if can't switch out
2021-06-10 12:55:05 +02:00
2020-12-20 05:58:23 +01:00
if ( gDisableStructs [ battlerAtk ] . isFirstTurn
2022-01-13 17:28:27 +01:00
& & ShouldTryToFlinch ( battlerAtk , battlerDef , AI_DATA - > abilities [ battlerAtk ] , AI_DATA - > abilities [ battlerDef ] , move )
2020-12-20 05:58:23 +01:00
& & ! DoesSubstituteBlockMove ( battlerAtk , battlerDef , move ) )
return TRUE ;
2021-06-10 12:55:05 +02:00
2020-12-20 05:58:23 +01:00
return FALSE ;
}
static u32 FindMoveUsedXTurnsAgo ( u32 battlerId , u32 x )
{
s32 i , index = BATTLE_HISTORY - > moveHistoryIndex [ battlerId ] ;
for ( i = 0 ; i < x ; i + + )
{
if ( - - index < 0 )
index = AI_MOVE_HISTORY_COUNT - 1 ;
}
return BATTLE_HISTORY - > moveHistory [ battlerId ] [ index ] ;
}
bool32 IsWakeupTurn ( u8 battler )
{
// Check if rest was used 2 turns ago
if ( ( gBattleMons [ battler ] . status1 & STATUS1_SLEEP ) = = 1 & & FindMoveUsedXTurnsAgo ( battler , 2 ) = = MOVE_REST )
return TRUE ;
else // no way to know
return FALSE ;
}
2020-12-13 23:02:21 +01:00
bool32 AnyPartyMemberStatused ( u8 battlerId , bool32 checkSoundproof )
{
struct Pokemon * party ;
u32 i ;
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
if ( GetBattlerSide ( battlerId ) = = B_SIDE_PLAYER )
party = gPlayerParty ;
else
party = gEnemyParty ;
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
for ( i = 0 ; i < PARTY_SIZE ; i + + )
{
if ( checkSoundproof & & GetMonAbility ( & party [ i ] ) = = ABILITY_SOUNDPROOF )
continue ;
if ( GetMonData ( & party [ i ] , MON_DATA_STATUS ) ! = STATUS1_NONE )
return TRUE ;
}
return FALSE ;
}
u16 GetBattlerSideSpeedAverage ( u8 battler )
{
u16 speed1 = 0 ;
u16 speed2 = 0 ;
u8 numBattlersAlive = 0 ;
if ( IsBattlerAlive ( battler ) )
{
speed1 = GetBattlerTotalSpeedStat ( battler ) ;
numBattlersAlive + + ;
}
if ( IsDoubleBattle ( ) & & IsBattlerAlive ( BATTLE_PARTNER ( battler ) ) )
{
speed2 = GetBattlerTotalSpeedStat ( BATTLE_PARTNER ( battler ) ) ;
numBattlersAlive + + ;
}
return ( speed1 + speed2 ) / numBattlersAlive ;
}
bool32 ShouldUseRecoilMove ( u8 battlerAtk , u8 battlerDef , u32 recoilDmg , u8 moveIndex )
{
if ( recoilDmg > = gBattleMons [ battlerAtk ] . hp //Recoil kills attacker
2020-12-20 05:58:23 +01:00
& & CountUsablePartyMons ( battlerDef ) ! = 0 ) //Foe has more than 1 target left
2020-12-13 23:02:21 +01:00
{
2021-11-08 08:37:28 +01:00
if ( recoilDmg > = gBattleMons [ battlerDef ] . hp & & ! CanAIFaintTarget ( battlerAtk , battlerDef , 0 ) )
2020-12-13 23:02:21 +01:00
return TRUE ; //If it's the only KO move then just use it
else
return FALSE ; //Not as good to use move if you'll faint and not win
}
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
return TRUE ;
}
2020-12-20 05:58:23 +01:00
bool32 ShouldAbsorb ( u8 battlerAtk , u8 battlerDef , u16 move , s32 damage )
2021-06-10 12:55:05 +02:00
{
2022-06-05 17:09:04 +02:00
if ( move = = 0xFFFF | | AI_WhoStrikesFirst ( battlerAtk , battlerDef , move ) = = AI_IS_FASTER )
2020-12-16 05:57:33 +01:00
{
// using item or user goes first
u8 healPercent = ( gBattleMoves [ move ] . argument = = 0 ) ? 50 : gBattleMoves [ move ] . argument ;
s32 healDmg = ( healPercent * damage ) / 100 ;
2021-06-10 12:55:05 +02:00
2020-12-20 05:58:23 +01:00
if ( gStatuses3 [ battlerAtk ] & STATUS3_HEAL_BLOCK )
healDmg = 0 ;
2021-06-10 12:55:05 +02:00
2020-12-16 05:57:33 +01:00
if ( CanTargetFaintAi ( battlerDef , battlerAtk )
& & ! CanTargetFaintAiWithMod ( battlerDef , battlerAtk , healDmg , 0 ) )
return TRUE ; // target can faint attacker unless they heal
2022-01-13 17:28:27 +01:00
else if ( ! CanTargetFaintAi ( battlerDef , battlerAtk ) & & AI_DATA - > hpPercents [ battlerAtk ] < 60 & & ( Random ( ) % 3 ) )
2020-12-16 05:57:33 +01:00
return TRUE ; // target can't faint attacker at all, attacker health is about half, 2/3rds rate of encouraging healing
}
else
{
// opponent goes first
if ( ! CanTargetFaintAi ( battlerDef , battlerAtk ) )
return TRUE ;
}
2021-06-10 12:55:05 +02:00
2020-12-16 05:57:33 +01:00
return FALSE ;
}
2020-12-20 05:58:23 +01:00
bool32 ShouldRecover ( u8 battlerAtk , u8 battlerDef , u16 move , u8 healPercent )
{
2022-06-05 17:09:04 +02:00
if ( move = = 0xFFFF | | AI_WhoStrikesFirst ( battlerAtk , battlerDef , move ) = = AI_IS_FASTER )
2020-12-20 05:58:23 +01:00
{
// using item or user going first
2022-01-13 17:28:27 +01:00
s32 damage = AI_DATA - > simulatedDmg [ battlerAtk ] [ battlerDef ] [ AI_THINKING_STRUCT - > movesetIndex ] ;
2020-12-20 05:58:23 +01:00
s32 healAmount = ( healPercent * damage ) / 100 ;
if ( gStatuses3 [ battlerAtk ] & STATUS3_HEAL_BLOCK )
healAmount = 0 ;
if ( CanTargetFaintAi ( battlerDef , battlerAtk )
& & ! CanTargetFaintAiWithMod ( battlerDef , battlerAtk , healAmount , 0 ) )
return TRUE ; // target can faint attacker unless they heal
2022-01-13 17:28:27 +01:00
else if ( ! CanTargetFaintAi ( battlerDef , battlerAtk ) & & AI_DATA - > hpPercents [ battlerAtk ] < 60 & & ( Random ( ) % 3 ) )
2020-12-20 05:58:23 +01:00
return TRUE ; // target can't faint attacker at all, attacker health is about half, 2/3rds rate of encouraging healing
}
return FALSE ;
}
bool32 ShouldSetScreen ( u8 battlerAtk , u8 battlerDef , u16 moveEffect )
{
u8 atkSide = GetBattlerSide ( battlerAtk ) ;
2021-02-14 17:11:29 +01:00
switch ( moveEffect )
2020-12-20 05:58:23 +01:00
{
2021-02-14 17:11:29 +01:00
case EFFECT_AURORA_VEIL :
// Use only in Hail and only if AI doesn't already have Reflect, Light Screen or Aurora Veil itself active.
2021-11-21 19:40:26 +01:00
if ( gBattleWeather & B_WEATHER_HAIL
2021-02-14 17:11:29 +01:00
& & ! ( gSideStatuses [ atkSide ] & ( SIDE_STATUS_REFLECT | SIDE_STATUS_LIGHTSCREEN | SIDE_STATUS_AURORA_VEIL ) ) )
return TRUE ;
break ;
case EFFECT_REFLECT :
// Use only if the player has a physical move and AI doesn't already have Reflect itself active.
if ( HasMoveWithSplit ( battlerDef , SPLIT_PHYSICAL )
& & ! ( gSideStatuses [ atkSide ] & SIDE_STATUS_REFLECT ) )
return TRUE ;
break ;
case EFFECT_LIGHT_SCREEN :
// Use only if the player has a special move and AI doesn't already have Light Screen itself active.
if ( HasMoveWithSplit ( battlerDef , SPLIT_SPECIAL )
& & ! ( gSideStatuses [ atkSide ] & SIDE_STATUS_LIGHTSCREEN ) )
return TRUE ;
break ;
2020-12-20 05:58:23 +01:00
}
2021-02-14 17:11:29 +01:00
2020-12-20 05:58:23 +01:00
return FALSE ;
}
2020-12-13 23:02:21 +01:00
// Partner Logic
bool32 IsValidDoubleBattle ( u8 battlerAtk )
{
if ( IsDoubleBattle ( )
& & ( ( IsBattlerAlive ( BATTLE_OPPOSITE ( battlerAtk ) ) & & IsBattlerAlive ( BATTLE_PARTNER ( BATTLE_OPPOSITE ( battlerAtk ) ) ) ) | | IsBattlerAlive ( BATTLE_PARTNER ( battlerAtk ) ) ) )
return TRUE ;
return FALSE ;
}
2022-01-13 17:28:27 +01:00
u16 GetAllyChosenMove ( u8 battlerId )
2020-12-13 23:02:21 +01:00
{
2022-01-13 17:28:27 +01:00
u8 partnerBattler = BATTLE_PARTNER ( battlerId ) ;
2022-08-23 01:07:25 +02:00
2020-12-13 23:02:21 +01:00
if ( ! IsBattlerAlive ( partnerBattler ) | | ! IsBattlerAIControlled ( partnerBattler ) )
return MOVE_NONE ;
2022-01-13 17:28:27 +01:00
else if ( partnerBattler > battlerId ) // Battler with the lower id chooses the move first.
return gLastMoves [ partnerBattler ] ;
2020-12-13 23:02:21 +01:00
else
return gBattleMons [ partnerBattler ] . moves [ gBattleStruct - > chosenMovePositions [ partnerBattler ] ] ;
}
bool32 IsTargetingPartner ( u8 battlerAtk , u8 battlerDef )
{
2021-01-04 23:30:02 +01:00
if ( ( battlerAtk & BIT_SIDE ) = = ( battlerDef & BIT_SIDE ) )
2020-12-13 23:02:21 +01:00
return TRUE ;
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
return FALSE ;
}
//PARTNER_MOVE_EFFECT_IS_SAME
bool32 DoesPartnerHaveSameMoveEffect ( u8 battlerAtkPartner , u8 battlerDef , u16 move , u16 partnerMove )
{
if ( ! IsDoubleBattle ( ) )
return FALSE ;
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
if ( gBattleMoves [ move ] . effect = = gBattleMoves [ partnerMove ] . effect
& & gChosenMoveByBattler [ battlerAtkPartner ] ! = MOVE_NONE
& & gBattleStruct - > moveTarget [ battlerAtkPartner ] = = battlerDef )
{
return TRUE ;
}
return FALSE ;
}
//PARTNER_MOVE_EFFECT_IS_SAME_NO_TARGET
bool32 PartnerHasSameMoveEffectWithoutTarget ( u8 battlerAtkPartner , u16 move , u16 partnerMove )
{
if ( ! IsDoubleBattle ( ) )
return FALSE ;
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
if ( gBattleMoves [ move ] . effect = = gBattleMoves [ partnerMove ] . effect
& & gChosenMoveByBattler [ battlerAtkPartner ] ! = MOVE_NONE )
return TRUE ;
return FALSE ;
}
//PARTNER_MOVE_EFFECT_IS_STATUS_SAME_TARGET
bool32 PartnerMoveEffectIsStatusSameTarget ( u8 battlerAtkPartner , u8 battlerDef , u16 partnerMove )
{
if ( ! IsDoubleBattle ( ) )
return FALSE ;
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
if ( gChosenMoveByBattler [ battlerAtkPartner ] ! = MOVE_NONE
& & gBattleStruct - > moveTarget [ battlerAtkPartner ] = = battlerDef
& & ( gBattleMoves [ partnerMove ] . effect = = EFFECT_SLEEP
| | gBattleMoves [ partnerMove ] . effect = = EFFECT_POISON
| | gBattleMoves [ partnerMove ] . effect = = EFFECT_TOXIC
| | gBattleMoves [ partnerMove ] . effect = = EFFECT_PARALYZE
| | gBattleMoves [ partnerMove ] . effect = = EFFECT_WILL_O_WISP
| | gBattleMoves [ partnerMove ] . effect = = EFFECT_YAWN ) )
return TRUE ;
return FALSE ;
}
//PARTNER_MOVE_EFFECT_IS_WEATHER
bool32 PartnerMoveEffectIsWeather ( u8 battlerAtkPartner , u16 partnerMove )
{
if ( ! IsDoubleBattle ( ) )
return FALSE ;
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
if ( gChosenMoveByBattler [ battlerAtkPartner ] ! = MOVE_NONE
& & ( gBattleMoves [ partnerMove ] . effect = = EFFECT_SUNNY_DAY
| | gBattleMoves [ partnerMove ] . effect = = EFFECT_RAIN_DANCE
| | gBattleMoves [ partnerMove ] . effect = = EFFECT_SANDSTORM
| | gBattleMoves [ partnerMove ] . effect = = EFFECT_HAIL ) )
return TRUE ;
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
return FALSE ;
}
//PARTNER_MOVE_EFFECT_IS_TERRAIN
bool32 PartnerMoveEffectIsTerrain ( u8 battlerAtkPartner , u16 partnerMove )
{
if ( ! IsDoubleBattle ( ) )
return FALSE ;
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
if ( gChosenMoveByBattler [ battlerAtkPartner ] ! = MOVE_NONE
& & ( gBattleMoves [ partnerMove ] . effect = = EFFECT_GRASSY_TERRAIN
| | gBattleMoves [ partnerMove ] . effect = = EFFECT_MISTY_TERRAIN
| | gBattleMoves [ partnerMove ] . effect = = EFFECT_ELECTRIC_TERRAIN
| | gBattleMoves [ partnerMove ] . effect = = EFFECT_PSYCHIC_TERRAIN ) )
return TRUE ;
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
return FALSE ;
}
//PARTNER_MOVE_IS_TAILWIND_TRICKROOM
bool32 PartnerMoveIs ( u8 battlerAtkPartner , u16 partnerMove , u16 moveCheck )
{
if ( ! IsDoubleBattle ( ) )
return FALSE ;
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
if ( gChosenMoveByBattler [ battlerAtkPartner ] ! = MOVE_NONE & & partnerMove = = moveCheck )
return TRUE ;
return FALSE ;
}
//PARTNER_MOVE_IS_SAME
bool32 PartnerMoveIsSameAsAttacker ( u8 battlerAtkPartner , u8 battlerDef , u16 move , u16 partnerMove )
{
if ( ! IsDoubleBattle ( ) )
return FALSE ;
2021-06-10 12:55:05 +02:00
2020-12-13 23:02:21 +01:00
if ( gChosenMoveByBattler [ battlerAtkPartner ] ! = MOVE_NONE & & move = = partnerMove & & gBattleStruct - > moveTarget [ battlerAtkPartner ] = = battlerDef )
return TRUE ;
return FALSE ;
}
//PARTNER_MOVE_IS_SAME_NO_TARGET
bool32 PartnerMoveIsSameNoTarget ( u8 battlerAtkPartner , u16 move , u16 partnerMove )
{
if ( ! IsDoubleBattle ( ) )
return FALSE ;
if ( gChosenMoveByBattler [ battlerAtkPartner ] ! = MOVE_NONE & & move = = partnerMove )
return TRUE ;
return FALSE ;
2020-12-11 16:05:00 +01:00
}
2020-12-20 05:58:23 +01:00
bool32 ShouldUseWishAromatherapy ( u8 battlerAtk , u8 battlerDef , u16 move )
{
u32 i ;
u32 firstId , lastId ;
struct Pokemon * party ;
bool32 hasStatus = FALSE ;
bool32 needHealing = FALSE ;
GetAIPartyIndexes ( battlerAtk , & firstId , & lastId ) ;
2021-06-10 12:55:05 +02:00
2020-12-20 05:58:23 +01:00
if ( GetBattlerSide ( gActiveBattler ) = = B_SIDE_PLAYER )
party = gPlayerParty ;
else
party = gEnemyParty ;
if ( CountUsablePartyMons ( battlerAtk ) = = 0
2022-01-13 17:28:27 +01:00
& & ( CanTargetFaintAi ( battlerDef , battlerAtk ) | | BattlerWillFaintFromSecondaryDamage ( battlerAtk , AI_DATA - > abilities [ battlerAtk ] ) ) )
2020-12-20 05:58:23 +01:00
return FALSE ; // Don't heal if last mon and will faint
for ( i = 0 ; i < PARTY_SIZE ; i + + )
{
u16 currHp = GetMonData ( & party [ i ] , MON_DATA_HP ) ;
u16 maxHp = GetMonData ( & party [ i ] , MON_DATA_MAX_HP ) ;
if ( ! GetMonData ( & party [ i ] , MON_DATA_IS_EGG , NULL ) & & currHp > 0 )
{
if ( ( currHp * 100 ) / maxHp < 65 // Less than 65% health remaining
& & i > = firstId & & i < lastId ) // Can only switch to mon on your team
{
needHealing = TRUE ;
}
if ( GetMonData ( & party [ i ] , MON_DATA_STATUS , NULL ) ! = STATUS1_NONE )
{
if ( move ! = MOVE_HEAL_BELL | | GetMonAbility ( & party [ i ] ) ! = ABILITY_SOUNDPROOF )
hasStatus = TRUE ;
}
}
}
if ( ! IsDoubleBattle ( ) )
{
switch ( gBattleMoves [ move ] . effect )
{
case EFFECT_WISH :
if ( needHealing )
return TRUE ;
break ;
case EFFECT_HEAL_BELL :
if ( hasStatus )
return TRUE ;
}
}
else
{
switch ( gBattleMoves [ move ] . effect )
{
case EFFECT_WISH :
return ShouldRecover ( battlerAtk , battlerDef , move , 50 ) ; // Switch recovery isn't good idea in doubles
case EFFECT_HEAL_BELL :
if ( hasStatus )
return TRUE ;
}
}
return FALSE ;
}
2020-12-16 05:57:33 +01:00
// party logic
s32 AI_CalcPartyMonDamage ( u16 move , u8 battlerAtk , u8 battlerDef , struct Pokemon * mon )
{
s32 dmg ;
u32 i ;
2022-01-13 17:28:27 +01:00
u8 effectiveness ;
2020-12-16 05:57:33 +01:00
struct BattlePokemon * battleMons = Alloc ( sizeof ( struct BattlePokemon ) * MAX_BATTLERS_COUNT ) ;
for ( i = 0 ; i < MAX_BATTLERS_COUNT ; i + + )
battleMons [ i ] = gBattleMons [ i ] ;
PokemonToBattleMon ( mon , & gBattleMons [ battlerAtk ] ) ;
2022-07-17 18:40:43 +02:00
dmg = AI_CalcDamage ( move , battlerAtk , battlerDef , & effectiveness , FALSE ) ;
2020-12-16 05:57:33 +01:00
for ( i = 0 ; i < MAX_BATTLERS_COUNT ; i + + )
gBattleMons [ i ] = battleMons [ i ] ;
Free ( battleMons ) ;
return dmg ;
}
s32 CountUsablePartyMons ( u8 battlerId )
{
s32 battlerOnField1 , battlerOnField2 , i , ret ;
struct Pokemon * party ;
if ( GetBattlerSide ( battlerId ) = = B_SIDE_PLAYER )
party = gPlayerParty ;
else
party = gEnemyParty ;
if ( gBattleTypeFlags & BATTLE_TYPE_DOUBLE )
{
battlerOnField1 = gBattlerPartyIndexes [ battlerId ] ;
2022-09-03 05:57:43 +02:00
battlerOnField2 = gBattlerPartyIndexes [ GetBattlerAtPosition ( BATTLE_PARTNER ( GetBattlerPosition ( battlerId ) ) ) ] ;
2020-12-16 05:57:33 +01:00
}
else // In singles there's only one battlerId by side.
{
battlerOnField1 = gBattlerPartyIndexes [ battlerId ] ;
battlerOnField2 = gBattlerPartyIndexes [ battlerId ] ;
}
ret = 0 ;
for ( i = 0 ; i < PARTY_SIZE ; i + + )
{
if ( i ! = battlerOnField1 & & i ! = battlerOnField2
& & GetMonData ( & party [ i ] , MON_DATA_HP ) ! = 0
& & GetMonData ( & party [ i ] , MON_DATA_SPECIES2 ) ! = SPECIES_NONE
& & GetMonData ( & party [ i ] , MON_DATA_SPECIES2 ) ! = SPECIES_EGG )
{
ret + + ;
}
}
return ret ;
}
bool32 IsPartyFullyHealedExceptBattler ( u8 battlerId )
{
struct Pokemon * party ;
u32 i ;
2021-06-10 12:55:05 +02:00
2020-12-16 05:57:33 +01:00
if ( GetBattlerSide ( battlerId ) = = B_SIDE_PLAYER )
party = gPlayerParty ;
else
party = gEnemyParty ;
2021-06-10 12:55:05 +02:00
2020-12-16 05:57:33 +01:00
for ( i = 0 ; i < PARTY_SIZE ; i + + )
{
if ( i ! = gBattlerPartyIndexes [ battlerId ]
& & GetMonData ( & party [ i ] , MON_DATA_HP ) ! = 0
& & GetMonData ( & party [ i ] , MON_DATA_SPECIES2 ) ! = SPECIES_NONE
& & GetMonData ( & party [ i ] , MON_DATA_SPECIES2 ) ! = SPECIES_EGG
& & GetMonData ( & party [ i ] , MON_DATA_HP ) < GetMonData ( & party [ i ] , MON_DATA_MAX_HP ) )
return FALSE ;
}
return TRUE ;
}
2020-12-20 05:58:23 +01:00
bool32 PartyHasMoveSplit ( u8 battlerId , u8 split )
{
u8 firstId , lastId ;
struct Pokemon * party = GetBattlerPartyData ( battlerId ) ;
u32 i , j ;
for ( i = 0 ; i < PARTY_SIZE ; i + + )
{
if ( GetMonData ( & party [ i ] , MON_DATA_HP , NULL ) = = 0 )
continue ;
2021-06-10 12:55:05 +02:00
2020-12-20 05:58:23 +01:00
for ( j = 0 ; j < MAX_MON_MOVES ; j + + )
{
u16 move = GetMonData ( & party [ i ] , MON_DATA_MOVE1 + j , NULL ) ;
u16 pp = GetMonData ( & party [ i ] , MON_DATA_PP1 + j , NULL ) ;
if ( pp > 0 & & move ! = MOVE_NONE )
{
//TODO - handle photon geyser, light that burns the sky
if ( gBattleMoves [ move ] . split = = split )
return TRUE ;
}
}
}
return FALSE ;
}
bool32 SideHasMoveSplit ( u8 battlerId , u8 split )
{
if ( IsDoubleBattle ( ) )
{
if ( HasMoveWithSplit ( battlerId , split ) | | HasMoveWithSplit ( BATTLE_PARTNER ( battlerId ) , split ) )
return TRUE ;
}
else
{
if ( HasMoveWithSplit ( battlerId , split ) )
return TRUE ;
}
return FALSE ;
}
bool32 IsAbilityOfRating ( u16 ability , s8 rating )
{
if ( sAiAbilityRatings [ ability ] > = rating )
return TRUE ;
2021-01-13 19:08:43 +01:00
return FALSE ;
2020-12-20 05:58:23 +01:00
}
s8 GetAbilityRating ( u16 ability )
{
return sAiAbilityRatings [ ability ] ;
}
2021-06-10 12:55:05 +02:00
static const u16 sRecycleEncouragedItems [ ] =
2020-12-20 05:58:23 +01:00
{
ITEM_CHESTO_BERRY ,
ITEM_LUM_BERRY ,
ITEM_STARF_BERRY ,
ITEM_SITRUS_BERRY ,
ITEM_MICLE_BERRY ,
ITEM_CUSTAP_BERRY ,
ITEM_MENTAL_HERB ,
ITEM_FOCUS_SASH ,
// TODO expand this
} ;
2021-11-08 17:37:41 +01:00
// Its assumed that the berry is strategically given, so no need to check benefits of the berry
bool32 IsStatBoostingBerry ( u16 item )
{
switch ( item )
{
case ITEM_LIECHI_BERRY :
case ITEM_GANLON_BERRY :
case ITEM_SALAC_BERRY :
case ITEM_PETAYA_BERRY :
case ITEM_APICOT_BERRY :
//case ITEM_LANSAT_BERRY:
case ITEM_STARF_BERRY :
case ITEM_MICLE_BERRY :
return TRUE ;
default :
return FALSE ;
}
}
2021-11-09 03:33:00 +01:00
bool32 ShouldRestoreHpBerry ( u8 battlerAtk , u16 item )
2021-11-08 17:37:41 +01:00
{
switch ( item )
{
case ITEM_ORAN_BERRY :
2021-11-09 03:33:00 +01:00
if ( gBattleMons [ battlerAtk ] . maxHP < = 50 )
2021-11-09 03:27:40 +01:00
return TRUE ; // Only worth it in the early game
return FALSE ;
2021-11-08 17:37:41 +01:00
case ITEM_SITRUS_BERRY :
case ITEM_FIGY_BERRY :
case ITEM_WIKI_BERRY :
case ITEM_MAGO_BERRY :
case ITEM_AGUAV_BERRY :
case ITEM_IAPAPA_BERRY :
return TRUE ;
default :
return FALSE ;
}
}
2020-12-20 05:58:23 +01:00
bool32 IsRecycleEncouragedItem ( u16 item )
{
u32 i ;
for ( i = 0 ; i < ARRAY_COUNT ( sRecycleEncouragedItems ) ; i + + )
{
if ( item = = sRecycleEncouragedItems [ i ] )
return TRUE ;
}
return FALSE ;
}
// score increases
# define STAT_UP_2_STAGE 8
2021-06-10 12:55:05 +02:00
# define STAT_UP_STAGE 10
2020-12-20 05:58:23 +01:00
void IncreaseStatUpScore ( u8 battlerAtk , u8 battlerDef , u8 statId , s16 * score )
{
2022-01-13 17:28:27 +01:00
if ( AI_DATA - > abilities [ battlerAtk ] = = ABILITY_CONTRARY )
2020-12-20 05:58:23 +01:00
return ;
2021-06-10 12:55:05 +02:00
2022-01-13 17:28:27 +01:00
if ( AI_DATA - > hpPercents [ battlerAtk ] < 80 & & AI_RandLessThan ( 128 ) )
2021-01-05 03:39:59 +01:00
return ;
2022-08-23 01:07:25 +02:00
2021-11-08 17:42:32 +01:00
if ( ( AI_THINKING_STRUCT - > aiFlags & AI_FLAG_TRY_TO_FAINT ) & & CanAIFaintTarget ( battlerAtk , battlerDef , 0 ) )
2021-11-08 17:37:41 +01:00
return ; // Damaging moves would get a score boost from AI_TryToFaint or PreferStrongestMove so we don't consider them here
2021-06-10 12:55:05 +02:00
2020-12-20 05:58:23 +01:00
switch ( statId )
{
case STAT_ATK :
2022-01-13 17:28:27 +01:00
if ( HasMoveWithSplit ( battlerAtk , SPLIT_PHYSICAL ) & & AI_DATA - > hpPercents [ battlerAtk ] > 40 )
2020-12-20 05:58:23 +01:00
{
if ( gBattleMons [ battlerAtk ] . statStages [ STAT_ATK ] < STAT_UP_2_STAGE )
* score + = 2 ;
else if ( gBattleMons [ battlerAtk ] . statStages [ STAT_ATK ] < STAT_UP_STAGE )
* ( score ) + + ;
}
if ( HasMoveEffect ( battlerAtk , EFFECT_FOUL_PLAY ) )
* ( score ) + + ;
break ;
case STAT_DEF :
if ( ( HasMoveWithSplit ( battlerDef , SPLIT_PHYSICAL ) | | IS_MOVE_PHYSICAL ( gLastMoves [ battlerDef ] ) )
2022-01-13 17:28:27 +01:00
& & AI_DATA - > hpPercents [ battlerAtk ] > 70 )
2020-12-20 05:58:23 +01:00
{
if ( gBattleMons [ battlerAtk ] . statStages [ STAT_DEF ] < STAT_UP_2_STAGE )
* score + = 2 ; // seems better to raise def at higher HP
else if ( gBattleMons [ battlerAtk ] . statStages [ STAT_DEF ] < STAT_UP_STAGE )
* ( score ) + + ;
}
break ;
case STAT_SPEED :
2021-09-28 03:03:27 +02:00
if ( ! WillAIStrikeFirst ( ) )
2020-12-20 23:15:33 +01:00
{
if ( gBattleMons [ battlerAtk ] . statStages [ STAT_SPEED ] < STAT_UP_2_STAGE )
* score + = 2 ;
else if ( gBattleMons [ battlerAtk ] . statStages [ STAT_SPEED ] < STAT_UP_STAGE )
* ( score ) + + ;
}
2020-12-20 05:58:23 +01:00
break ;
case STAT_SPATK :
2022-01-13 17:28:27 +01:00
if ( HasMoveWithSplit ( battlerAtk , SPLIT_SPECIAL ) & & AI_DATA - > hpPercents [ battlerAtk ] > 40 )
2020-12-20 05:58:23 +01:00
{
if ( gBattleMons [ battlerAtk ] . statStages [ STAT_SPATK ] < STAT_UP_2_STAGE )
* score + = 2 ;
else if ( gBattleMons [ battlerAtk ] . statStages [ STAT_SPATK ] < STAT_UP_STAGE )
* ( score ) + + ;
}
break ;
case STAT_SPDEF :
if ( ( HasMoveWithSplit ( battlerDef , SPLIT_SPECIAL ) | | IS_MOVE_SPECIAL ( gLastMoves [ battlerDef ] ) )
2022-01-13 17:28:27 +01:00
& & AI_DATA - > hpPercents [ battlerAtk ] > 70 )
2020-12-20 05:58:23 +01:00
{
if ( gBattleMons [ battlerAtk ] . statStages [ STAT_SPDEF ] < STAT_UP_2_STAGE )
* score + = 2 ; // seems better to raise spdef at higher HP
else if ( gBattleMons [ battlerAtk ] . statStages [ STAT_SPDEF ] < STAT_UP_STAGE )
* ( score ) + + ;
}
break ;
case STAT_ACC :
2022-01-13 17:28:27 +01:00
if ( HasMoveWithLowAccuracy ( battlerAtk , battlerDef , 80 , TRUE , AI_DATA - > abilities [ battlerAtk ] , AI_DATA - > abilities [ battlerDef ] , AI_DATA - > holdEffects [ battlerAtk ] , AI_DATA - > holdEffects [ battlerDef ] ) )
2021-01-05 04:23:09 +01:00
* score + = 2 ; // has moves with less than 80% accuracy
2022-01-13 17:28:27 +01:00
else if ( HasMoveWithLowAccuracy ( battlerAtk , battlerDef , 90 , TRUE , AI_DATA - > abilities [ battlerAtk ] , AI_DATA - > abilities [ battlerDef ] , AI_DATA - > holdEffects [ battlerAtk ] , AI_DATA - > holdEffects [ battlerDef ] ) )
2021-01-05 04:23:09 +01:00
* ( score ) + + ;
2020-12-20 05:58:23 +01:00
break ;
case STAT_EVASION :
2022-01-13 17:28:27 +01:00
if ( ! BattlerWillFaintFromWeather ( battlerAtk , AI_DATA - > abilities [ battlerAtk ] ) )
2020-12-20 05:58:23 +01:00
{
if ( ! GetBattlerSecondaryDamage ( battlerAtk ) & & ! ( gStatuses3 [ battlerAtk ] & STATUS3_ROOTED ) )
* score + = 2 ;
2021-01-05 04:23:09 +01:00
else
* ( score ) + + ;
2020-12-20 05:58:23 +01:00
}
break ;
}
}
void IncreasePoisonScore ( u8 battlerAtk , u8 battlerDef , u16 move , s16 * score )
{
2021-11-08 17:42:32 +01:00
if ( ( AI_THINKING_STRUCT - > aiFlags & AI_FLAG_TRY_TO_FAINT ) & & CanAIFaintTarget ( battlerAtk , battlerDef , 0 ) )
2021-11-08 17:37:41 +01:00
return ;
2022-01-13 17:28:27 +01:00
if ( AI_CanPoison ( battlerAtk , battlerDef , AI_DATA - > abilities [ battlerDef ] , move , AI_DATA - > partnerMove ) & & AI_DATA - > hpPercents [ battlerDef ] > 20 )
2020-12-20 05:58:23 +01:00
{
if ( ! HasDamagingMove ( battlerDef ) )
* score + = 2 ;
2021-06-10 12:55:05 +02:00
2021-01-05 03:39:59 +01:00
if ( AI_THINKING_STRUCT - > aiFlags & AI_FLAG_STALL & & HasMoveEffect ( battlerAtk , EFFECT_PROTECT ) )
2020-12-20 05:58:23 +01:00
( * score ) + + ; // stall tactic
2021-06-10 12:55:05 +02:00
2020-12-20 05:58:23 +01:00
if ( HasMoveEffect ( battlerAtk , EFFECT_VENOSHOCK )
| | HasMoveEffect ( battlerAtk , EFFECT_HEX )
| | HasMoveEffect ( battlerAtk , EFFECT_VENOM_DRENCH )
2022-01-13 17:28:27 +01:00
| | AI_DATA - > abilities [ battlerAtk ] = = ABILITY_MERCILESS )
2021-01-05 03:39:59 +01:00
* ( score ) + = 2 ;
2020-12-20 05:58:23 +01:00
else
2021-01-05 03:39:59 +01:00
* ( score ) + + ;
2020-12-20 05:58:23 +01:00
}
}
void IncreaseBurnScore ( u8 battlerAtk , u8 battlerDef , u16 move , s16 * score )
{
2021-11-08 17:42:32 +01:00
if ( ( AI_THINKING_STRUCT - > aiFlags & AI_FLAG_TRY_TO_FAINT ) & & CanAIFaintTarget ( battlerAtk , battlerDef , 0 ) )
2021-11-08 17:37:41 +01:00
return ;
2022-01-13 17:28:27 +01:00
if ( AI_CanBurn ( battlerAtk , battlerDef , AI_DATA - > abilities [ battlerDef ] , BATTLE_PARTNER ( battlerAtk ) , move , AI_DATA - > partnerMove ) )
2020-12-20 05:58:23 +01:00
{
( * score ) + + ; // burning is good
if ( HasMoveWithSplit ( battlerDef , SPLIT_PHYSICAL ) )
{
if ( CanTargetFaintAi ( battlerDef , battlerAtk ) )
* score + = 2 ; // burning the target to stay alive is cool
}
2021-06-10 12:55:05 +02:00
2022-01-13 17:28:27 +01:00
if ( HasMoveEffect ( battlerAtk , EFFECT_HEX ) | | HasMoveEffect ( BATTLE_PARTNER ( battlerAtk ) , EFFECT_HEX ) )
2020-12-20 05:58:23 +01:00
( * score ) + + ;
}
}
void IncreaseParalyzeScore ( u8 battlerAtk , u8 battlerDef , u16 move , s16 * score )
{
2021-11-08 17:42:32 +01:00
if ( ( AI_THINKING_STRUCT - > aiFlags & AI_FLAG_TRY_TO_FAINT ) & & CanAIFaintTarget ( battlerAtk , battlerDef , 0 ) )
2021-11-08 17:37:41 +01:00
return ;
2022-08-23 01:07:25 +02:00
2022-01-13 17:28:27 +01:00
if ( AI_CanParalyze ( battlerAtk , battlerDef , AI_DATA - > abilities [ battlerDef ] , move , AI_DATA - > partnerMove ) )
2020-12-20 05:58:23 +01:00
{
u8 atkSpeed = GetBattlerTotalSpeedStat ( battlerAtk ) ;
u8 defSpeed = GetBattlerTotalSpeedStat ( battlerDef ) ;
2021-06-10 12:55:05 +02:00
2020-12-20 05:58:23 +01:00
if ( ( defSpeed > = atkSpeed & & defSpeed / 2 < atkSpeed ) // You'll go first after paralyzing foe
| | HasMoveEffect ( battlerAtk , EFFECT_HEX )
| | HasMoveEffect ( battlerAtk , EFFECT_FLINCH_HIT )
| | gBattleMons [ battlerDef ] . status2 & STATUS2_INFATUATION
| | gBattleMons [ battlerDef ] . status2 & STATUS2_CONFUSION )
* score + = 4 ;
else
* score + = 2 ;
}
}
void IncreaseSleepScore ( u8 battlerAtk , u8 battlerDef , u16 move , s16 * score )
{
2021-11-08 17:42:32 +01:00
if ( ( AI_THINKING_STRUCT - > aiFlags & AI_FLAG_TRY_TO_FAINT ) & & CanAIFaintTarget ( battlerAtk , battlerDef , 0 ) )
2021-11-08 17:37:41 +01:00
return ;
2022-08-23 01:07:25 +02:00
2022-01-13 17:28:27 +01:00
if ( AI_CanPutToSleep ( battlerAtk , battlerDef , AI_DATA - > abilities [ battlerDef ] , move , AI_DATA - > partnerMove ) )
2021-01-05 03:39:59 +01:00
* score + = 2 ;
else
return ;
2021-06-10 12:55:05 +02:00
2020-12-20 05:58:23 +01:00
if ( ( HasMoveEffect ( battlerAtk , EFFECT_DREAM_EATER ) | | HasMoveEffect ( battlerAtk , EFFECT_NIGHTMARE ) )
& & ! ( HasMoveEffect ( battlerDef , EFFECT_SNORE ) | | HasMoveEffect ( battlerDef , EFFECT_SLEEP_TALK ) ) )
( * score ) + + ;
2021-06-10 12:55:05 +02:00
2022-01-13 17:28:27 +01:00
if ( HasMoveEffect ( battlerAtk , EFFECT_HEX ) | | HasMoveEffect ( BATTLE_PARTNER ( battlerAtk ) , EFFECT_HEX ) )
2020-12-20 05:58:23 +01:00
( * score ) + + ;
}
void IncreaseConfusionScore ( u8 battlerAtk , u8 battlerDef , u16 move , s16 * score )
{
2021-11-08 17:42:32 +01:00
if ( ( AI_THINKING_STRUCT - > aiFlags & AI_FLAG_TRY_TO_FAINT ) & & CanAIFaintTarget ( battlerAtk , battlerDef , 0 ) )
2021-11-08 17:37:41 +01:00
return ;
2022-08-23 01:07:25 +02:00
2022-01-13 17:28:27 +01:00
if ( AI_CanConfuse ( battlerAtk , battlerDef , AI_DATA - > abilities [ battlerDef ] , BATTLE_PARTNER ( battlerAtk ) , move , AI_DATA - > partnerMove )
& & AI_DATA - > holdEffects [ battlerDef ] ! = HOLD_EFFECT_CURE_CONFUSION
& & AI_DATA - > holdEffects [ battlerDef ] ! = HOLD_EFFECT_CURE_STATUS )
2020-12-20 05:58:23 +01:00
{
if ( gBattleMons [ battlerDef ] . status1 & STATUS1_PARALYSIS
| | gBattleMons [ battlerDef ] . status2 & STATUS2_INFATUATION
2022-01-13 17:28:27 +01:00
| | ( AI_DATA - > abilities [ battlerAtk ] = = ABILITY_SERENE_GRACE & & HasMoveEffect ( battlerAtk , EFFECT_FLINCH_HIT ) ) )
2020-12-20 05:58:23 +01:00
* score + = 3 ;
else
* score + = 2 ;
}
}
2021-11-08 17:55:06 +01:00
bool32 AI_MoveMakesContact ( u32 ability , u32 holdEffect , u16 move )
{
if ( TestMoveFlags ( move , FLAG_MAKES_CONTACT )
& & ability ! = ABILITY_LONG_REACH
& & holdEffect ! = HOLD_EFFECT_PROTECTIVE_PADS )
return TRUE ;
return FALSE ;
}
2022-03-22 19:10:24 +01:00
//TODO - this could use some more sophisticated logic
bool32 ShouldUseZMove ( u8 battlerAtk , u8 battlerDef , u16 chosenMove )
{
// simple logic. just upgrades chosen move to z move if possible, unless regular move would kill opponent
if ( ( gBattleTypeFlags & BATTLE_TYPE_DOUBLE ) & & battlerDef = = BATTLE_PARTNER ( battlerAtk ) )
return FALSE ; //don't use z move on partner
if ( gBattleStruct - > zmove . used [ battlerAtk ] )
return FALSE ; //cant use z move twice
2022-08-23 01:07:25 +02:00
2022-03-22 19:10:24 +01:00
if ( IsViableZMove ( battlerAtk , chosenMove ) )
{
2022-07-17 18:40:43 +02:00
u8 effectiveness ;
2022-09-13 21:26:36 +02:00
2022-03-22 19:10:24 +01:00
if ( gBattleMons [ battlerDef ] . ability = = ABILITY_DISGUISE & & gBattleMons [ battlerDef ] . species = = SPECIES_MIMIKYU )
return FALSE ; // Don't waste a Z-Move busting disguise
if ( gBattleMons [ battlerDef ] . ability = = ABILITY_ICE_FACE & & gBattleMons [ battlerDef ] . species = = SPECIES_EISCUE & & IS_MOVE_PHYSICAL ( chosenMove ) )
return FALSE ; // Don't waste a Z-Move busting Ice Face
2022-09-13 21:26:36 +02:00
2022-03-22 19:10:24 +01:00
if ( IS_MOVE_STATUS ( chosenMove ) & & ! IS_MOVE_STATUS ( gBattleStruct - > zmove . chosenZMove ) )
return FALSE ;
else if ( ! IS_MOVE_STATUS ( chosenMove ) & & IS_MOVE_STATUS ( gBattleStruct - > zmove . chosenZMove ) )
return FALSE ;
2022-08-23 01:07:25 +02:00
2022-07-17 18:40:43 +02:00
if ( ! IS_MOVE_STATUS ( chosenMove ) & & AI_CalcDamage ( chosenMove , battlerAtk , battlerDef , & effectiveness , FALSE ) > = gBattleMons [ battlerDef ] . hp )
2022-03-22 19:10:24 +01:00
return FALSE ; // don't waste damaging z move if can otherwise faint target
2022-08-23 01:07:25 +02:00
2022-03-22 19:10:24 +01:00
return TRUE ;
}
2022-08-23 01:07:25 +02:00
2022-03-22 19:10:24 +01:00
return FALSE ;
}