summaryrefslogtreecommitdiff
path: root/src/battle_ai_switch_items.c
diff options
context:
space:
mode:
authorjiangzhengwenjz <jiangzhengwenjzw@qq.com>2019-08-03 23:59:41 +0800
committerjiangzhengwenjz <jiangzhengwenjzw@qq.com>2019-08-03 23:59:41 +0800
commit5568895b043dc37e95be0e99787541d78a7c6fee (patch)
tree719b85960d6e9c7b8add6a141fa503925290a5c9 /src/battle_ai_switch_items.c
parent8fed9a5f205cec0d86149a908d64f73316cbb1f4 (diff)
ported battle_ai_switch_items from pokeem
Diffstat (limited to 'src/battle_ai_switch_items.c')
-rw-r--r--src/battle_ai_switch_items.c734
1 files changed, 734 insertions, 0 deletions
diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c
new file mode 100644
index 000000000..6f059e9a5
--- /dev/null
+++ b/src/battle_ai_switch_items.c
@@ -0,0 +1,734 @@
+#include "global.h"
+#include "battle.h"
+#include "battle_anim.h"
+#include "battle_controllers.h"
+#include "pokemon.h"
+#include "random.h"
+#include "util.h"
+#include "constants/abilities.h"
+#include "constants/item_effects.h"
+#include "constants/items.h"
+#include "constants/moves.h"
+#include "constants/species.h"
+#include "constants/pokemon.h"
+
+static bool8 HasSuperEffectiveMoveAgainstOpponents(bool8 noRng);
+static bool8 FindMonWithFlagsAndSuperEffective(u8 flags, u8 moduloPercent);
+static bool8 ShouldUseItem(void);
+
+static bool8 ShouldSwitchIfPerishSong(void)
+{
+ if (gStatuses3[gActiveBattler] & STATUS3_PERISH_SONG
+ && gDisableStructs[gActiveBattler].perishSongTimer == 0)
+ {
+ *(gBattleStruct->AI_monToSwitchIntoId + (GetBattlerPosition(gActiveBattler) >> 1)) = PARTY_SIZE;
+ BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0);
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+static bool8 ShouldSwitchIfWonderGuard(void)
+{
+ u8 opposingBattler;
+ u8 moveFlags;
+ s32 i, j;
+ u16 move;
+
+ if(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
+ return FALSE;
+ if (gBattleMons[GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)].ability == ABILITY_WONDER_GUARD)
+ {
+ // Check if Pokemon has a super effective move.
+ for (opposingBattler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT), i = 0; i < MAX_MON_MOVES; ++i)
+ {
+ move = gBattleMons[gActiveBattler].moves[i];
+ if (move == MOVE_NONE)
+ continue;
+ moveFlags = AI_TypeCalc(move, gBattleMons[opposingBattler].species, gBattleMons[opposingBattler].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 < PARTY_SIZE; ++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[gActiveBattler])
+ continue;
+ GetMonData(&gEnemyParty[i], MON_DATA_SPECIES); // Unused return value.
+ GetMonData(&gEnemyParty[i], MON_DATA_ABILITY_NUM); // Unused return value.
+
+ for (opposingBattler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT), j = 0; j < MAX_MON_MOVES; ++j)
+ {
+ move = GetMonData(&gEnemyParty[i], MON_DATA_MOVE1 + j);
+ if (move == MOVE_NONE)
+ continue;
+ moveFlags = AI_TypeCalc(move, gBattleMons[opposingBattler].species, gBattleMons[opposingBattler].ability);
+ if (moveFlags & MOVE_RESULT_SUPER_EFFECTIVE && Random() % 3 < 2)
+ {
+ // We found a mon.
+ *(gBattleStruct->AI_monToSwitchIntoId + (GetBattlerPosition(gActiveBattler) >> 1)) = i;
+ BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0);
+ return TRUE;
+ }
+ }
+ }
+ }
+ return FALSE; // There is not a single Pokemon in the party that has a super effective move against a mon with Wonder Guard.
+}
+
+static bool8 FindMonThatAbsorbsOpponentsMove(void)
+{
+ u8 battlerIn1, battlerIn2;
+ u8 absorbingTypeAbility;
+ s32 i;
+
+ if (HasSuperEffectiveMoveAgainstOpponents(TRUE) && Random() % 3)
+ return FALSE;
+ if (gLastLandedMoves[gActiveBattler] == MOVE_NONE)
+ return FALSE;
+ if (gLastLandedMoves[gActiveBattler] == 0xFFFF)
+ return FALSE;
+ if (gBattleMoves[gLastLandedMoves[gActiveBattler]].power == 0)
+ return FALSE;
+ if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
+ {
+ battlerIn1 = gActiveBattler;
+ if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler)))])
+ battlerIn2 = gActiveBattler;
+ else
+ battlerIn2 = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler)));
+ }
+ else
+ {
+ battlerIn1 = gActiveBattler;
+ battlerIn2 = 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 < PARTY_SIZE; ++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[battlerIn1])
+ continue;
+ if (i == gBattlerPartyIndexes[battlerIn2])
+ continue;
+ if (i == *(gBattleStruct->monToSwitchIntoId + battlerIn1))
+ continue;
+ if (i == *(gBattleStruct->monToSwitchIntoId + battlerIn2))
+ continue;
+ species = GetMonData(&gEnemyParty[i], MON_DATA_SPECIES);
+ if (GetMonData(&gEnemyParty[i], MON_DATA_ABILITY_NUM) != ABILITY_NONE)
+ monAbility = gBaseStats[species].abilities[1];
+ else
+ monAbility = gBaseStats[species].abilities[0];
+ if (absorbingTypeAbility == monAbility && Random() & 1)
+ {
+ // we found a mon
+ *(gBattleStruct->AI_monToSwitchIntoId + (GetBattlerPosition(gActiveBattler) >> 1)) = i;
+ BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static bool8 ShouldSwitchIfNaturalCure(void)
+{
+ if (!(gBattleMons[gActiveBattler].status1 & STATUS1_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] == MOVE_NONE || gLastLandedMoves[gActiveBattler] == 0xFFFF) && Random() & 1)
+ {
+ *(gBattleStruct->AI_monToSwitchIntoId + (GetBattlerPosition(gActiveBattler) >> 1)) = PARTY_SIZE;
+ BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0);
+ return TRUE;
+ }
+ else if (gBattleMoves[gLastLandedMoves[gActiveBattler]].power == 0 && Random() & 1)
+ {
+ *(gBattleStruct->AI_monToSwitchIntoId + (GetBattlerPosition(gActiveBattler) >> 1)) = PARTY_SIZE;
+ BtlController_EmitTwoReturnValues(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)
+ {
+ *(gBattleStruct->AI_monToSwitchIntoId + (GetBattlerPosition(gActiveBattler) >> 1)) = PARTY_SIZE;
+ BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static bool8 HasSuperEffectiveMoveAgainstOpponents(bool8 noRng)
+{
+ u8 opposingBattler;
+ s32 i;
+ u8 moveFlags;
+ u16 move;
+
+ opposingBattler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
+ if (!(gAbsentBattlerFlags & gBitTable[opposingBattler]))
+ {
+ for (i = 0; i < MAX_MON_MOVES; ++i)
+ {
+ move = gBattleMons[gActiveBattler].moves[i];
+ if (move == MOVE_NONE)
+ continue;
+
+ moveFlags = AI_TypeCalc(move, gBattleMons[opposingBattler].species, gBattleMons[opposingBattler].ability);
+ if (moveFlags & MOVE_RESULT_SUPER_EFFECTIVE)
+ {
+ if (noRng)
+ return TRUE;
+ if (Random() % 10 != 0)
+ return TRUE;
+ }
+ }
+ }
+ if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
+ return FALSE;
+ opposingBattler = GetBattlerAtPosition(BATTLE_PARTNER(B_POSITION_PLAYER_LEFT));
+ if (!(gAbsentBattlerFlags & gBitTable[opposingBattler]))
+ {
+ for (i = 0; i < MAX_MON_MOVES; ++i)
+ {
+ move = gBattleMons[gActiveBattler].moves[i];
+ if (move == MOVE_NONE)
+ continue;
+ moveFlags = AI_TypeCalc(move, gBattleMons[opposingBattler].species, gBattleMons[opposingBattler].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 < NUM_BATTLE_STATS; ++i)
+ {
+ if (gBattleMons[gActiveBattler].statStages[i] > 6)
+ buffedStatsValue += gBattleMons[gActiveBattler].statStages[i] - 6;
+ }
+ return (buffedStatsValue > 3);
+}
+
+static bool8 FindMonWithFlagsAndSuperEffective(u8 flags, u8 moduloPercent)
+{
+ u8 battlerIn1, battlerIn2;
+ 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)
+ {
+ battlerIn1 = gActiveBattler;
+ if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler)))])
+ battlerIn2 = gActiveBattler;
+ else
+ battlerIn2 = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler)));
+ }
+ else
+ {
+ battlerIn1 = gActiveBattler;
+ battlerIn2 = gActiveBattler;
+ }
+
+ for (i = 0; i < PARTY_SIZE; ++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[battlerIn1])
+ continue;
+ if (i == gBattlerPartyIndexes[battlerIn2])
+ continue;
+ if (i == *(gBattleStruct->monToSwitchIntoId + battlerIn1))
+ continue;
+ if (i == *(gBattleStruct->monToSwitchIntoId + battlerIn2))
+ continue;
+ species = GetMonData(&gEnemyParty[i], MON_DATA_SPECIES);
+ if (GetMonData(&gEnemyParty[i], MON_DATA_ABILITY_NUM) != ABILITY_NONE)
+ monAbility = gBaseStats[species].abilities[1];
+ else
+ monAbility = gBaseStats[species].abilities[0];
+ moveFlags = AI_TypeCalc(gLastLandedMoves[gActiveBattler], species, monAbility);
+ if (moveFlags & flags)
+ {
+ battlerIn1 = gLastHitBy[gActiveBattler];
+ for (j = 0; j < MAX_MON_MOVES; ++j)
+ {
+ move = GetMonData(&gEnemyParty[i], MON_DATA_MOVE1 + j);
+ if (move == MOVE_NONE)
+ continue;
+ moveFlags = AI_TypeCalc(move, gBattleMons[battlerIn1].species, gBattleMons[battlerIn1].ability);
+ if (moveFlags & MOVE_RESULT_SUPER_EFFECTIVE && Random() % moduloPercent == 0)
+ {
+ *(gBattleStruct->AI_monToSwitchIntoId + (GetBattlerPosition(gActiveBattler) >> 1)) = i;
+ BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0);
+ return TRUE;
+ }
+ }
+ }
+ }
+ return FALSE;
+}
+
+static bool8 ShouldSwitch(void)
+{
+ u8 battlerIn1, battlerIn2;
+ 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)
+ {
+ battlerIn1 = gActiveBattler;
+ if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(GetBattlerPosition(gActiveBattler) ^ BIT_FLANK)])
+ battlerIn2 = gActiveBattler;
+ else
+ battlerIn2 = GetBattlerAtPosition(GetBattlerPosition(gActiveBattler) ^ BIT_FLANK);
+ }
+ else
+ {
+ battlerIn2 = gActiveBattler;
+ battlerIn1 = gActiveBattler;
+ }
+ for (i = 0; i < PARTY_SIZE; ++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[battlerIn1])
+ continue;
+ if (i == gBattlerPartyIndexes[battlerIn2])
+ continue;
+ if (i == *(gBattleStruct->monToSwitchIntoId + battlerIn1))
+ continue;
+ if (i == *(gBattleStruct->monToSwitchIntoId + battlerIn2))
+ continue;
+ ++availableToSwitch;
+ }
+ if (!availableToSwitch)
+ 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 battlerIn1, battlerIn2;
+
+ if (gBattleTypeFlags & BATTLE_TYPE_TRAINER)
+ {
+ if (ShouldSwitch())
+ {
+ if (*(gBattleStruct->AI_monToSwitchIntoId + (GetBattlerPosition(gActiveBattler) >> 1)) == 6)
+ {
+ s32 monToSwitchId = GetMostSuitableMonToSwitchInto();
+ if (monToSwitchId == 6)
+ {
+ if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
+ {
+ battlerIn1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
+ battlerIn2 = battlerIn1;
+ }
+ else
+ {
+ battlerIn1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
+ battlerIn2 = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
+ }
+
+ for (monToSwitchId = 0; monToSwitchId < PARTY_SIZE; ++monToSwitchId)
+ {
+ if (GetMonData(&gEnemyParty[monToSwitchId], MON_DATA_HP) == 0)
+ continue;
+ if (monToSwitchId == gBattlerPartyIndexes[battlerIn1])
+ continue;
+ if (monToSwitchId == gBattlerPartyIndexes[battlerIn2])
+ continue;
+ if (monToSwitchId == *(gBattleStruct->monToSwitchIntoId + battlerIn1))
+ continue;
+ if (monToSwitchId == *(gBattleStruct->monToSwitchIntoId + battlerIn2))
+ continue;
+ break;
+ }
+ }
+ *(gBattleStruct->AI_monToSwitchIntoId + (GetBattlerPosition(gActiveBattler) >> 1)) = monToSwitchId;
+ }
+ *(gBattleStruct->monToSwitchIntoId + gActiveBattler) = *(gBattleStruct->AI_monToSwitchIntoId + (GetBattlerPosition(gActiveBattler) >> 1));
+ return;
+ }
+ else if (ShouldUseItem())
+ {
+ return;
+ }
+ }
+ BtlController_EmitTwoReturnValues(1, B_ACTION_USE_MOVE, (gActiveBattler ^ BIT_SIDE) << 8);
+}
+
+static void ModulateByTypeEffectiveness(u8 atkType, u8 defType1, u8 defType2, 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) == atkType)
+ {
+ // Check type1.
+ if (TYPE_EFFECT_DEF_TYPE(i) == defType1)
+ *var = (*var * TYPE_EFFECT_MULTIPLIER(i)) / 10;
+ // Check type2.
+ if (TYPE_EFFECT_DEF_TYPE(i) == defType2 && defType1 != defType2)
+ *var = (*var * TYPE_EFFECT_MULTIPLIER(i)) / 10;
+ }
+ i += 3;
+ }
+}
+
+u8 GetMostSuitableMonToSwitchInto(void)
+{
+ u8 opposingBattler;
+ u8 bestDmg; // Note : should be changed to u32 for obvious reasons.
+ u8 bestMonId;
+ u8 battlerIn1, battlerIn2;
+ s32 i, j;
+ u8 invalidMons;
+ u16 move;
+
+ if (*(gBattleStruct->monToSwitchIntoId + gActiveBattler) != PARTY_SIZE)
+ return *(gBattleStruct->monToSwitchIntoId + gActiveBattler);
+ if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
+ {
+ battlerIn1 = gActiveBattler;
+ if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(GetBattlerPosition(gActiveBattler) ^ BIT_FLANK)])
+ battlerIn2 = gActiveBattler;
+ else
+ battlerIn2 = GetBattlerAtPosition(GetBattlerPosition(gActiveBattler) ^ BIT_FLANK);
+ // UB: It considers the opponent only player's side even though it can battle alongside player.
+ opposingBattler = Random() & BIT_FLANK;
+ if (gAbsentBattlerFlags & gBitTable[opposingBattler])
+ opposingBattler ^= BIT_FLANK;
+ }
+ else
+ {
+ opposingBattler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
+ battlerIn1 = gActiveBattler;
+ battlerIn2 = gActiveBattler;
+ }
+ invalidMons = 0;
+ while (invalidMons != 0x3F) // All mons are invalid.
+ {
+ bestDmg = 0;
+ bestMonId = 6;
+ // Find the mon whose type is the most suitable offensively.
+ for (i = 0; i < PARTY_SIZE; ++i)
+ {
+ u16 species = GetMonData(&gEnemyParty[i], MON_DATA_SPECIES);
+ if (species != SPECIES_NONE
+ && GetMonData(&gEnemyParty[i], MON_DATA_HP) != 0
+ && !(gBitTable[i] & invalidMons)
+ && gBattlerPartyIndexes[battlerIn1] != i
+ && gBattlerPartyIndexes[battlerIn2] != i
+ && i != *(gBattleStruct->monToSwitchIntoId + battlerIn1)
+ && i != *(gBattleStruct->monToSwitchIntoId + battlerIn2))
+ {
+ u8 type1 = gBaseStats[species].type1;
+ u8 type2 = gBaseStats[species].type2;
+ u8 typeDmg = 10;
+ ModulateByTypeEffectiveness(gBattleMons[opposingBattler].type1, type1, type2, &typeDmg);
+ ModulateByTypeEffectiveness(gBattleMons[opposingBattler].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 != PARTY_SIZE)
+ {
+ for (i = 0; i < MAX_MON_MOVES; ++i)
+ {
+ move = GetMonData(&gEnemyParty[bestMonId], MON_DATA_MOVE1 + i);
+ if (move != MOVE_NONE && TypeCalc(move, gActiveBattler, opposingBattler) & MOVE_RESULT_SUPER_EFFECTIVE)
+ break;
+ }
+ if (i != MAX_MON_MOVES)
+ 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;
+ 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 < PARTY_SIZE; ++i)
+ {
+ if ((u16)(GetMonData(&gEnemyParty[i], MON_DATA_SPECIES)) == SPECIES_NONE)
+ continue;
+ if (GetMonData(&gEnemyParty[i], MON_DATA_HP) == 0)
+ continue;
+ if (gBattlerPartyIndexes[battlerIn1] == i)
+ continue;
+ if (gBattlerPartyIndexes[battlerIn2] == i)
+ continue;
+ if (i == *(gBattleStruct->monToSwitchIntoId + battlerIn1))
+ continue;
+ if (i == *(gBattleStruct->monToSwitchIntoId + battlerIn2))
+ continue;
+ for (j = 0; j < MAX_MON_MOVES; ++j)
+ {
+ move = GetMonData(&gEnemyParty[i], MON_DATA_MOVE1 + j);
+ gBattleMoveDamage = 0;
+ if (move != MOVE_NONE && gBattleMoves[move].power != 1)
+ {
+ AI_CalcDmg(gActiveBattler, opposingBattler);
+ TypeCalc(move, gActiveBattler, opposingBattler);
+ }
+ if (bestDmg < gBattleMoveDamage)
+ {
+ bestDmg = gBattleMoveDamage;
+ bestMonId = i;
+ }
+ }
+ }
+ return bestMonId;
+}
+
+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;
+ else if (itemEffect[4] & ITEM4_HEAL_HP)
+ return AI_ITEM_HEAL_HP;
+ else if (itemEffect[3] & ITEM3_STATUS_ALL)
+ return AI_ITEM_CURE_CONDITION;
+ else if (itemEffect[0] & (ITEM0_HIGH_CRIT | ITEM0_X_ATTACK) || itemEffect[1] != 0 || itemEffect[2] != 0)
+ return AI_ITEM_X_STAT;
+ else if (itemEffect[3] & ITEM3_MIST)
+ return AI_ITEM_GUARD_SPECS;
+ else
+ return AI_ITEM_NOT_RECOGNIZABLE;
+}
+
+static bool8 ShouldUseItem(void)
+{
+ s32 i;
+ u8 validMons = 0;
+ bool8 shouldUse = FALSE;
+
+ for (i = 0; i < PARTY_SIZE; ++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)
+ ++validMons;
+ for (i = 0; i < MAX_MON_MOVES; ++i)
+ {
+ u16 item;
+ const u8 *itemEffects;
+ u8 paramOffset;
+ u8 battlerSide;
+
+ if (i && validMons > (gBattleResources->battleHistory->itemsNo - i) + 1)
+ continue;
+ item = gBattleResources->battleHistory->trainerItems[i];
+ if (item == ITEM_NONE)
+ continue;
+ if (gItemEffectTable[item - ITEM_POTION] == NULL)
+ continue;
+ if (item == ITEM_ENIGMA_BERRY)
+ itemEffects = gSaveBlock1Ptr->enigmaBerry.itemEffect;
+ else
+ itemEffects = gItemEffectTable[item - ITEM_POTION];
+ *(gBattleStruct->AI_itemType + gActiveBattler / 2) = GetAI_ItemType(item, itemEffects);
+ switch (*(gBattleStruct->AI_itemType + gActiveBattler / 2))
+ {
+ case AI_ITEM_FULL_RESTORE:
+ if (gBattleMons[gActiveBattler].hp >= gBattleMons[gActiveBattler].maxHP / 4)
+ break;
+ if (gBattleMons[gActiveBattler].hp == 0)
+ break;
+ shouldUse = TRUE;
+ break;
+ case AI_ITEM_HEAL_HP:
+ paramOffset = GetItemEffectParamOffset(item, 4, 4);
+ if (paramOffset == 0)
+ break;
+ if (gBattleMons[gActiveBattler].hp == 0)
+ break;
+ if (gBattleMons[gActiveBattler].hp < gBattleMons[gActiveBattler].maxHP / 4 || gBattleMons[gActiveBattler].maxHP - gBattleMons[gActiveBattler].hp > itemEffects[paramOffset])
+ shouldUse = TRUE;
+ break;
+ case AI_ITEM_CURE_CONDITION:
+ *(gBattleStruct->AI_itemFlags + gActiveBattler / 2) = 0;
+ if (itemEffects[3] & ITEM3_SLEEP && gBattleMons[gActiveBattler].status1 & STATUS1_SLEEP)
+ {
+ *(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x20;
+ shouldUse = TRUE;
+ }
+ if (itemEffects[3] & ITEM3_POISON && (gBattleMons[gActiveBattler].status1 & STATUS1_POISON || gBattleMons[gActiveBattler].status1 & STATUS1_TOXIC_POISON))
+ {
+ *(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x10;
+ shouldUse = TRUE;
+ }
+ if (itemEffects[3] & ITEM3_BURN && gBattleMons[gActiveBattler].status1 & STATUS1_BURN)
+ {
+ *(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x8;
+ shouldUse = TRUE;
+ }
+ if (itemEffects[3] & ITEM3_FREEZE && gBattleMons[gActiveBattler].status1 & STATUS1_FREEZE)
+ {
+ *(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x4;
+ shouldUse = TRUE;
+ }
+ if (itemEffects[3] & ITEM3_PARALYSIS && gBattleMons[gActiveBattler].status1 & STATUS1_PARALYSIS)
+ {
+ *(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x2;
+ shouldUse = TRUE;
+ }
+ if (itemEffects[3] & ITEM3_CONFUSION && gBattleMons[gActiveBattler].status2 & STATUS2_CONFUSION)
+ {
+ *(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x1;
+ shouldUse = TRUE;
+ }
+ break;
+ case AI_ITEM_X_STAT:
+ *(gBattleStruct->AI_itemFlags + gActiveBattler / 2) = 0;
+ if (!gDisableStructs[gActiveBattler].isFirstTurn)
+ break;
+ if (itemEffects[0] & ITEM0_X_ATTACK)
+ *(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x1;
+ if (itemEffects[1] & ITEM1_X_DEFEND)
+ *(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x2;
+ if (itemEffects[1] & ITEM1_X_SPEED)
+ *(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x4;
+ if (itemEffects[2] & ITEM2_X_SPATK)
+ *(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x8;
+ if (itemEffects[2] & ITEM2_X_ACCURACY)
+ *(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x20;
+ if (itemEffects[0] & ITEM0_HIGH_CRIT)
+ *(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x80;
+ shouldUse = TRUE;
+ break;
+ case AI_ITEM_GUARD_SPECS:
+ battlerSide = GetBattlerSide(gActiveBattler);
+ if (gDisableStructs[gActiveBattler].isFirstTurn && gSideTimers[battlerSide].mistTimer == 0)
+ shouldUse = TRUE;
+ break;
+ case AI_ITEM_NOT_RECOGNIZABLE:
+ return FALSE;
+ }
+ if (shouldUse)
+ {
+ BtlController_EmitTwoReturnValues(1, B_ACTION_USE_ITEM, 0);
+ *(gBattleStruct->chosenItem + (gActiveBattler / 2) * 2) = item;
+ gBattleResources->battleHistory->trainerItems[i] = 0;
+ return shouldUse;
+ }
+ }
+ return FALSE;
+}