diff options
author | jiangzhengwenjz <jiangzhengwenjzw@qq.com> | 2019-08-03 23:59:41 +0800 |
---|---|---|
committer | jiangzhengwenjz <jiangzhengwenjzw@qq.com> | 2019-08-03 23:59:41 +0800 |
commit | 5568895b043dc37e95be0e99787541d78a7c6fee (patch) | |
tree | 719b85960d6e9c7b8add6a141fa503925290a5c9 /src/battle_ai_switch_items.c | |
parent | 8fed9a5f205cec0d86149a908d64f73316cbb1f4 (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.c | 734 |
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; +} |