diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index e6d99f2eed83..fd55a99f730b 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -168,7 +168,9 @@ bool32 PartnerMoveIsSameNoTarget(u8 battlerAtkPartner, u16 move, u16 partnerMove bool32 ShouldUseWishAromatherapy(u8 battlerAtk, u8 battlerDef, u16 move); // party logic -s32 AI_CalcPartyMonDamage(u16 move, u8 battlerAtk, u8 battlerDef, struct Pokemon *mon); +struct BattlePokemon *AllocSaveBattleMons(void); +void FreeRestoreBattleMons(struct BattlePokemon *savedBattleMons); +s32 AI_CalcPartyMonBestMoveDamage(u32 battlerAtk, u32 battlerDef, struct Pokemon *attackerMon, struct Pokemon *targetMon); s32 CountUsablePartyMons(u8 battlerId); bool32 IsPartyFullyHealedExceptBattler(u8 battler); bool32 PartyHasMoveSplit(u8 battlerId, u8 split); diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index 73d7ecb645a6..d815d3f8bcab 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -28,6 +28,7 @@ static bool8 ShouldUseItem(void); static bool32 AiExpectsToFaintPlayer(void); static bool32 AI_ShouldHeal(u32 healAmount); static bool32 AI_OpponentCanFaintAiWithMod(u32 healAmount); +static bool32 IsAiPartyMonOHKOBy(u32 battlerAtk, struct Pokemon *aiMon); static bool32 IsAceMon(u32 battlerId, u32 monPartyId) { @@ -112,11 +113,7 @@ static bool8 ShouldSwitchIfWonderGuard(void) // 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_SPECIES_OR_EGG) == SPECIES_NONE) - continue; - if (GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) == SPECIES_EGG) + if (!IsValidForBattle(&party[i])) continue; if (i == gBattlerPartyIndexes[gActiveBattler]) continue; @@ -195,13 +192,9 @@ static bool8 FindMonThatAbsorbsOpponentsMove(void) for (i = firstId; i < lastId; i++) { - u16 species; u16 monAbility; - if (GetMonData(&party[i], MON_DATA_HP) == 0) - continue; - species = GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG); - if (species == SPECIES_NONE || species == SPECIES_EGG) + if (!IsValidForBattle(&party[i])) continue; if (i == gBattlerPartyIndexes[battlerIn1]) continue; @@ -215,7 +208,6 @@ static bool8 FindMonThatAbsorbsOpponentsMove(void) continue; monAbility = GetMonAbility(&party[i]); - if (absorbingTypeAbility == monAbility && Random() & 1) { // we found a mon. @@ -290,9 +282,7 @@ static bool8 ShouldSwitchIfGameStatePrompt(void) continue; //Look for mon in party that is able to be switched into and has ability that sets terrain - if (GetMonData(&party[i], MON_DATA_HP) != 0 - && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE - && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG + if (IsValidForBattle(&party[i]) && i != gBattlerPartyIndexes[gActiveBattler] && i != gBattlerPartyIndexes[BATTLE_PARTNER(gActiveBattler)] && IsBattlerGrounded(gActiveBattler) @@ -561,13 +551,9 @@ static bool8 FindMonWithFlagsAndSuperEffective(u16 flags, u8 moduloPercent) for (i = firstId; i < lastId; i++) { - u16 species; - u16 monAbility; + u16 species, monAbility; - if (GetMonData(&party[i], MON_DATA_HP) == 0) - continue; - species = GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG); - if (species == SPECIES_NONE || species == SPECIES_EGG) + if (!IsValidForBattle(&party[i])) continue; if (i == gBattlerPartyIndexes[battlerIn1]) continue; @@ -580,8 +566,8 @@ static bool8 FindMonWithFlagsAndSuperEffective(u16 flags, u8 moduloPercent) if (IsAceMon(gActiveBattler, i)) continue; + species = GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG); monAbility = GetMonAbility(&party[i]); - CalcPartyMonTypeEffectivenessMultiplier(gLastLandedMoves[gActiveBattler], species, monAbility); if (gMoveResultFlags & flags) { @@ -650,11 +636,7 @@ bool32 ShouldSwitch(void) for (i = firstId; i < lastId; i++) { - if (GetMonData(&party[i], MON_DATA_HP) == 0) - continue; - if (GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) == SPECIES_NONE) - continue; - if (GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) == SPECIES_EGG) + if (!IsValidForBattle(&party[i])) continue; if (i == gBattlerPartyIndexes[battlerIn1]) continue; @@ -751,7 +733,7 @@ void AI_TrySwitchOrUseItem(void) for (monToSwitchId = (lastId-1); monToSwitchId >= firstId; monToSwitchId--) { - if (GetMonData(&party[monToSwitchId], MON_DATA_HP) == 0) + if (!IsValidForBattle(&party[monToSwitchId])) continue; if (monToSwitchId == gBattlerPartyIndexes[battlerIn1]) continue; @@ -785,7 +767,7 @@ void AI_TrySwitchOrUseItem(void) // If there are two(or more) mons to choose from, always choose one that has baton pass // as most often it can't do much on its own. -static u32 GetBestMonBatonPass(struct Pokemon *party, int firstId, int lastId, u8 invalidMons, int aliveCount) +static u32 GetBestMonBatonPass(struct Pokemon *party, int firstId, int lastId, u8 invalidMons, int aliveCount, u32 opposingBattler) { int i, j, bits = 0; @@ -793,6 +775,8 @@ static u32 GetBestMonBatonPass(struct Pokemon *party, int firstId, int lastId, u { if (invalidMons & gBitTable[i]) continue; + if (IsAiPartyMonOHKOBy(opposingBattler, &party[i])) + continue; for (j = 0; j < MAX_MON_MOVES; j++) { @@ -837,6 +821,9 @@ static u32 GetBestMonTypeMatchup(struct Pokemon *party, int firstId, int lastId, u8 defType1 = gSpeciesInfo[species].types[0]; u8 defType2 = gSpeciesInfo[species].types[1]; + if (IsAiPartyMonOHKOBy(opposingBattler, &party[i])) + continue; + typeEffectiveness = uq4_12_multiply(typeEffectiveness, (GetTypeModifier(atkType1, defType1))); if (atkType2 != atkType1) typeEffectiveness = uq4_12_multiply(typeEffectiveness, (GetTypeModifier(atkType2, defType1))); @@ -881,7 +868,7 @@ static u32 GetBestMonTypeMatchup(struct Pokemon *party, int firstId, int lastId, static u32 GetBestMonDmg(struct Pokemon *party, int firstId, int lastId, u8 invalidMons, u32 opposingBattler) { int i, j; - int bestDmg = 0; + int dmg, bestDmg = 0; int bestMonId = PARTY_SIZE; gMoveResultFlags = 0; @@ -890,19 +877,14 @@ static u32 GetBestMonDmg(struct Pokemon *party, int firstId, int lastId, u8 inva { if (gBitTable[i] & invalidMons) continue; + if (IsAiPartyMonOHKOBy(opposingBattler, &party[i])) + continue; - for (j = 0; j < MAX_MON_MOVES; j++) + dmg = AI_CalcPartyMonBestMoveDamage(gActiveBattler, opposingBattler, &party[i], NULL); + if (bestDmg < dmg) { - u32 move = GetMonData(&party[i], MON_DATA_MOVE1 + j); - if (move != MOVE_NONE && gBattleMoves[move].power != 0) - { - s32 dmg = AI_CalcPartyMonDamage(move, gActiveBattler, opposingBattler, &party[i]); - if (bestDmg < dmg) - { - bestDmg = dmg; - bestMonId = i; - } - } + bestDmg = dmg; + bestMonId = i; } } @@ -912,7 +894,7 @@ static u32 GetBestMonDmg(struct Pokemon *party, int firstId, int lastId, u8 inva u8 GetMostSuitableMonToSwitchInto(void) { u32 opposingBattler = 0; - u32 bestMonId = 0; + u32 bestMonId = PARTY_SIZE; u8 battlerIn1 = 0, battlerIn2 = 0; s32 firstId = 0; s32 lastId = 0; // + 1 @@ -954,10 +936,7 @@ u8 GetMostSuitableMonToSwitchInto(void) // Get invalid slots ids. for (i = firstId; i < lastId; i++) { - u16 species = GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG); - if (species == SPECIES_NONE - || species == SPECIES_EGG - || GetMonData(&party[i], MON_DATA_HP) == 0 + if (!IsValidForBattle(&party[i]) || gBattlerPartyIndexes[battlerIn1] == i || gBattlerPartyIndexes[battlerIn2] == i || i == *(gBattleStruct->monToSwitchIntoId + battlerIn1) @@ -977,7 +956,7 @@ u8 GetMostSuitableMonToSwitchInto(void) } } - bestMonId = GetBestMonBatonPass(party, firstId, lastId, invalidMons, aliveCount); + bestMonId = GetBestMonBatonPass(party, firstId, lastId, invalidMons, aliveCount, opposingBattler); if (bestMonId != PARTY_SIZE) return bestMonId; @@ -1040,9 +1019,7 @@ static bool8 ShouldUseItem(void) for (i = 0; i < PARTY_SIZE; i++) { - if (GetMonData(&party[i], MON_DATA_HP) != 0 - && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE - && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG) + if (IsValidForBattle(&party[i])) { validMons++; } @@ -1155,3 +1132,29 @@ static bool32 AI_OpponentCanFaintAiWithMod(u32 healAmount) } return FALSE; } + +static bool32 IsAiPartyMonOHKOBy(u32 battlerAtk, struct Pokemon *aiMon) +{ + bool32 ret = FALSE; + struct BattlePokemon *savedBattleMons; + s32 hp = GetMonData(aiMon, MON_DATA_HP); + s32 bestDmg = AI_CalcPartyMonBestMoveDamage(battlerAtk, gActiveBattler, NULL, aiMon); + + switch (GetNoOfHitsToKO(bestDmg, hp)) + { + case 1: + ret = TRUE; + break; + case 2: // if AI mon is faster allow 2 turns + savedBattleMons = AllocSaveBattleMons(); + PokemonToBattleMon(aiMon, &gBattleMons[gActiveBattler]); + if (AI_WhoStrikesFirst(gActiveBattler, battlerAtk, 0) == AI_IS_SLOWER) + ret = TRUE; + else + ret = FALSE; + FreeRestoreBattleMons(savedBattleMons); + break; + } + + return ret; +} diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index c18209314bbf..66ef2f1330c3 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -3369,25 +3369,49 @@ bool32 ShouldUseWishAromatherapy(u8 battlerAtk, u8 battlerDef, u16 move) return FALSE; } +#define SIZE_G_BATTLE_MONS (sizeof(struct BattlePokemon) * MAX_BATTLERS_COUNT) + +struct BattlePokemon *AllocSaveBattleMons(void) +{ + struct BattlePokemon *savedBattleMons = Alloc(SIZE_G_BATTLE_MONS); + memcpy(savedBattleMons, gBattleMons, SIZE_G_BATTLE_MONS); + return savedBattleMons; +} + +void FreeRestoreBattleMons(struct BattlePokemon *savedBattleMons) +{ + memcpy(gBattleMons, savedBattleMons, SIZE_G_BATTLE_MONS); + Free(savedBattleMons); +} + // party logic -s32 AI_CalcPartyMonDamage(u16 move, u8 battlerAtk, u8 battlerDef, struct Pokemon *mon) +s32 AI_CalcPartyMonBestMoveDamage(u32 battlerAtk, u32 battlerDef, struct Pokemon *attackerMon, struct Pokemon *targetMon) { - s32 dmg; - u32 i; + s32 i, move, bestDmg, dmg; u8 effectiveness; - struct BattlePokemon *battleMons = Alloc(sizeof(struct BattlePokemon) * MAX_BATTLERS_COUNT); - - for (i = 0; i < MAX_BATTLERS_COUNT; i++) - battleMons[i] = gBattleMons[i]; + struct BattlePokemon *savedBattleMons = AllocSaveBattleMons(); - PokemonToBattleMon(mon, &gBattleMons[battlerAtk]); - dmg = AI_CalcDamage(move, battlerAtk, battlerDef, &effectiveness, FALSE); + if (attackerMon != NULL) + PokemonToBattleMon(attackerMon, &gBattleMons[battlerAtk]); + if (targetMon != NULL) + PokemonToBattleMon(targetMon, &gBattleMons[battlerDef]); - for (i = 0; i < MAX_BATTLERS_COUNT; i++) - gBattleMons[i] = battleMons[i]; + for (bestDmg = 0, i = 0; i < MAX_MON_MOVES; i++) + { + if (BattlerHasAi(battlerAtk)) + move = GetMonData(attackerMon, MON_DATA_MOVE1 + i); + else + move = AI_PARTY->mons[GET_BATTLER_SIDE2(battlerAtk)][gBattlerPartyIndexes[battlerAtk]].moves[i]; - Free(battleMons); + if (move != MOVE_NONE && gBattleMoves[move].power != 0) + { + dmg = AI_CalcDamage(move, battlerAtk, battlerDef, &effectiveness, FALSE); + if (dmg > bestDmg) + bestDmg = dmg; + } + } + FreeRestoreBattleMons(savedBattleMons); return dmg; } diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index f316f19ea879..a786dafa6962 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -1688,7 +1688,6 @@ static void OpponentHandleChoosePokemon(void) else if (*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) == PARTY_SIZE) { chosenMonId = GetMostSuitableMonToSwitchInto(); - if (chosenMonId == PARTY_SIZE) { s32 battler1, battler2, firstId, lastId; @@ -1702,14 +1701,13 @@ static void OpponentHandleChoosePokemon(void) battler1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); battler2 = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); pokemonInBattle = 2; - } GetAIPartyIndexes(gActiveBattler, &firstId, &lastId); for (chosenMonId = (lastId-1); chosenMonId >= firstId; chosenMonId--) { - if (GetMonData(&gEnemyParty[chosenMonId], MON_DATA_HP) != 0 + if (IsValidForBattle(&gEnemyParty[chosenMonId]) && chosenMonId != gBattlerPartyIndexes[battler1] && chosenMonId != gBattlerPartyIndexes[battler2] && (!(AI_THINKING_STRUCT->aiFlags & AI_FLAG_ACE_POKEMON)