summaryrefslogtreecommitdiff
path: root/src/battle/battle_9.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/battle/battle_9.c')
-rwxr-xr-xsrc/battle/battle_9.c838
1 files changed, 837 insertions, 1 deletions
diff --git a/src/battle/battle_9.c b/src/battle/battle_9.c
index d9a1ee97e..c8bb89a0a 100755
--- a/src/battle/battle_9.c
+++ b/src/battle/battle_9.c
@@ -1,27 +1,863 @@
#include "global.h"
#include "battle.h"
+#include "battle_script_commands.h"
+#include "data2.h"
#include "ewram.h"
#include "pokemon.h"
#include "random.h"
#include "rom_8077ABC.h"
#include "rom3.h"
+#include "util.h"
#include "constants/abilities.h"
#include "constants/items.h"
#include "constants/moves.h"
#include "constants/species.h"
+extern u8 gUnknown_02023A14_50;
+
+extern u8 gLastHitBy[];
extern u8 gActiveBattler;
+extern u16 gBattleTypeFlags;
+extern u8 gAbsentBattlerFlags;
+extern s32 gBattleMoveDamage;
+extern u8 gMoveResultFlags;
+extern u16 gDynamicBasePower;
+extern u8 gCritMultiplier;
+extern u16 gBattlerPartyIndexes[];
+extern u16 gLastLandedMoves[];
+extern const u8 gTypeEffectiveness[];
+extern struct BattlePokemon gBattleMons[];
extern u32 gStatuses3[BATTLE_BANKS_COUNT];
+/*static*/ bool8 HasSuperEffectiveMoveAgainstOpponents(bool8 noRng);
+/*static*/ bool8 FindMonWithFlagsAndSuperEffective(u8 flags, u8 moduloPercent);
+/*static*/ bool8 ShouldUseItem(void);
+/*static*/ u8 GetMostSuitableMonToSwitchInto(void);
+
+
/*static*/ bool8 ShouldSwitchIfPerishSong(void)
{
if (gStatuses3[gActiveBattler] & STATUS3_PERISH_SONG
&& gDisableStructs[gActiveBattler].perishSongTimer1 == 0)
{
- ewram160C8arr(GetBankIdentity(gActiveBattler)) = 6; // gBattleStruct->AI_monToSwitchIntoId[GetBankIdentity(gActiveBattler)] = 6;
+ ewram160C8arr(GetBattlerPosition(gActiveBattler)) = 6; // gBattleStruct->AI_monToSwitchIntoId[GetBattlerPosition(gActiveBattler)] = 6;
Emitcmd33(1, 2, 0);
return TRUE;
}
return FALSE;
}
+
+#ifdef NONMATCHING
+/*static*/ bool8 ShouldSwitchIfWonderGuard(void)
+{
+ u8 opposingBank;
+ u8 moveFlags;
+ s32 i, j;
+
+ if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
+ return FALSE;
+
+ if (gBattleMons[GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)].ability != ABILITY_WONDER_GUARD)
+ return FALSE;
+
+ // check if pokemon has a super effective move
+ opposingBank = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
+ for (i = 0; i < 4; i++)
+ {
+ u16 move = gBattleMons[gActiveBattler].moves[i];
+ if (move == MOVE_NONE)
+ continue;
+
+ moveFlags = AI_TypeCalc(move, gBattleMons[opposingBank].species, gBattleMons[opposingBank].ability);
+ if (moveFlags & MOVE_RESULT_SUPER_EFFECTIVE)
+ return FALSE;
+ }
+
+ // find a pokemon in the party that has a super effective move
+ for (i = 0; i < 6; i++)
+ {
+ if (GetMonData(&gEnemyParty[i], MON_DATA_HP) == 0
+ || GetMonData(&gEnemyParty[i], MON_DATA_SPECIES2) == SPECIES_NONE
+ || GetMonData(&gEnemyParty[i], MON_DATA_SPECIES2) == SPECIES_EGG
+ || i == gBattlerPartyIndexes[gActiveBattler])
+ continue;
+
+ GetMonData(&gEnemyParty[i], MON_DATA_SPECIES); // unused return value
+ GetMonData(&gEnemyParty[i], MON_DATA_ALT_ABILITY); // unused return value
+
+ opposingBank = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
+ for (j = 0; j < 4; j++)
+ {
+ u16 move = GetMonData(&gEnemyParty[i], MON_DATA_MOVE1 + j);
+ if (move == MOVE_NONE)
+ continue;
+
+ moveFlags = AI_TypeCalc(move, gBattleMons[opposingBank].species, gBattleMons[opposingBank].ability);
+ if (moveFlags & MOVE_RESULT_SUPER_EFFECTIVE && (Random() % 3) < 2)
+ {
+ // we found a mon
+ ewram160C8arr(GetBattlerPosition(gActiveBattler)) = i; // gBattleStruct->AI_monToSwitchIntoId[GetBattlerPosition(gActiveBattler)] = i;
+ Emitcmd33(1, B_ACTION_SWITCH, 0);
+ 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
+}
+#else
+__attribute__((naked))
+/*static*/ bool8 ShouldSwitchIfWonderGuard(void)
+{
+ asm(".syntax unified\n\
+ push {r4-r7,lr}\n\
+ mov r7, r9\n\
+ mov r6, r8\n\
+ push {r6,r7}\n\
+ ldr r0, _0803606C @ =gBattleTypeFlags\n\
+ ldrh r1, [r0]\n\
+ movs r0, 0x1\n\
+ ands r0, r1\n\
+ cmp r0, 0\n\
+ beq _080360A0\n\
+ b _080361C8\n\
+ .align 2, 0\n\
+_0803606C: .4byte gBattleTypeFlags\n\
+_08036070:\n\
+ ldr r0, _08036094 @ =gActiveBattler\n\
+ ldrb r0, [r0]\n\
+ bl GetBattlerPosition\n\
+ ldr r1, _08036098 @ =gSharedMem\n\
+ lsls r0, 24\n\
+ lsrs r0, 25\n\
+ ldr r2, _0803609C @ =0x000160c8\n\
+ adds r0, r2\n\
+ adds r0, r1\n\
+ strb r6, [r0]\n\
+ movs r0, 0x1\n\
+ movs r1, 0x2\n\
+ movs r2, 0\n\
+ bl Emitcmd33\n\
+ movs r0, 0x1\n\
+ b _080361CA\n\
+ .align 2, 0\n\
+_08036094: .4byte gActiveBattler\n\
+_08036098: .4byte gSharedMem\n\
+_0803609C: .4byte 0x000160c8\n\
+_080360A0:\n\
+ ldr r4, _080361D8 @ =gBattleMons\n\
+ movs r0, 0\n\
+ bl GetBattlerAtPosition\n\
+ lsls r0, 24\n\
+ lsrs r0, 24\n\
+ movs r1, 0x58\n\
+ muls r0, r1\n\
+ adds r0, r4\n\
+ adds r0, 0x20\n\
+ ldrb r0, [r0]\n\
+ cmp r0, 0x19\n\
+ beq _080360BC\n\
+ b _080361C8\n\
+_080360BC:\n\
+ movs r0, 0\n\
+ bl GetBattlerAtPosition\n\
+ lsls r0, 24\n\
+ lsrs r2, r0, 24\n\
+ movs r6, 0\n\
+ adds r7, r4, 0\n\
+ movs r5, 0x58\n\
+ adds r0, r2, 0\n\
+ muls r0, r5\n\
+ adds r4, r0, r7\n\
+ movs r3, 0x20\n\
+ adds r3, r4\n\
+ mov r8, r3\n\
+_080360D8:\n\
+ lsls r1, r6, 1\n\
+ ldr r0, _080361DC @ =gActiveBattler\n\
+ ldrb r0, [r0]\n\
+ muls r0, r5\n\
+ adds r1, r0\n\
+ adds r0, r7, 0\n\
+ adds r0, 0xC\n\
+ adds r1, r0\n\
+ ldrh r0, [r1]\n\
+ cmp r0, 0\n\
+ beq _08036104\n\
+ ldrh r1, [r4]\n\
+ mov r3, r8\n\
+ ldrb r2, [r3]\n\
+ bl AI_TypeCalc\n\
+ lsls r0, 24\n\
+ lsrs r1, r0, 24\n\
+ movs r0, 0x2\n\
+ ands r1, r0\n\
+ cmp r1, 0\n\
+ bne _080361C8\n\
+_08036104:\n\
+ adds r6, 0x1\n\
+ cmp r6, 0x3\n\
+ ble _080360D8\n\
+ movs r6, 0\n\
+ ldr r0, _080361E0 @ =gEnemyParty\n\
+ mov r9, r0\n\
+_08036110:\n\
+ movs r0, 0x64\n\
+ adds r5, r6, 0\n\
+ muls r5, r0\n\
+ mov r2, r9\n\
+ adds r4, r5, r2\n\
+ adds r0, r4, 0\n\
+ movs r1, 0x39\n\
+ bl GetMonData\n\
+ cmp r0, 0\n\
+ beq _080361C2\n\
+ adds r0, r4, 0\n\
+ movs r1, 0x41\n\
+ bl GetMonData\n\
+ cmp r0, 0\n\
+ beq _080361C2\n\
+ adds r0, r4, 0\n\
+ movs r1, 0x41\n\
+ bl GetMonData\n\
+ movs r1, 0xCE\n\
+ lsls r1, 1\n\
+ cmp r0, r1\n\
+ beq _080361C2\n\
+ ldr r1, _080361E4 @ =gBattlerPartyIndexes\n\
+ ldr r0, _080361DC @ =gActiveBattler\n\
+ ldrb r0, [r0]\n\
+ lsls r0, 1\n\
+ adds r0, r1\n\
+ ldrh r0, [r0]\n\
+ cmp r6, r0\n\
+ beq _080361C2\n\
+ adds r0, r4, 0\n\
+ movs r1, 0xB\n\
+ bl GetMonData\n\
+ adds r0, r4, 0\n\
+ movs r1, 0x2E\n\
+ bl GetMonData\n\
+ movs r0, 0\n\
+ bl GetBattlerAtPosition\n\
+ lsls r0, 24\n\
+ lsrs r2, r0, 24\n\
+ movs r4, 0\n\
+ mov r8, r5\n\
+ ldr r1, _080361D8 @ =gBattleMons\n\
+ movs r0, 0x58\n\
+ muls r0, r2\n\
+ adds r5, r0, r1\n\
+ adds r7, r5, 0\n\
+ adds r7, 0x20\n\
+_0803617C:\n\
+ adds r1, r4, 0\n\
+ adds r1, 0xD\n\
+ mov r0, r8\n\
+ add r0, r9\n\
+ bl GetMonData\n\
+ lsls r0, 16\n\
+ lsrs r0, 16\n\
+ cmp r0, 0\n\
+ beq _080361BC\n\
+ ldrh r1, [r5]\n\
+ ldrb r2, [r7]\n\
+ bl AI_TypeCalc\n\
+ lsls r0, 24\n\
+ lsrs r1, r0, 24\n\
+ movs r0, 0x2\n\
+ ands r1, r0\n\
+ cmp r1, 0\n\
+ beq _080361BC\n\
+ bl Random\n\
+ lsls r0, 16\n\
+ lsrs r0, 16\n\
+ movs r1, 0x3\n\
+ bl __umodsi3\n\
+ lsls r0, 16\n\
+ lsrs r0, 16\n\
+ cmp r0, 0x1\n\
+ bhi _080361BC\n\
+ b _08036070\n\
+_080361BC:\n\
+ adds r4, 0x1\n\
+ cmp r4, 0x3\n\
+ ble _0803617C\n\
+_080361C2:\n\
+ adds r6, 0x1\n\
+ cmp r6, 0x5\n\
+ ble _08036110\n\
+_080361C8:\n\
+ movs r0, 0\n\
+_080361CA:\n\
+ pop {r3,r4}\n\
+ mov r8, r3\n\
+ mov r9, r4\n\
+ pop {r4-r7}\n\
+ pop {r1}\n\
+ bx r1\n\
+ .align 2, 0\n\
+_080361D8: .4byte gBattleMons\n\
+_080361DC: .4byte gActiveBattler\n\
+_080361E0: .4byte gEnemyParty\n\
+_080361E4: .4byte gBattlerPartyIndexes\n\
+ .syntax divided\n");
+}
+#endif // NONMATCHING
+
+/*static*/ bool8 FindMonThatAbsorbsOpponentsMove(void)
+{
+ u8 bankIn1, bankIn2;
+ u8 absorbingTypeAbility;
+ s32 i;
+
+ if (HasSuperEffectiveMoveAgainstOpponents(TRUE) && Random() % 3 != 0)
+ return FALSE;
+ if (gLastLandedMoves[gActiveBattler] == 0)
+ return FALSE;
+ if (gLastLandedMoves[gActiveBattler] == 0xFFFF)
+ return FALSE;
+ if (gBattleMoves[gLastLandedMoves[gActiveBattler]].power == 0)
+ return FALSE;
+
+ if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
+ {
+ bankIn1 = gActiveBattler;
+ if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler)))])
+ bankIn2 = gActiveBattler;
+ else
+ bankIn2 = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler)));
+ }
+ else
+ {
+ bankIn1 = gActiveBattler;
+ bankIn2 = gActiveBattler;
+ }
+
+ if (gBattleMoves[gLastLandedMoves[gActiveBattler]].type == TYPE_FIRE)
+ absorbingTypeAbility = ABILITY_FLASH_FIRE;
+ else if (gBattleMoves[gLastLandedMoves[gActiveBattler]].type == TYPE_WATER)
+ absorbingTypeAbility = ABILITY_WATER_ABSORB;
+ else if (gBattleMoves[gLastLandedMoves[gActiveBattler]].type == TYPE_ELECTRIC)
+ absorbingTypeAbility = ABILITY_VOLT_ABSORB;
+ else
+ return FALSE;
+
+ if (gBattleMons[gActiveBattler].ability == absorbingTypeAbility)
+ return FALSE;
+
+ for (i = 0; i < 6; i++)
+ {
+ u16 species;
+ u8 monAbility;
+
+ if (GetMonData(&gEnemyParty[i], MON_DATA_HP) == 0)
+ continue;
+ if (GetMonData(&gEnemyParty[i], MON_DATA_SPECIES2) == SPECIES_NONE)
+ continue;
+ if (GetMonData(&gEnemyParty[i], MON_DATA_SPECIES2) == SPECIES_EGG)
+ continue;
+ if (i == gBattlerPartyIndexes[bankIn1])
+ continue;
+ if (i == gBattlerPartyIndexes[bankIn2])
+ continue;
+ if (i == ewram16068arr(bankIn1))
+ continue;
+ if (i == ewram16068arr(bankIn2))
+ continue;
+
+ species = GetMonData(&gEnemyParty[i], MON_DATA_SPECIES);
+ if (GetMonData(&gEnemyParty[i], MON_DATA_ALT_ABILITY) != 0)
+ monAbility = gBaseStats[species].ability2;
+ else
+ monAbility = gBaseStats[species].ability1;
+
+ if (absorbingTypeAbility == monAbility && Random() & 1)
+ {
+ // we found a mon
+ ewram160C8arr(GetBattlerPosition(gActiveBattler)) = i;
+ Emitcmd33(1, B_ACTION_SWITCH, 0);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/*static*/ bool8 ShouldSwitchIfNaturalCure(void)
+{
+ if (!(gBattleMons[gActiveBattler].status1 & STATUS_SLEEP))
+ return FALSE;
+ if (gBattleMons[gActiveBattler].ability != ABILITY_NATURAL_CURE)
+ return FALSE;
+ if (gBattleMons[gActiveBattler].hp < gBattleMons[gActiveBattler].maxHP / 2)
+ return FALSE;
+
+ if ((gLastLandedMoves[gActiveBattler] == 0 || gLastLandedMoves[gActiveBattler] == 0xFFFF) && Random() & 1)
+ {
+ ewram160C8arr(GetBattlerPosition(gActiveBattler)) = 6;
+ Emitcmd33(1, B_ACTION_SWITCH, 0);
+ return TRUE;
+ }
+ else if (gBattleMoves[gLastLandedMoves[gActiveBattler]].power == 0 && Random() & 1)
+ {
+ ewram160C8arr(GetBattlerPosition(gActiveBattler)) = 6;
+ Emitcmd33(1, B_ACTION_SWITCH, 0);
+ return TRUE;
+ }
+
+ if (FindMonWithFlagsAndSuperEffective(MOVE_RESULT_DOESNT_AFFECT_FOE, 1))
+ return TRUE;
+ if (FindMonWithFlagsAndSuperEffective(MOVE_RESULT_NOT_VERY_EFFECTIVE, 1))
+ return TRUE;
+ if (Random() & 1)
+ {
+ ewram160C8arr(GetBattlerPosition(gActiveBattler)) = 6;
+ Emitcmd33(1, B_ACTION_SWITCH, 0);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*static*/ bool8 HasSuperEffectiveMoveAgainstOpponents(bool8 noRng)
+{
+ u8 opposingBank;
+ s32 i;
+ u8 moveFlags;
+ u16 move;
+
+ opposingBank = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
+ if (!(gAbsentBattlerFlags & gBitTable[opposingBank]))
+ {
+ for (i = 0; i < 4; i++)
+ {
+ move = gBattleMons[gActiveBattler].moves[i];
+ if (move == MOVE_NONE)
+ continue;
+
+ moveFlags = AI_TypeCalc(move, gBattleMons[opposingBank].species, gBattleMons[opposingBank].ability);
+ if (moveFlags & MOVE_RESULT_SUPER_EFFECTIVE)
+ {
+ if (noRng)
+ return TRUE;
+ if (Random() % 10 != 0)
+ return TRUE;
+ }
+ }
+ }
+ if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
+ return FALSE;
+
+ opposingBank = GetBattlerAtPosition(BATTLE_PARTNER(B_POSITION_PLAYER_LEFT));
+ if (!(gAbsentBattlerFlags & gBitTable[opposingBank]))
+ {
+ for (i = 0; i < 4; i++)
+ {
+ move = gBattleMons[gActiveBattler].moves[i];
+ if (move == MOVE_NONE)
+ continue;
+
+ moveFlags = AI_TypeCalc(move, gBattleMons[opposingBank].species, gBattleMons[opposingBank].ability);
+ if (moveFlags & MOVE_RESULT_SUPER_EFFECTIVE)
+ {
+ if (noRng)
+ return TRUE;
+ if (Random() % 10 != 0)
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+/*static*/ bool8 AreStatsRaised(void)
+{
+ u8 buffedStatsValue = 0;
+ s32 i;
+
+ for (i = 0; i < BATTLE_STATS_NO; i++)
+ {
+ if (gBattleMons[gActiveBattler].statStages[i] > 6)
+ buffedStatsValue += gBattleMons[gActiveBattler].statStages[i] - 6;
+ }
+
+ return (buffedStatsValue > 3);
+}
+
+/*static*/ bool8 FindMonWithFlagsAndSuperEffective(u8 flags, u8 moduloPercent)
+{
+ u8 bankIn1, bankIn2;
+ s32 i, j;
+ u16 move;
+ u8 moveFlags;
+
+ if (gLastLandedMoves[gActiveBattler] == 0)
+ return FALSE;
+ if (gLastLandedMoves[gActiveBattler] == 0xFFFF)
+ return FALSE;
+ if (gLastHitBy[gActiveBattler] == 0xFF)
+ return FALSE;
+ if (gBattleMoves[gLastLandedMoves[gActiveBattler]].power == 0)
+ return FALSE;
+
+ if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
+ {
+ bankIn1 = gActiveBattler;
+ if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler)))])
+ bankIn2 = gActiveBattler;
+ else
+ bankIn2 = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler)));
+ }
+ else
+ {
+ bankIn1 = gActiveBattler;
+ bankIn2 = gActiveBattler;
+ }
+
+ for (i = 0; i < 6; i++)
+ {
+ u16 species;
+ u8 monAbility;
+
+ if (GetMonData(&gEnemyParty[i], MON_DATA_HP) == 0)
+ continue;
+ if (GetMonData(&gEnemyParty[i], MON_DATA_SPECIES2) == SPECIES_NONE)
+ continue;
+ if (GetMonData(&gEnemyParty[i], MON_DATA_SPECIES2) == SPECIES_EGG)
+ continue;
+ if (i == gBattlerPartyIndexes[bankIn1])
+ continue;
+ if (i == gBattlerPartyIndexes[bankIn2])
+ continue;
+ if (i == ewram16068arr(bankIn1))
+ continue;
+ if (i == ewram16068arr(bankIn2))
+ continue;
+
+ species = GetMonData(&gEnemyParty[i], MON_DATA_SPECIES);
+ if (GetMonData(&gEnemyParty[i], MON_DATA_ALT_ABILITY) != 0)
+ monAbility = gBaseStats[species].ability2;
+ else
+ monAbility = gBaseStats[species].ability1;
+
+ moveFlags = AI_TypeCalc(gLastLandedMoves[gActiveBattler], species, monAbility);
+ if (moveFlags & flags)
+ {
+ bankIn1 = gLastHitBy[gActiveBattler];
+
+ for (j = 0; j < 4; j++)
+ {
+ move = GetMonData(&gEnemyParty[i], MON_DATA_MOVE1 + j);
+ if (move == 0)
+ continue;
+
+ moveFlags = AI_TypeCalc(move, gBattleMons[bankIn1].species, gBattleMons[bankIn1].ability);
+ if (moveFlags & MOVE_RESULT_SUPER_EFFECTIVE && Random() % moduloPercent == 0)
+ {
+ ewram160C8arr(GetBattlerPosition(gActiveBattler)) = i;
+ Emitcmd33(1, B_ACTION_SWITCH, 0);
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+/*static*/ bool8 ShouldSwitch(void)
+{
+ u8 bankIn1, bankIn2;
+ s32 i;
+ s32 availableToSwitch;
+
+ if (gBattleMons[gActiveBattler].status2 & (STATUS2_WRAPPED | STATUS2_ESCAPE_PREVENTION))
+ return FALSE;
+ if (gStatuses3[gActiveBattler] & STATUS3_ROOTED)
+ return FALSE;
+ if (AbilityBattleEffects(ABILITYEFFECT_CHECK_OTHER_SIDE, gActiveBattler, ABILITY_SHADOW_TAG, 0, 0))
+ return FALSE;
+ if (AbilityBattleEffects(ABILITYEFFECT_CHECK_OTHER_SIDE, gActiveBattler, ABILITY_ARENA_TRAP, 0, 0))
+ return FALSE; // misses the flying or levitate check
+ if (AbilityBattleEffects(ABILITYEFFECT_FIELD_SPORT, 0, ABILITY_MAGNET_PULL, 0, 0))
+ {
+ if (gBattleMons[gActiveBattler].type1 == TYPE_STEEL)
+ return FALSE;
+ if (gBattleMons[gActiveBattler].type2 == TYPE_STEEL)
+ return FALSE;
+ }
+
+ availableToSwitch = 0;
+ if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
+ {
+ bankIn1 = gActiveBattler;
+ if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(GetBattlerPosition(gActiveBattler) ^ BIT_FLANK)])
+ bankIn2 = gActiveBattler;
+ else
+ bankIn2 = GetBattlerAtPosition(GetBattlerPosition(gActiveBattler) ^ BIT_FLANK);
+ }
+ else
+ {
+ bankIn1 = gActiveBattler;
+ bankIn2 = gActiveBattler;
+ }
+
+ for (i = 0; i < 6; i++)
+ {
+ if (GetMonData(&gEnemyParty[i], MON_DATA_HP) == 0)
+ continue;
+ if (GetMonData(&gEnemyParty[i], MON_DATA_SPECIES2) == SPECIES_NONE)
+ continue;
+ if (GetMonData(&gEnemyParty[i], MON_DATA_SPECIES2) == SPECIES_EGG)
+ continue;
+ if (i == gBattlerPartyIndexes[bankIn1])
+ continue;
+ if (i == gBattlerPartyIndexes[bankIn2])
+ continue;
+ if (i == ewram16068arr(bankIn1))
+ continue;
+ if (i == ewram16068arr(bankIn2))
+ continue;
+
+ availableToSwitch++;
+ }
+
+ if (availableToSwitch == 0)
+ return FALSE;
+ if (ShouldSwitchIfPerishSong())
+ return TRUE;
+ if (ShouldSwitchIfWonderGuard())
+ return TRUE;
+ if (FindMonThatAbsorbsOpponentsMove())
+ return TRUE;
+ if (ShouldSwitchIfNaturalCure())
+ return TRUE;
+ if (HasSuperEffectiveMoveAgainstOpponents(FALSE))
+ return FALSE;
+ if (AreStatsRaised())
+ return FALSE;
+ if (FindMonWithFlagsAndSuperEffective(MOVE_RESULT_DOESNT_AFFECT_FOE, 2)
+ || FindMonWithFlagsAndSuperEffective(MOVE_RESULT_NOT_VERY_EFFECTIVE, 3))
+ return TRUE;
+
+ return FALSE;
+}
+
+void AI_TrySwitchOrUseItem(void)
+{
+ u8 bankIn1, bankIn2;
+
+ if (gBattleTypeFlags & BATTLE_TYPE_TRAINER)
+ {
+ if (ShouldSwitch())
+ {
+ if (ewram160C8arr(GetBattlerPosition(gActiveBattler)) == 6)
+ {
+ s32 monToSwitchId = GetMostSuitableMonToSwitchInto();
+ if (monToSwitchId == 6)
+ {
+ if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
+ {
+ bankIn1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
+ bankIn2 = bankIn1;
+ }
+ else
+ {
+ bankIn1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
+ bankIn2 = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
+ }
+
+ for (monToSwitchId = 0; monToSwitchId < 6; monToSwitchId++)
+ {
+ if (GetMonData(&gEnemyParty[monToSwitchId], MON_DATA_HP) == 0)
+ continue;
+ if (monToSwitchId == gBattlerPartyIndexes[bankIn1])
+ continue;
+ if (monToSwitchId == gBattlerPartyIndexes[bankIn2])
+ continue;
+ if (monToSwitchId == ewram16068arr(bankIn1))
+ continue;
+ if (monToSwitchId == ewram16068arr(bankIn2))
+ continue;
+
+ break;
+ }
+ }
+
+ ewram160C8arr(GetBattlerPosition(gActiveBattler)) = monToSwitchId;
+ }
+
+ ewram16068arr(gActiveBattler) = ewram160C8arr(GetBattlerPosition(gActiveBattler));
+ return;
+ }
+ else
+ {
+ #if DEBUG
+ if (!(gUnknown_02023A14_50 & 0x20) && ShouldUseItem())
+ return;
+ #else
+ if (ShouldUseItem())
+ return;
+ #endif
+ }
+ }
+
+ Emitcmd33(1, B_ACTION_USE_MOVE, (gActiveBattler ^ BIT_SIDE) << 8);
+}
+
+/*static*/ void ModulateByTypeEffectiveness(u8 attackType, u8 defenseType1, u8 defenseType2, u8 *var)
+{
+ s32 i = 0;
+
+ while (TYPE_EFFECT_ATK_TYPE(i) != TYPE_ENDTABLE)
+ {
+ if (TYPE_EFFECT_ATK_TYPE(i) == TYPE_FORESIGHT)
+ {
+ i += 3;
+ continue;
+ }
+ else if (TYPE_EFFECT_ATK_TYPE(i) == attackType)
+ {
+ // check type1
+ if (TYPE_EFFECT_DEF_TYPE(i) == defenseType1)
+ *var = (*var * TYPE_EFFECT_MULTIPLIER(i)) / 10;
+ // check type2
+ if (TYPE_EFFECT_DEF_TYPE(i) == defenseType2 && defenseType1 != defenseType2)
+ *var = (*var * TYPE_EFFECT_MULTIPLIER(i)) / 10;
+ }
+ i += 3;
+ }
+}
+
+u8 GetMostSuitableMonToSwitchInto(void)
+{
+ u8 opposingBank;
+ u8 bestDmg; // note : should be changed to s32
+ u8 bestMonId;
+ u8 bankIn1, bankIn2;
+ s32 i, j;
+ u8 invalidMons;
+ u16 move;
+
+ if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
+ {
+ bankIn1 = gActiveBattler;
+ if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(GetBattlerPosition(gActiveBattler) ^ BIT_FLANK)])
+ bankIn2 = gActiveBattler;
+ else
+ bankIn2 = GetBattlerAtPosition(GetBattlerPosition(gActiveBattler) ^ BIT_FLANK);
+
+ // UB: It considers the opponent only player's side even though it can battle alongside player;
+ opposingBank = Random() & BIT_FLANK;
+ if (gAbsentBattlerFlags & gBitTable[opposingBank])
+ opposingBank ^= BIT_FLANK;
+ }
+ else
+ {
+ opposingBank = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
+ bankIn1 = gActiveBattler;
+ bankIn2 = gActiveBattler;
+ }
+
+ 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 = 0; i < 6; i++)
+ {
+ u16 species = GetMonData(&gEnemyParty[i], MON_DATA_SPECIES);
+ if (species != SPECIES_NONE
+ && GetMonData(&gEnemyParty[i], MON_DATA_HP) != 0
+ && !(gBitTable[i] & invalidMons)
+ && gBattlerPartyIndexes[bankIn1] != i
+ && gBattlerPartyIndexes[bankIn2] != i
+ && i != ewram16068arr(bankIn1)
+ && i != ewram16068arr(bankIn2))
+ {
+ 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(&gEnemyParty[bestMonId], MON_DATA_MOVE1 + i);
+ if (move != MOVE_NONE && TypeCalc(move, gActiveBattler, opposingBank) & MOVE_RESULT_SUPER_EFFECTIVE)
+ 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;
+ gBattleStruct->dmgMultiplier = 1;
+ gMoveResultFlags = 0;
+ 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 = 0; i < 6; i++)
+ {
+ if ((u16)(GetMonData(&gEnemyParty[i], MON_DATA_SPECIES)) == SPECIES_NONE)
+ continue;
+ if (GetMonData(&gEnemyParty[i], MON_DATA_HP) == 0)
+ continue;
+ if (gBattlerPartyIndexes[bankIn1] == i)
+ continue;
+ if (gBattlerPartyIndexes[bankIn2] == i)
+ continue;
+ if (i == ewram16068arr(bankIn1))
+ continue;
+ if (i == ewram16068arr(bankIn2))
+ continue;
+
+ for (j = 0; j < 4; j++)
+ {
+ move = GetMonData(&gEnemyParty[i], MON_DATA_MOVE1 + j);
+ gBattleMoveDamage = 0;
+ if (move != MOVE_NONE && gBattleMoves[move].power != 1)
+ {
+ AI_CalcDmg(gActiveBattler, opposingBank);
+ TypeCalc(move, gActiveBattler, opposingBank);
+ }
+ if (bestDmg < gBattleMoveDamage)
+ {
+ bestDmg = gBattleMoveDamage;
+ bestMonId = i;
+ }
+ }
+ }
+
+ return bestMonId;
+}