pokeemerald/src/battle_ai_switch_items.c

949 lines
31 KiB
C
Raw Normal View History

2017-10-10 18:01:45 +02:00
#include "global.h"
#include "battle.h"
#include "battle_controllers.h"
2017-12-05 18:55:48 +01:00
#include "constants/abilities.h"
2017-12-11 19:27:51 +01:00
#include "constants/moves.h"
2017-10-10 18:01:45 +02:00
#include "pokemon.h"
2017-12-11 19:27:51 +01:00
#include "constants/species.h"
#include "random.h"
2017-10-10 18:01:45 +02:00
#include "util.h"
2017-12-05 18:55:48 +01:00
#include "constants/items.h"
2017-10-11 12:49:42 +02:00
#include "pokemon_item_effects.h"
2017-10-10 18:01:45 +02:00
2018-02-06 02:46:59 +01:00
extern u8 gActiveBattler;
extern u8 gAbsentBattlerFlags;
2017-10-10 18:01:45 +02:00
extern u32 gBattleTypeFlags;
2018-02-06 02:46:59 +01:00
extern u32 gStatuses3[MAX_BATTLERS_COUNT];
extern struct BattlePokemon gBattleMons[MAX_BATTLERS_COUNT];
2018-02-06 20:48:02 +01:00
extern u16 gBattlerPartyIndexes[MAX_BATTLERS_COUNT];
2018-02-06 02:46:59 +01:00
extern u16 gLastLandedMoves[MAX_BATTLERS_COUNT];
extern u8 gLastHitBy[MAX_BATTLERS_COUNT];
2017-10-10 21:45:07 +02:00
extern u16 gDynamicBasePower;
2018-01-16 22:12:38 +01:00
extern u8 gMoveResultFlags;
2017-10-10 21:45:07 +02:00
extern u8 gCritMultiplier;
extern s32 gBattleMoveDamage;
2017-10-10 18:01:45 +02:00
extern const struct BattleMove gBattleMoves[];
extern const struct BaseStats gBaseStats[];
2017-10-10 21:45:07 +02:00
extern const u8 gTypeEffectiveness[];
2017-10-11 12:49:42 +02:00
extern const u8 * const gItemEffectTable[]; // todo: fix once struct is declared
2017-10-10 18:01:45 +02:00
// this file's functions
2017-10-11 12:49:42 +02:00
static bool8 HasSuperEffectiveMoveAgainstOpponents(bool8 noRng);
static bool8 FindMonWithFlagsAndSuperEffective(u8 flags, u8 moduloPercent);
static bool8 ShouldUseItem(void);
2017-10-10 18:01:45 +02:00
2017-10-11 12:49:42 +02:00
static bool8 ShouldSwitchIfPerishSong(void)
2017-10-10 18:01:45 +02:00
{
2018-02-06 02:46:59 +01:00
if (gStatuses3[gActiveBattler] & STATUS3_PERISH_SONG
&& gDisableStructs[gActiveBattler].perishSongTimer1 == 0)
2017-10-10 18:01:45 +02:00
{
2018-02-06 02:46:59 +01:00
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = 6;
2018-02-06 20:48:02 +01:00
BtlController_EmitTwoReturnValues(1, ACTION_SWITCH, 0);
2017-10-10 18:01:45 +02:00
return TRUE;
}
return FALSE;
}
2017-10-11 12:49:42 +02:00
static bool8 ShouldSwitchIfWonderGuard(void)
2017-10-10 18:01:45 +02:00
{
2018-01-16 22:12:38 +01:00
u8 opposingPosition;
2017-10-10 18:01:45 +02:00
u8 opposingBank;
u8 moveFlags;
s32 i, j;
s32 firstId;
s32 lastId; // + 1
struct Pokemon *party = NULL;
u16 move;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
return FALSE;
2018-02-06 02:46:59 +01:00
opposingPosition = B_POSITION_OPPOSITE(GetBattlerPosition(gActiveBattler));
2017-10-10 18:01:45 +02:00
2018-02-06 02:46:59 +01:00
if (gBattleMons[GetBattlerAtPosition(opposingPosition)].ability != ABILITY_WONDER_GUARD)
2017-10-10 18:01:45 +02:00
return FALSE;
// check if pokemon has a super effective move
2018-02-06 02:46:59 +01:00
for (opposingBank = GetBattlerAtPosition(opposingPosition), i = 0; i < 4; i++)
2017-10-10 18:01:45 +02:00
{
2018-02-06 02:46:59 +01:00
move = gBattleMons[gActiveBattler].moves[i];
2017-10-10 18:01:45 +02:00
if (move == MOVE_NONE)
continue;
moveFlags = AI_TypeCalc(move, gBattleMons[opposingBank].species, gBattleMons[opposingBank].ability);
2018-01-16 22:12:38 +01:00
if (moveFlags & MOVE_RESULT_SUPER_EFFECTIVE)
2017-10-10 18:01:45 +02:00
return FALSE;
}
// get party information
if (gBattleTypeFlags & (BATTLE_TYPE_TWO_OPPONENTS | BATTLE_TYPE_x800000))
{
2018-02-06 02:46:59 +01:00
if ((gActiveBattler & BIT_FLANK) == 0)
2017-10-10 18:01:45 +02:00
firstId = 0, lastId = 3;
else
firstId = 3, lastId = 6;
}
else
{
firstId = 0, lastId = 6;
}
2018-02-06 02:46:59 +01:00
if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
2017-10-10 18:01:45 +02:00
party = gPlayerParty;
else
party = gEnemyParty;
// find a pokemon in the party that has a super effective move
for (i = firstId; i < lastId; i++)
{
if (GetMonData(&party[i], MON_DATA_HP) == 0)
continue;
if (GetMonData(&party[i], MON_DATA_SPECIES2) == SPECIES_NONE)
continue;
if (GetMonData(&party[i], MON_DATA_SPECIES2) == SPECIES_EGG)
continue;
2018-02-06 20:48:02 +01:00
if (i == gBattlerPartyIndexes[gActiveBattler])
2017-10-10 18:01:45 +02:00
continue;
GetMonData(&party[i], MON_DATA_SPECIES); // unused return value
GetMonData(&party[i], MON_DATA_ALT_ABILITY); // unused return value
2018-02-06 02:46:59 +01:00
for (opposingBank = GetBattlerAtPosition(opposingPosition), j = 0; j < 4; j++)
2017-10-10 18:01:45 +02:00
{
move = GetMonData(&party[i], MON_DATA_MOVE1 + j);
if (move == MOVE_NONE)
continue;
moveFlags = AI_TypeCalc(move, gBattleMons[opposingBank].species, gBattleMons[opposingBank].ability);
2018-01-16 22:12:38 +01:00
if (moveFlags & MOVE_RESULT_SUPER_EFFECTIVE && Random() % 3 < 2)
2017-10-10 18:01:45 +02:00
{
// we found a mon
2018-02-06 02:46:59 +01:00
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = i;
2018-02-06 20:48:02 +01:00
BtlController_EmitTwoReturnValues(1, ACTION_SWITCH, 0);
2017-10-10 18:01:45 +02:00
return TRUE;
}
}
}
return FALSE; // at this point there is not a single pokemon in the party that has a super effective move against a pokemon with wonder guard
}
2017-10-11 12:49:42 +02:00
static bool8 FindMonThatAbsorbsOpponentsMove(void)
2017-10-10 18:01:45 +02:00
{
u8 bankIn1, bankIn2;
u8 absorbingTypeAbility;
s32 firstId;
s32 lastId; // + 1
struct Pokemon *party;
s32 i;
if (HasSuperEffectiveMoveAgainstOpponents(TRUE) && Random() % 3 != 0)
return FALSE;
2018-02-06 02:46:59 +01:00
if (gLastLandedMoves[gActiveBattler] == 0)
2017-10-10 18:01:45 +02:00
return FALSE;
2018-02-06 02:46:59 +01:00
if (gLastLandedMoves[gActiveBattler] == 0xFFFF)
2017-10-10 18:01:45 +02:00
return FALSE;
2018-02-06 02:46:59 +01:00
if (gBattleMoves[gLastLandedMoves[gActiveBattler]].power == 0)
2017-10-10 18:01:45 +02:00
return FALSE;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
{
2018-02-06 02:46:59 +01:00
bankIn1 = gActiveBattler;
if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(B_POSITION_PARTNER(GetBattlerPosition(gActiveBattler)))])
bankIn2 = gActiveBattler;
2017-10-10 18:01:45 +02:00
else
2018-02-06 02:46:59 +01:00
bankIn2 = GetBattlerAtPosition(B_POSITION_PARTNER(GetBattlerPosition(gActiveBattler)));
2017-10-10 18:01:45 +02:00
}
else
{
2018-02-06 02:46:59 +01:00
bankIn1 = gActiveBattler;
bankIn2 = gActiveBattler;
2017-10-10 18:01:45 +02:00
}
2018-02-06 02:46:59 +01:00
if (gBattleMoves[gLastLandedMoves[gActiveBattler]].type == TYPE_FIRE)
2017-10-10 18:01:45 +02:00
absorbingTypeAbility = ABILITY_FLASH_FIRE;
2018-02-06 02:46:59 +01:00
else if (gBattleMoves[gLastLandedMoves[gActiveBattler]].type == TYPE_WATER)
2017-10-10 18:01:45 +02:00
absorbingTypeAbility = ABILITY_WATER_ABSORB;
2018-02-06 02:46:59 +01:00
else if (gBattleMoves[gLastLandedMoves[gActiveBattler]].type == TYPE_ELECTRIC)
2017-10-10 18:01:45 +02:00
absorbingTypeAbility = ABILITY_VOLT_ABSORB;
else
return FALSE;
2018-02-06 02:46:59 +01:00
if (gBattleMons[gActiveBattler].ability == absorbingTypeAbility)
2017-10-10 18:01:45 +02:00
return FALSE;
if (gBattleTypeFlags & (BATTLE_TYPE_TWO_OPPONENTS | BATTLE_TYPE_x800000))
{
2018-02-06 02:46:59 +01:00
if ((gActiveBattler & BIT_FLANK) == 0)
2017-10-10 18:01:45 +02:00
firstId = 0, lastId = 3;
else
firstId = 3, lastId = 6;
}
else
{
firstId = 0, lastId = 6;
}
2018-02-06 02:46:59 +01:00
if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
2017-10-10 18:01:45 +02:00
party = gPlayerParty;
else
party = gEnemyParty;
for (i = firstId; i < lastId; i++)
{
u16 species;
u8 monAbility;
if (GetMonData(&party[i], MON_DATA_HP) == 0)
continue;
if (GetMonData(&party[i], MON_DATA_SPECIES2) == SPECIES_NONE)
continue;
if (GetMonData(&party[i], MON_DATA_SPECIES2) == SPECIES_EGG)
continue;
2018-02-06 20:48:02 +01:00
if (i == gBattlerPartyIndexes[bankIn1])
2017-10-10 18:01:45 +02:00
continue;
2018-02-06 20:48:02 +01:00
if (i == gBattlerPartyIndexes[bankIn2])
2017-10-10 18:01:45 +02:00
continue;
2017-11-12 17:06:31 +01:00
if (i == *(gBattleStruct->monToSwitchIntoId + bankIn1))
2017-10-10 18:01:45 +02:00
continue;
2017-11-12 17:06:31 +01:00
if (i == *(gBattleStruct->monToSwitchIntoId + bankIn2))
2017-10-10 18:01:45 +02:00
continue;
species = GetMonData(&party[i], MON_DATA_SPECIES);
if (GetMonData(&party[i], MON_DATA_ALT_ABILITY) != 0)
monAbility = gBaseStats[species].ability2;
else
monAbility = gBaseStats[species].ability1;
if (absorbingTypeAbility == monAbility && Random() & 1)
{
// we found a mon
2018-02-06 02:46:59 +01:00
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = i;
2018-02-06 20:48:02 +01:00
BtlController_EmitTwoReturnValues(1, ACTION_SWITCH, 0);
2017-10-10 18:01:45 +02:00
return TRUE;
}
}
return FALSE;
}
2017-10-11 12:49:42 +02:00
static bool8 ShouldSwitchIfNaturalCure(void)
2017-10-10 18:01:45 +02:00
{
2018-02-06 02:46:59 +01:00
if (!(gBattleMons[gActiveBattler].status1 & STATUS1_SLEEP))
2017-10-10 18:01:45 +02:00
return FALSE;
2018-02-06 02:46:59 +01:00
if (gBattleMons[gActiveBattler].ability != ABILITY_NATURAL_CURE)
2017-10-10 18:01:45 +02:00
return FALSE;
2018-02-06 02:46:59 +01:00
if (gBattleMons[gActiveBattler].hp < gBattleMons[gActiveBattler].maxHP / 2)
2017-10-10 18:01:45 +02:00
return FALSE;
2018-02-06 02:46:59 +01:00
if ((gLastLandedMoves[gActiveBattler] == 0 || gLastLandedMoves[gActiveBattler] == 0xFFFF) && Random() & 1)
2017-10-10 18:01:45 +02:00
{
2018-02-06 02:46:59 +01:00
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = 6;
2018-02-06 20:48:02 +01:00
BtlController_EmitTwoReturnValues(1, ACTION_SWITCH, 0);
2017-10-10 18:01:45 +02:00
return TRUE;
}
2018-02-06 02:46:59 +01:00
else if (gBattleMoves[gLastLandedMoves[gActiveBattler]].power == 0 && Random() & 1)
2017-10-10 18:01:45 +02:00
{
2018-02-06 02:46:59 +01:00
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = 6;
2018-02-06 20:48:02 +01:00
BtlController_EmitTwoReturnValues(1, ACTION_SWITCH, 0);
2017-10-10 18:01:45 +02:00
return TRUE;
}
2018-01-16 22:12:38 +01:00
if (FindMonWithFlagsAndSuperEffective(MOVE_RESULT_DOESNT_AFFECT_FOE, 1))
2017-10-10 18:01:45 +02:00
return TRUE;
2018-01-16 22:12:38 +01:00
if (FindMonWithFlagsAndSuperEffective(MOVE_RESULT_NOT_VERY_EFFECTIVE, 1))
2017-10-10 18:01:45 +02:00
return TRUE;
if (Random() & 1)
{
2018-02-06 02:46:59 +01:00
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = 6;
2018-02-06 20:48:02 +01:00
BtlController_EmitTwoReturnValues(1, ACTION_SWITCH, 0);
2017-10-10 18:01:45 +02:00
return TRUE;
}
return FALSE;
}
2017-10-11 12:49:42 +02:00
static bool8 HasSuperEffectiveMoveAgainstOpponents(bool8 noRng)
2017-10-10 18:01:45 +02:00
{
2018-01-16 22:12:38 +01:00
u8 opposingPosition;
2017-10-10 18:01:45 +02:00
u8 opposingBank;
s32 i;
u8 moveFlags;
u16 move;
2018-02-06 02:46:59 +01:00
opposingPosition = B_POSITION_OPPOSITE(GetBattlerPosition(gActiveBattler));
opposingBank = GetBattlerAtPosition(opposingPosition);
2017-10-10 18:01:45 +02:00
2018-02-06 02:46:59 +01:00
if (!(gAbsentBattlerFlags & gBitTable[opposingBank]))
2017-10-10 18:01:45 +02:00
{
for (i = 0; i < 4; i++)
{
2018-02-06 02:46:59 +01:00
move = gBattleMons[gActiveBattler].moves[i];
2017-10-10 18:01:45 +02:00
if (move == MOVE_NONE)
continue;
moveFlags = AI_TypeCalc(move, gBattleMons[opposingBank].species, gBattleMons[opposingBank].ability);
2018-01-16 22:12:38 +01:00
if (moveFlags & MOVE_RESULT_SUPER_EFFECTIVE)
2017-10-10 18:01:45 +02:00
{
if (noRng)
return TRUE;
if (Random() % 10 != 0)
return TRUE;
}
}
}
if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
return FALSE;
2018-02-06 02:46:59 +01:00
opposingBank = GetBattlerAtPosition(B_POSITION_PARTNER(opposingPosition));
2017-10-10 18:01:45 +02:00
2018-02-06 02:46:59 +01:00
if (!(gAbsentBattlerFlags & gBitTable[opposingBank]))
2017-10-10 18:01:45 +02:00
{
for (i = 0; i < 4; i++)
{
2018-02-06 02:46:59 +01:00
move = gBattleMons[gActiveBattler].moves[i];
2017-10-10 18:01:45 +02:00
if (move == MOVE_NONE)
continue;
moveFlags = AI_TypeCalc(move, gBattleMons[opposingBank].species, gBattleMons[opposingBank].ability);
2018-01-16 22:12:38 +01:00
if (moveFlags & MOVE_RESULT_SUPER_EFFECTIVE)
2017-10-10 18:01:45 +02:00
{
if (noRng)
return TRUE;
if (Random() % 10 != 0)
return TRUE;
}
}
}
return FALSE;
}
2017-10-11 12:49:42 +02:00
static bool8 AreStatsRaised(void)
2017-10-10 18:01:45 +02:00
{
u8 buffedStatsValue = 0;
s32 i;
for (i = 0; i < BATTLE_STATS_NO; i++)
{
2018-02-06 02:46:59 +01:00
if (gBattleMons[gActiveBattler].statStages[i] > 6)
buffedStatsValue += gBattleMons[gActiveBattler].statStages[i] - 6;
2017-10-10 18:01:45 +02:00
}
return (buffedStatsValue > 3);
}
2017-10-11 12:49:42 +02:00
static bool8 FindMonWithFlagsAndSuperEffective(u8 flags, u8 moduloPercent)
2017-10-10 18:01:45 +02:00
{
u8 bankIn1, bankIn2;
s32 firstId;
s32 lastId; // + 1
struct Pokemon *party;
s32 i, j;
u16 move;
u8 moveFlags;
2018-02-06 02:46:59 +01:00
if (gLastLandedMoves[gActiveBattler] == 0)
2017-10-10 18:01:45 +02:00
return FALSE;
2018-02-06 02:46:59 +01:00
if (gLastLandedMoves[gActiveBattler] == 0xFFFF)
2017-10-10 18:01:45 +02:00
return FALSE;
2018-02-06 02:46:59 +01:00
if (gLastHitBy[gActiveBattler] == 0xFF)
2017-10-10 18:01:45 +02:00
return FALSE;
2018-02-06 02:46:59 +01:00
if (gBattleMoves[gLastLandedMoves[gActiveBattler]].power == 0)
2017-10-10 18:01:45 +02:00
return FALSE;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
{
2018-02-06 02:46:59 +01:00
bankIn1 = gActiveBattler;
if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(B_POSITION_PARTNER(GetBattlerPosition(gActiveBattler)))])
bankIn2 = gActiveBattler;
2017-10-10 18:01:45 +02:00
else
2018-02-06 02:46:59 +01:00
bankIn2 = GetBattlerAtPosition(B_POSITION_PARTNER(GetBattlerPosition(gActiveBattler)));
2017-10-10 18:01:45 +02:00
}
else
{
2018-02-06 02:46:59 +01:00
bankIn1 = gActiveBattler;
bankIn2 = gActiveBattler;
2017-10-10 18:01:45 +02:00
}
if (gBattleTypeFlags & (BATTLE_TYPE_TWO_OPPONENTS | BATTLE_TYPE_x800000))
{
2018-02-06 02:46:59 +01:00
if ((gActiveBattler & BIT_FLANK) == 0)
2017-10-10 18:01:45 +02:00
firstId = 0, lastId = 3;
else
firstId = 3, lastId = 6;
}
else
{
firstId = 0, lastId = 6;
}
2018-02-06 02:46:59 +01:00
if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
2017-10-10 18:01:45 +02:00
party = gPlayerParty;
else
party = gEnemyParty;
for (i = firstId; i < lastId; i++)
{
u16 species;
u8 monAbility;
if (GetMonData(&party[i], MON_DATA_HP) == 0)
continue;
if (GetMonData(&party[i], MON_DATA_SPECIES2) == SPECIES_NONE)
continue;
if (GetMonData(&party[i], MON_DATA_SPECIES2) == SPECIES_EGG)
continue;
2018-02-06 20:48:02 +01:00
if (i == gBattlerPartyIndexes[bankIn1])
2017-10-10 18:01:45 +02:00
continue;
2018-02-06 20:48:02 +01:00
if (i == gBattlerPartyIndexes[bankIn2])
2017-10-10 18:01:45 +02:00
continue;
2017-11-12 17:06:31 +01:00
if (i == *(gBattleStruct->monToSwitchIntoId + bankIn1))
2017-10-10 18:01:45 +02:00
continue;
2017-11-12 17:06:31 +01:00
if (i == *(gBattleStruct->monToSwitchIntoId + bankIn2))
2017-10-10 18:01:45 +02:00
continue;
species = GetMonData(&party[i], MON_DATA_SPECIES);
if (GetMonData(&party[i], MON_DATA_ALT_ABILITY) != 0)
monAbility = gBaseStats[species].ability2;
else
monAbility = gBaseStats[species].ability1;
2018-02-06 02:46:59 +01:00
moveFlags = AI_TypeCalc(gLastLandedMoves[gActiveBattler], species, monAbility);
2017-10-10 18:01:45 +02:00
if (moveFlags & flags)
{
2018-02-06 02:46:59 +01:00
bankIn1 = gLastHitBy[gActiveBattler];
2017-10-10 18:01:45 +02:00
for (j = 0; j < 4; j++)
{
move = GetMonData(&party[i], MON_DATA_MOVE1 + j);
if (move == 0)
continue;
moveFlags = AI_TypeCalc(move, gBattleMons[bankIn1].species, gBattleMons[bankIn1].ability);
2018-01-16 22:12:38 +01:00
if (moveFlags & MOVE_RESULT_SUPER_EFFECTIVE && Random() % moduloPercent == 0)
2017-10-10 18:01:45 +02:00
{
2018-02-06 02:46:59 +01:00
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = i;
2018-02-06 20:48:02 +01:00
BtlController_EmitTwoReturnValues(1, ACTION_SWITCH, 0);
2017-10-10 18:01:45 +02:00
return TRUE;
}
}
}
}
return FALSE;
}
2017-10-11 12:49:42 +02:00
static bool8 ShouldSwitch(void)
2017-10-10 18:01:45 +02:00
{
u8 bankIn1, bankIn2;
2017-10-10 21:45:07 +02:00
u8 *activeBankPtr; // needed to match
2017-10-10 18:01:45 +02:00
s32 firstId;
s32 lastId; // + 1
struct Pokemon *party;
s32 i;
s32 availableToSwitch;
2018-02-06 02:46:59 +01:00
if (gBattleMons[*(activeBankPtr = &gActiveBattler)].status2 & (STATUS2_WRAPPED | STATUS2_ESCAPE_PREVENTION))
2017-10-10 18:01:45 +02:00
return FALSE;
2018-02-06 02:46:59 +01:00
if (gStatuses3[gActiveBattler] & STATUS3_ROOTED)
2017-10-10 18:01:45 +02:00
return FALSE;
2018-02-06 02:46:59 +01:00
if (AbilityBattleEffects(ABILITYEFFECT_CHECK_OTHER_SIDE, gActiveBattler, ABILITY_SHADOW_TAG, 0, 0))
2017-10-10 18:01:45 +02:00
return FALSE;
2018-02-06 02:46:59 +01:00
if (AbilityBattleEffects(ABILITYEFFECT_CHECK_OTHER_SIDE, gActiveBattler, ABILITY_ARENA_TRAP, 0, 0))
2017-10-10 18:01:45 +02:00
return FALSE; // misses the flying or levitate check
2017-10-10 21:45:07 +02:00
if (AbilityBattleEffects(ABILITYEFFECT_FIELD_SPORT, 0, ABILITY_MAGNET_PULL, 0, 0))
{
2018-02-06 02:46:59 +01:00
if (gBattleMons[gActiveBattler].type1 == TYPE_STEEL)
2017-10-10 21:45:07 +02:00
return FALSE;
2018-02-06 02:46:59 +01:00
if (gBattleMons[gActiveBattler].type2 == TYPE_STEEL)
2017-10-10 21:45:07 +02:00
return FALSE;
}
2017-10-10 18:01:45 +02:00
if (gBattleTypeFlags & BATTLE_TYPE_ARENA)
return FALSE;
availableToSwitch = 0;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
{
2017-10-10 21:45:07 +02:00
bankIn1 = *activeBankPtr;
2018-02-06 02:46:59 +01:00
if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(GetBattlerPosition(*activeBankPtr) ^ BIT_FLANK)])
2017-10-10 21:45:07 +02:00
bankIn2 = *activeBankPtr;
2017-10-10 18:01:45 +02:00
else
2018-02-06 02:46:59 +01:00
bankIn2 = GetBattlerAtPosition(GetBattlerPosition(*activeBankPtr) ^ BIT_FLANK);
2017-10-10 18:01:45 +02:00
}
else
{
2017-10-10 21:45:07 +02:00
bankIn1 = *activeBankPtr;
bankIn2 = *activeBankPtr;
2017-10-10 18:01:45 +02:00
}
if (gBattleTypeFlags & (BATTLE_TYPE_TWO_OPPONENTS | BATTLE_TYPE_x800000))
{
2018-02-06 02:46:59 +01:00
if ((gActiveBattler & BIT_FLANK) == 0)
2017-10-10 18:01:45 +02:00
firstId = 0, lastId = 3;
else
firstId = 3, lastId = 6;
}
else
{
firstId = 0, lastId = 6;
}
2018-02-06 02:46:59 +01:00
if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
2017-10-10 18:01:45 +02:00
party = gPlayerParty;
else
party = gEnemyParty;
for (i = firstId; i < lastId; i++)
{
if (GetMonData(&party[i], MON_DATA_HP) == 0)
continue;
if (GetMonData(&party[i], MON_DATA_SPECIES2) == SPECIES_NONE)
continue;
if (GetMonData(&party[i], MON_DATA_SPECIES2) == SPECIES_EGG)
continue;
2018-02-06 20:48:02 +01:00
if (i == gBattlerPartyIndexes[bankIn1])
2017-10-10 18:01:45 +02:00
continue;
2018-02-06 20:48:02 +01:00
if (i == gBattlerPartyIndexes[bankIn2])
2017-10-10 18:01:45 +02:00
continue;
2017-11-12 17:06:31 +01:00
if (i == *(gBattleStruct->monToSwitchIntoId + bankIn1))
2017-10-10 18:01:45 +02:00
continue;
2017-11-12 17:06:31 +01:00
if (i == *(gBattleStruct->monToSwitchIntoId + bankIn2))
2017-10-10 18:01:45 +02:00
continue;
availableToSwitch++;
}
if (availableToSwitch == 0)
return FALSE;
if (ShouldSwitchIfPerishSong())
return TRUE;
if (ShouldSwitchIfWonderGuard())
return TRUE;
2017-10-10 21:45:07 +02:00
if (FindMonThatAbsorbsOpponentsMove())
2017-10-10 18:01:45 +02:00
return TRUE;
if (ShouldSwitchIfNaturalCure())
return TRUE;
if (HasSuperEffectiveMoveAgainstOpponents(FALSE))
return FALSE;
if (AreStatsRaised())
return FALSE;
2018-01-16 22:12:38 +01:00
if (FindMonWithFlagsAndSuperEffective(MOVE_RESULT_DOESNT_AFFECT_FOE, 2)
|| FindMonWithFlagsAndSuperEffective(MOVE_RESULT_NOT_VERY_EFFECTIVE, 3))
2017-10-10 18:01:45 +02:00
return TRUE;
return FALSE;
}
2017-10-10 21:45:07 +02:00
void AI_TrySwitchOrUseItem(void)
{
struct Pokemon *party;
u8 bankIn1, bankIn2;
s32 firstId;
s32 lastId; // + 1
2018-02-06 02:46:59 +01:00
u8 bankIdentity = GetBattlerPosition(gActiveBattler);
2017-10-10 21:45:07 +02:00
2018-02-06 02:46:59 +01:00
if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
2017-10-10 21:45:07 +02:00
party = gPlayerParty;
else
party = gEnemyParty;
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER)
{
if (ShouldSwitch())
{
2018-02-06 02:46:59 +01:00
if (*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) == 6)
2017-10-10 21:45:07 +02:00
{
s32 monToSwitchId = GetMostSuitableMonToSwitchInto();
if (monToSwitchId == 6)
{
if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
{
2018-02-06 02:46:59 +01:00
bankIn1 = GetBattlerAtPosition(bankIdentity);
2017-10-10 21:45:07 +02:00
bankIn2 = bankIn1;
}
else
{
2018-02-06 02:46:59 +01:00
bankIn1 = GetBattlerAtPosition(bankIdentity);
bankIn2 = GetBattlerAtPosition(bankIdentity ^ BIT_FLANK);
2017-10-10 21:45:07 +02:00
}
if (gBattleTypeFlags & (BATTLE_TYPE_TWO_OPPONENTS | BATTLE_TYPE_x800000))
{
2018-02-06 02:46:59 +01:00
if ((gActiveBattler & BIT_FLANK) == 0)
2017-10-10 21:45:07 +02:00
firstId = 0, lastId = 3;
else
firstId = 3, lastId = 6;
}
else
{
firstId = 0, lastId = 6;
}
for (monToSwitchId = firstId; monToSwitchId < lastId; monToSwitchId++)
{
if (GetMonData(&party[monToSwitchId], MON_DATA_HP) == 0)
continue;
2018-02-06 20:48:02 +01:00
if (monToSwitchId == gBattlerPartyIndexes[bankIn1])
2017-10-10 21:45:07 +02:00
continue;
2018-02-06 20:48:02 +01:00
if (monToSwitchId == gBattlerPartyIndexes[bankIn2])
2017-10-10 21:45:07 +02:00
continue;
2017-11-12 17:06:31 +01:00
if (monToSwitchId == *(gBattleStruct->monToSwitchIntoId + bankIn1))
2017-10-10 21:45:07 +02:00
continue;
2017-11-12 17:06:31 +01:00
if (monToSwitchId == *(gBattleStruct->monToSwitchIntoId + bankIn2))
2017-10-10 21:45:07 +02:00
continue;
break;
}
}
2018-02-06 02:46:59 +01:00
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = monToSwitchId;
2017-10-10 21:45:07 +02:00
}
2018-02-06 02:46:59 +01:00
*(gBattleStruct->monToSwitchIntoId + gActiveBattler) = *(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler);
2017-10-10 21:45:07 +02:00
return;
}
else if (ShouldUseItem())
{
return;
}
}
2018-02-06 20:48:02 +01:00
BtlController_EmitTwoReturnValues(1, ACTION_USE_MOVE, (gActiveBattler ^ BIT_SIDE) << 8);
2017-10-10 21:45:07 +02:00
}
static void ModulateByTypeEffectiveness(u8 atkType, u8 defType1, u8 defType2, u8 *var)
{
s32 i = 0;
2017-11-12 17:06:31 +01:00
while (TYPE_EFFECT_ATK_TYPE(i) != TYPE_ENDTABLE)
2017-10-10 21:45:07 +02:00
{
2017-11-12 17:06:31 +01:00
if (TYPE_EFFECT_ATK_TYPE(i) == TYPE_FORESIGHT)
2017-10-10 21:45:07 +02:00
{
i += 3;
continue;
}
2017-11-12 17:06:31 +01:00
else if (TYPE_EFFECT_ATK_TYPE(i) == atkType)
2017-10-10 21:45:07 +02:00
{
// check type1
2017-11-12 17:06:31 +01:00
if (TYPE_EFFECT_DEF_TYPE(i) == defType1)
*var = (*var * TYPE_EFFECT_MULTIPLIER(i)) / 10;
2017-10-10 21:45:07 +02:00
// check type2
2017-11-12 17:06:31 +01:00
if (TYPE_EFFECT_DEF_TYPE(i) == defType2 && defType1 != defType2)
*var = (*var * TYPE_EFFECT_MULTIPLIER(i)) / 10;
2017-10-10 21:45:07 +02:00
}
i += 3;
}
}
u8 GetMostSuitableMonToSwitchInto(void)
{
u8 opposingBank;
u8 bestDmg; // note : should be changed to u32 for obvious reasons
u8 bestMonId;
u8 bankIn1, bankIn2;
s32 firstId;
s32 lastId; // + 1
struct Pokemon *party;
s32 i, j;
u8 invalidMons;
u16 move;
2018-02-06 02:46:59 +01:00
if (*(gBattleStruct->monToSwitchIntoId + gActiveBattler) != 6)
return *(gBattleStruct->monToSwitchIntoId + gActiveBattler);
2017-10-10 21:45:07 +02:00
if (gBattleTypeFlags & BATTLE_TYPE_ARENA)
2018-02-06 20:48:02 +01:00
return gBattlerPartyIndexes[gActiveBattler] + 1;
2017-10-10 21:45:07 +02:00
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
{
2018-02-06 02:46:59 +01:00
bankIn1 = gActiveBattler;
if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(GetBattlerPosition(gActiveBattler) ^ BIT_FLANK)])
bankIn2 = gActiveBattler;
2017-10-10 21:45:07 +02:00
else
2018-02-06 02:46:59 +01:00
bankIn2 = GetBattlerAtPosition(GetBattlerPosition(gActiveBattler) ^ BIT_FLANK);
2017-10-10 21:45:07 +02:00
// UB: It considers the opponent only player's side even though it can battle alongside player;
2018-02-06 02:46:59 +01:00
opposingBank = Random() & BIT_FLANK;
if (gAbsentBattlerFlags & gBitTable[opposingBank])
opposingBank ^= BIT_FLANK;
2017-10-10 21:45:07 +02:00
}
else
{
2018-02-06 02:46:59 +01:00
opposingBank = GetBattlerAtPosition(GetBattlerPosition(gActiveBattler) ^ BIT_SIDE);
bankIn1 = gActiveBattler;
bankIn2 = gActiveBattler;
2017-10-10 21:45:07 +02:00
}
if (gBattleTypeFlags & (BATTLE_TYPE_TWO_OPPONENTS | BATTLE_TYPE_x800000))
{
2018-02-06 02:46:59 +01:00
if ((gActiveBattler & BIT_FLANK) == 0)
2017-10-10 21:45:07 +02:00
firstId = 0, lastId = 3;
else
firstId = 3, lastId = 6;
}
else
{
firstId = 0, lastId = 6;
}
2018-02-06 02:46:59 +01:00
if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
2017-10-10 21:45:07 +02:00
party = gPlayerParty;
else
party = gEnemyParty;
invalidMons = 0;
while (invalidMons != 0x3F) // all mons are invalid
{
bestDmg = 0;
bestMonId = 6;
// find the mon which type is the most suitable offensively
for (i = firstId; i < lastId; i++)
{
u16 species = GetMonData(&party[i], MON_DATA_SPECIES);
if (species != SPECIES_NONE
&& GetMonData(&party[i], MON_DATA_HP) != 0
&& !(gBitTable[i] & invalidMons)
2018-02-06 20:48:02 +01:00
&& gBattlerPartyIndexes[bankIn1] != i
&& gBattlerPartyIndexes[bankIn2] != i
2017-11-12 17:06:31 +01:00
&& i != *(gBattleStruct->monToSwitchIntoId + bankIn1)
&& i != *(gBattleStruct->monToSwitchIntoId + bankIn2))
2017-10-10 21:45:07 +02:00
{
u8 type1 = gBaseStats[species].type1;
u8 type2 = gBaseStats[species].type2;
u8 typeDmg = 10;
ModulateByTypeEffectiveness(gBattleMons[opposingBank].type1, type1, type2, &typeDmg);
ModulateByTypeEffectiveness(gBattleMons[opposingBank].type2, type1, type2, &typeDmg);
if (bestDmg < typeDmg)
{
bestDmg = typeDmg;
bestMonId = i;
}
}
else
{
invalidMons |= gBitTable[i];
}
}
// ok, we know the mon has the right typing but does it have at least one super effective move?
if (bestMonId != 6)
{
for (i = 0; i < 4; i++)
{
move = GetMonData(&party[bestMonId], MON_DATA_MOVE1 + i);
2018-02-06 02:46:59 +01:00
if (move != MOVE_NONE && TypeCalc(move, gActiveBattler, opposingBank) & MOVE_RESULT_SUPER_EFFECTIVE)
2017-10-10 21:45:07 +02:00
break;
}
if (i != 4)
return bestMonId; // has both the typing and at least one super effective move
invalidMons |= gBitTable[bestMonId]; // sorry buddy, we want something better
}
else
{
invalidMons = 0x3F; // no viable mon to switch
}
}
gDynamicBasePower = 0;
gBattleStruct->dynamicMoveType = 0;
gBattleScripting.dmgMultiplier = 1;
2018-01-16 22:12:38 +01:00
gMoveResultFlags = 0;
2017-10-10 21:45:07 +02:00
gCritMultiplier = 1;
bestDmg = 0;
bestMonId = 6;
// if we couldn't find the best mon in terms of typing, find the one that deals most damage
for (i = firstId; i < lastId; i++)
{
if ((u16)(GetMonData(&party[i], MON_DATA_SPECIES)) == SPECIES_NONE)
continue;
if (GetMonData(&party[i], MON_DATA_HP) == 0)
continue;
2018-02-06 20:48:02 +01:00
if (gBattlerPartyIndexes[bankIn1] == i)
2017-10-10 21:45:07 +02:00
continue;
2018-02-06 20:48:02 +01:00
if (gBattlerPartyIndexes[bankIn2] == i)
2017-10-10 21:45:07 +02:00
continue;
2017-11-12 17:06:31 +01:00
if (i == *(gBattleStruct->monToSwitchIntoId + bankIn1))
2017-10-10 21:45:07 +02:00
continue;
2017-11-12 17:06:31 +01:00
if (i == *(gBattleStruct->monToSwitchIntoId + bankIn2))
2017-10-10 21:45:07 +02:00
continue;
for (j = 0; j < 4; j++)
{
move = GetMonData(&party[i], MON_DATA_MOVE1 + j);
gBattleMoveDamage = 0;
if (move != MOVE_NONE && gBattleMoves[move].power != 1)
{
2018-02-06 02:46:59 +01:00
AI_CalcDmg(gActiveBattler, opposingBank);
TypeCalc(move, gActiveBattler, opposingBank);
2017-10-10 21:45:07 +02:00
}
if (bestDmg < gBattleMoveDamage)
{
bestDmg = gBattleMoveDamage;
bestMonId = i;
}
}
}
return bestMonId;
}
2017-10-11 12:49:42 +02:00
// TODO: use PokemonItemEffect struct instead of u8 once it's documented
static u8 GetAI_ItemType(u8 itemId, const u8 *itemEffect) // NOTE: should take u16 as item Id argument
{
if (itemId == ITEM_FULL_RESTORE)
return AI_ITEM_FULL_RESTORE;
if (itemEffect[4] & 4)
return AI_ITEM_HEAL_HP;
if (itemEffect[3] & 0x3F)
return AI_ITEM_CURE_CONDITION;
if (itemEffect[0] & 0x3F || itemEffect[1] != 0 || itemEffect[2] != 0)
return AI_ITEM_X_STAT;
if (itemEffect[3] & 0x80)
return AI_ITEM_GUARD_SPECS;
return AI_ITEM_NOT_RECOGNIZABLE;
}
static bool8 ShouldUseItem(void)
{
struct Pokemon *party;
s32 i;
u8 validMons = 0;
bool8 shouldUse = FALSE;
2018-02-06 02:46:59 +01:00
if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && GetBattlerPosition(gActiveBattler) == B_POSITION_PLAYER_RIGHT)
2017-10-11 12:49:42 +02:00
return FALSE;
2018-02-06 02:46:59 +01:00
if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
2017-10-11 12:49:42 +02:00
party = gPlayerParty;
else
party = gEnemyParty;
for (i = 0; i < 6; i++)
{
if (GetMonData(&party[i], MON_DATA_HP) != 0
&& GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_NONE
&& GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_EGG)
{
validMons++;
}
}
for (i = 0; i < 4; i++)
{
u16 item;
const u8 *itemEffects;
u8 paramOffset;
u8 bankSide;
if (i != 0 && validMons > (gBattleResources->battleHistory->itemsNo - i) + 1)
continue;
item = gBattleResources->battleHistory->trainerItems[i];
if (item == ITEM_NONE)
continue;
if (gItemEffectTable[item - 13] == NULL)
continue;
if (item == ITEM_ENIGMA_BERRY)
itemEffects = gSaveBlock1Ptr->enigmaBerry.itemEffect;
else
itemEffects = gItemEffectTable[item - 13];
2018-02-06 02:46:59 +01:00
*(gBattleStruct->AI_itemType + gActiveBattler / 2) = GetAI_ItemType(item, itemEffects);
2017-10-11 12:49:42 +02:00
2018-02-06 02:46:59 +01:00
switch (*(gBattleStruct->AI_itemType + gActiveBattler / 2))
2017-10-11 12:49:42 +02:00
{
case AI_ITEM_FULL_RESTORE:
2018-02-06 02:46:59 +01:00
if (gBattleMons[gActiveBattler].hp >= gBattleMons[gActiveBattler].maxHP / 4)
2017-10-11 12:49:42 +02:00
break;
2018-02-06 02:46:59 +01:00
if (gBattleMons[gActiveBattler].hp == 0)
2017-10-11 12:49:42 +02:00
break;
shouldUse = TRUE;
break;
case AI_ITEM_HEAL_HP:
paramOffset = GetItemEffectParamOffset(item, 4, 4);
if (paramOffset == 0)
break;
2018-02-06 02:46:59 +01:00
if (gBattleMons[gActiveBattler].hp == 0)
2017-10-11 12:49:42 +02:00
break;
2018-02-06 02:46:59 +01:00
if (gBattleMons[gActiveBattler].hp < gBattleMons[gActiveBattler].maxHP / 4 || gBattleMons[gActiveBattler].maxHP - gBattleMons[gActiveBattler].hp > itemEffects[paramOffset])
2017-10-11 12:49:42 +02:00
shouldUse = TRUE;
break;
case AI_ITEM_CURE_CONDITION:
2018-02-06 02:46:59 +01:00
*(gBattleStruct->AI_itemFlags + gActiveBattler / 2) = 0;
if (itemEffects[3] & 0x20 && gBattleMons[gActiveBattler].status1 & STATUS1_SLEEP)
2017-10-11 12:49:42 +02:00
{
2018-02-06 02:46:59 +01:00
*(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x20;
2017-10-11 12:49:42 +02:00
shouldUse = TRUE;
}
2018-02-06 02:46:59 +01:00
if (itemEffects[3] & 0x10 && (gBattleMons[gActiveBattler].status1 & STATUS1_POISON || gBattleMons[gActiveBattler].status1 & STATUS1_TOXIC_POISON))
2017-10-11 12:49:42 +02:00
{
2018-02-06 02:46:59 +01:00
*(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x10;
2017-10-11 12:49:42 +02:00
shouldUse = TRUE;
}
2018-02-06 02:46:59 +01:00
if (itemEffects[3] & 0x8 && gBattleMons[gActiveBattler].status1 & STATUS1_BURN)
2017-10-11 12:49:42 +02:00
{
2018-02-06 02:46:59 +01:00
*(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x8;
2017-10-11 12:49:42 +02:00
shouldUse = TRUE;
}
2018-02-06 02:46:59 +01:00
if (itemEffects[3] & 0x4 && gBattleMons[gActiveBattler].status1 & STATUS1_FREEZE)
2017-10-11 12:49:42 +02:00
{
2018-02-06 02:46:59 +01:00
*(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x4;
2017-10-11 12:49:42 +02:00
shouldUse = TRUE;
}
2018-02-06 02:46:59 +01:00
if (itemEffects[3] & 0x2 && gBattleMons[gActiveBattler].status1 & STATUS1_PARALYSIS)
2017-10-11 12:49:42 +02:00
{
2018-02-06 02:46:59 +01:00
*(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x2;
2017-10-11 12:49:42 +02:00
shouldUse = TRUE;
}
2018-02-06 02:46:59 +01:00
if (itemEffects[3] & 0x1 && gBattleMons[gActiveBattler].status2 & STATUS2_CONFUSION)
2017-10-11 12:49:42 +02:00
{
2018-02-06 02:46:59 +01:00
*(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x1;
2017-10-11 12:49:42 +02:00
shouldUse = TRUE;
}
break;
case AI_ITEM_X_STAT:
2018-02-06 02:46:59 +01:00
*(gBattleStruct->AI_itemFlags + gActiveBattler / 2) = 0;
if (gDisableStructs[gActiveBattler].isFirstTurn == 0)
2017-10-11 12:49:42 +02:00
break;
if (itemEffects[0] & 0xF)
2018-02-06 02:46:59 +01:00
*(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x1;
2017-10-11 12:49:42 +02:00
if (itemEffects[1] & 0xF0)
2018-02-06 02:46:59 +01:00
*(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x2;
2017-10-11 12:49:42 +02:00
if (itemEffects[1] & 0xF)
2018-02-06 02:46:59 +01:00
*(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x4;
2017-10-11 12:49:42 +02:00
if (itemEffects[2] & 0xF)
2018-02-06 02:46:59 +01:00
*(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x8;
2017-10-11 12:49:42 +02:00
if (itemEffects[2] & 0xF0)
2018-02-06 02:46:59 +01:00
*(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x20;
2017-10-11 12:49:42 +02:00
if (itemEffects[0] & 0x30)
2018-02-06 02:46:59 +01:00
*(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x80;
2017-10-11 12:49:42 +02:00
shouldUse = TRUE;
break;
case AI_ITEM_GUARD_SPECS:
2018-02-06 02:46:59 +01:00
bankSide = GetBattlerSide(gActiveBattler);
if (gDisableStructs[gActiveBattler].isFirstTurn != 0 && gSideTimers[bankSide].mistTimer == 0)
2017-10-11 12:49:42 +02:00
shouldUse = TRUE;
break;
case AI_ITEM_NOT_RECOGNIZABLE:
return FALSE;
}
if (shouldUse)
{
2018-02-06 20:48:02 +01:00
BtlController_EmitTwoReturnValues(1, ACTION_USE_ITEM, 0);
2018-02-06 02:46:59 +01:00
*(gBattleStruct->chosenItem + (gActiveBattler / 2) * 2) = item;
2017-10-11 12:49:42 +02:00
gBattleResources->battleHistory->trainerItems[i] = 0;
return shouldUse;
}
}
return FALSE;
}