mirror of
https://github.com/Ninjdai1/pokeemerald.git
synced 2025-03-13 17:22:25 +01:00
Recommit to Dev branch
This commit is contained in:
parent
7825153bb2
commit
cc4a9b9f4b
@ -33,7 +33,7 @@ enum {
|
||||
|
||||
void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId);
|
||||
void AI_TrySwitchOrUseItem(void);
|
||||
u8 GetMostSuitableMonToSwitchInto(void);
|
||||
u8 GetMostSuitableMonToSwitchInto(bool8);
|
||||
bool32 ShouldSwitch(void);
|
||||
|
||||
#endif // GUARD_BATTLE_AI_SWITCH_ITEMS_H
|
||||
|
@ -356,7 +356,7 @@ static u8 ChooseMoveOrAction_Singles(void)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == MAX_MON_MOVES && GetMostSuitableMonToSwitchInto() != PARTY_SIZE)
|
||||
if (i == MAX_MON_MOVES && GetMostSuitableMonToSwitchInto(TRUE) != PARTY_SIZE)
|
||||
{
|
||||
AI_THINKING_STRUCT->switchMon = TRUE;
|
||||
return AI_CHOICE_SWITCH;
|
||||
@ -370,7 +370,7 @@ static u8 ChooseMoveOrAction_Singles(void)
|
||||
&& gDisableStructs[sBattler_AI].truantCounter
|
||||
&& gBattleMons[sBattler_AI].hp >= gBattleMons[sBattler_AI].maxHP / 2)
|
||||
{
|
||||
if (GetMostSuitableMonToSwitchInto() != PARTY_SIZE)
|
||||
if (GetMostSuitableMonToSwitchInto(TRUE) != PARTY_SIZE)
|
||||
{
|
||||
AI_THINKING_STRUCT->switchMon = TRUE;
|
||||
return AI_CHOICE_SWITCH;
|
||||
|
@ -26,6 +26,7 @@ static bool8 ShouldUseItem(void);
|
||||
static bool32 AiExpectsToFaintPlayer(void);
|
||||
static bool32 AI_ShouldHeal(u32 healAmount);
|
||||
static bool32 AI_OpponentCanFaintAiWithMod(u32 healAmount);
|
||||
static bool32 AI_CheckSurvivabilty(bool8 checkSurvivability, int playerPokemon, int aiPokemon);
|
||||
|
||||
void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId)
|
||||
{
|
||||
@ -414,12 +415,12 @@ static bool8 ShouldSwitchIfAbilityBenefit(void)
|
||||
moduloChance = 4; //25%
|
||||
//Attempt to cure bad ailment
|
||||
if (gBattleMons[gActiveBattler].status1 & (STATUS1_SLEEP | STATUS1_FREEZE | STATUS1_TOXIC_POISON)
|
||||
&& GetMostSuitableMonToSwitchInto() != PARTY_SIZE)
|
||||
&& GetMostSuitableMonToSwitchInto(TRUE) != PARTY_SIZE)
|
||||
break;
|
||||
//Attempt to cure lesser ailment
|
||||
if ((gBattleMons[gActiveBattler].status1 & STATUS1_ANY)
|
||||
&& (gBattleMons[gActiveBattler].hp >= gBattleMons[gActiveBattler].maxHP / 2)
|
||||
&& GetMostSuitableMonToSwitchInto() != PARTY_SIZE
|
||||
&& GetMostSuitableMonToSwitchInto(TRUE) != PARTY_SIZE
|
||||
&& Random() % (moduloChance*chanceReducer) == 0)
|
||||
break;
|
||||
|
||||
@ -431,7 +432,7 @@ static bool8 ShouldSwitchIfAbilityBenefit(void)
|
||||
if (gBattleMons[gActiveBattler].status1 & STATUS1_ANY)
|
||||
return FALSE;
|
||||
if ((gBattleMons[gActiveBattler].hp <= ((gBattleMons[gActiveBattler].maxHP * 2) / 3))
|
||||
&& GetMostSuitableMonToSwitchInto() != PARTY_SIZE
|
||||
&& GetMostSuitableMonToSwitchInto(TRUE) != PARTY_SIZE
|
||||
&& Random() % (moduloChance*chanceReducer) == 0)
|
||||
break;
|
||||
|
||||
@ -726,7 +727,7 @@ void AI_TrySwitchOrUseItem(void)
|
||||
{
|
||||
if (*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) == PARTY_SIZE)
|
||||
{
|
||||
s32 monToSwitchId = GetMostSuitableMonToSwitchInto();
|
||||
s32 monToSwitchId = GetMostSuitableMonToSwitchInto(TRUE);
|
||||
if (monToSwitchId == PARTY_SIZE)
|
||||
{
|
||||
if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
|
||||
@ -746,6 +747,8 @@ void AI_TrySwitchOrUseItem(void)
|
||||
{
|
||||
if (GetMonData(&party[monToSwitchId], MON_DATA_HP) == 0)
|
||||
continue;
|
||||
if (GetMonData(&party[monToSwitchId], MON_DATA_SPECIES2) == SPECIES_NONE)
|
||||
continue;
|
||||
if (monToSwitchId == gBattlerPartyIndexes[battlerIn1])
|
||||
continue;
|
||||
if (monToSwitchId == gBattlerPartyIndexes[battlerIn2])
|
||||
@ -779,7 +782,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, bool8 checkSurvivability)
|
||||
{
|
||||
int i, j, bits = 0;
|
||||
|
||||
@ -788,6 +791,9 @@ static u32 GetBestMonBatonPass(struct Pokemon *party, int firstId, int lastId, u
|
||||
if (invalidMons & gBitTable[i])
|
||||
continue;
|
||||
|
||||
if (AI_CheckSurvivabilty(checkSurvivability, BATTLE_OPPOSITE(gActiveBattler), i))
|
||||
continue;
|
||||
|
||||
for (j = 0; j < MAX_MON_MOVES; j++)
|
||||
{
|
||||
if (GetMonData(&party[i], MON_DATA_MOVE1 + j, NULL) == MOVE_BATON_PASS)
|
||||
@ -810,69 +816,71 @@ static u32 GetBestMonBatonPass(struct Pokemon *party, int firstId, int lastId, u
|
||||
return PARTY_SIZE;
|
||||
}
|
||||
|
||||
static u32 GetBestMonTypeMatchup(struct Pokemon *party, int firstId, int lastId, u8 invalidMons, u32 opposingBattler)
|
||||
static u32 GetBestMonTypeMatchup(struct Pokemon *party, int firstId, int lastId, u8 invalidMons, u32 opposingBattler, bool8 checkSurvivability)
|
||||
{
|
||||
int i, bits = 0;
|
||||
|
||||
while (bits != 0x3F) // All mons were checked.
|
||||
int i, j = 0;
|
||||
u32 bestResist = UQ_4_12(1.0);
|
||||
int bestMonId = PARTY_SIZE;
|
||||
// Find the mon whose type is the most suitable defensively.
|
||||
for (i = firstId; i < lastId; i++)
|
||||
{
|
||||
u32 bestResist = UQ_4_12(1.0);
|
||||
int bestMonId = PARTY_SIZE;
|
||||
// Find the mon whose type is the most suitable defensively.
|
||||
for (i = firstId; i < lastId; i++)
|
||||
u16 species = GetMonData(&party[i], MON_DATA_SPECIES);
|
||||
u32 typeEffectiveness = UQ_4_12(1.0);
|
||||
|
||||
u8 atkType1 = gBattleMons[opposingBattler].type1;
|
||||
u8 atkType2 = gBattleMons[opposingBattler].type2;
|
||||
u8 defType1 = gBaseStats[species].type1;
|
||||
u8 defType2 = gBaseStats[species].type2;
|
||||
|
||||
if (gBitTable[i] & invalidMons)
|
||||
continue;
|
||||
if (AI_CheckSurvivabilty(checkSurvivability, BATTLE_OPPOSITE(gActiveBattler), i))
|
||||
continue;
|
||||
|
||||
typeEffectiveness *= GetTypeModifier(atkType1, defType1);
|
||||
if (atkType2 != atkType1)
|
||||
typeEffectiveness *= GetTypeModifier(atkType2, defType1);
|
||||
if (defType2 != defType1)
|
||||
{
|
||||
if (!(gBitTable[i] & invalidMons) && !(gBitTable[i] & bits))
|
||||
{
|
||||
u16 species = GetMonData(&party[i], MON_DATA_SPECIES);
|
||||
u32 typeEffectiveness = UQ_4_12(1.0);
|
||||
|
||||
u8 atkType1 = gBattleMons[opposingBattler].type1;
|
||||
u8 atkType2 = gBattleMons[opposingBattler].type2;
|
||||
u8 defType1 = gBaseStats[species].type1;
|
||||
u8 defType2 = gBaseStats[species].type2;
|
||||
|
||||
typeEffectiveness *= GetTypeModifier(atkType1, defType1);
|
||||
if (atkType2 != atkType1)
|
||||
typeEffectiveness *= GetTypeModifier(atkType2, defType1);
|
||||
if (defType2 != defType1)
|
||||
{
|
||||
typeEffectiveness *= GetTypeModifier(atkType1, defType2);
|
||||
if (atkType2 != atkType1)
|
||||
typeEffectiveness *= GetTypeModifier(atkType2, defType2);
|
||||
}
|
||||
if (typeEffectiveness < bestResist)
|
||||
{
|
||||
bestResist = typeEffectiveness;
|
||||
bestMonId = i;
|
||||
}
|
||||
}
|
||||
typeEffectiveness *= GetTypeModifier(atkType1, defType2);
|
||||
if (atkType2 != atkType1)
|
||||
typeEffectiveness *= GetTypeModifier(atkType2, defType2);
|
||||
}
|
||||
|
||||
// Ok, we know the mon has the right typing but does it have at least one super effective move?
|
||||
if (bestMonId != PARTY_SIZE)
|
||||
if ((typeEffectiveness < bestResist)
|
||||
|| ((typeEffectiveness <= bestResist) && !checkSurvivability)) //Fine with a nuetral matchup on second time through
|
||||
{
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
u32 move = GetMonData(&party[bestMonId], MON_DATA_MOVE1 + i);
|
||||
if (move != MOVE_NONE && AI_GetTypeEffectiveness(move, gActiveBattler, opposingBattler) >= UQ_4_12(2.0))
|
||||
break;
|
||||
}
|
||||
|
||||
if (i != MAX_MON_MOVES)
|
||||
return bestMonId; // Has both the typing and at least one super effective move.
|
||||
|
||||
bits |= gBitTable[bestMonId]; // Sorry buddy, we want something better.
|
||||
}
|
||||
else
|
||||
{
|
||||
bits = 0x3F; // No viable mon to switch.
|
||||
bestResist = typeEffectiveness;
|
||||
bestMonId = i;
|
||||
}
|
||||
}
|
||||
|
||||
return PARTY_SIZE;
|
||||
// Ok, we don't have anything that type resists. But do we at least have something with a super effective move?
|
||||
if (bestMonId == PARTY_SIZE)
|
||||
{
|
||||
// Find the mon that has an attack most suited offensively
|
||||
for (i = firstId; i < lastId; i++)
|
||||
{
|
||||
if (gBitTable[i] & invalidMons)
|
||||
continue;
|
||||
if (AI_CheckSurvivabilty(checkSurvivability, BATTLE_OPPOSITE(gActiveBattler), i))
|
||||
continue;
|
||||
|
||||
for (j = 0; j < MAX_MON_MOVES; j++)
|
||||
{
|
||||
u32 move = GetMonData(&party[i], MON_DATA_MOVE1 + j);
|
||||
if (move != MOVE_NONE && AI_GetTypeEffectiveness(move, gActiveBattler, opposingBattler) >= UQ_4_12(2.0))
|
||||
{
|
||||
bestMonId = i; // Has at least one super effective move.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bestMonId;
|
||||
}
|
||||
|
||||
static u32 GetBestMonDmg(struct Pokemon *party, int firstId, int lastId, u8 invalidMons, u32 opposingBattler)
|
||||
static u32 GetBestMonDmg(struct Pokemon *party, int firstId, int lastId, u8 invalidMons, u32 opposingBattler, bool8 checkSurvivability)
|
||||
{
|
||||
int i, j;
|
||||
int bestDmg = 0;
|
||||
@ -884,6 +892,8 @@ static u32 GetBestMonDmg(struct Pokemon *party, int firstId, int lastId, u8 inva
|
||||
{
|
||||
if (gBitTable[i] & invalidMons)
|
||||
continue;
|
||||
if (AI_CheckSurvivabilty(checkSurvivability, BATTLE_OPPOSITE(gActiveBattler), i))
|
||||
continue;
|
||||
|
||||
for (j = 0; j < MAX_MON_MOVES; j++)
|
||||
{
|
||||
@ -903,10 +913,10 @@ static u32 GetBestMonDmg(struct Pokemon *party, int firstId, int lastId, u8 inva
|
||||
return bestMonId;
|
||||
}
|
||||
|
||||
u8 GetMostSuitableMonToSwitchInto(void)
|
||||
u8 GetMostSuitableMonToSwitchInto(bool8 checkSurvivability)
|
||||
{
|
||||
u32 opposingBattler = 0;
|
||||
u32 bestMonId = 0;
|
||||
u32 bestMonId = PARTY_SIZE;
|
||||
u8 battlerIn1 = 0, battlerIn2 = 0;
|
||||
s32 firstId = 0;
|
||||
s32 lastId = 0; // + 1
|
||||
@ -948,7 +958,7 @@ u8 GetMostSuitableMonToSwitchInto(void)
|
||||
// Get invalid slots ids.
|
||||
for (i = firstId; i < lastId; i++)
|
||||
{
|
||||
if (GetMonData(&party[i], MON_DATA_SPECIES) == SPECIES_NONE
|
||||
if (GetMonData(&party[i], MON_DATA_SPECIES2) == SPECIES_NONE
|
||||
|| GetMonData(&party[i], MON_DATA_HP) == 0
|
||||
|| gBattlerPartyIndexes[battlerIn1] == i
|
||||
|| gBattlerPartyIndexes[battlerIn2] == i
|
||||
@ -962,19 +972,26 @@ u8 GetMostSuitableMonToSwitchInto(void)
|
||||
aliveCount++;
|
||||
}
|
||||
|
||||
bestMonId = GetBestMonBatonPass(party, firstId, lastId, invalidMons, aliveCount);
|
||||
bestMonId = GetBestMonBatonPass(party, firstId, lastId, invalidMons, aliveCount, checkSurvivability);
|
||||
if (bestMonId != PARTY_SIZE)
|
||||
return bestMonId;
|
||||
|
||||
bestMonId = GetBestMonTypeMatchup(party, firstId, lastId, invalidMons, opposingBattler);
|
||||
bestMonId = GetBestMonTypeMatchup(party, firstId, lastId, invalidMons, opposingBattler, checkSurvivability);
|
||||
if (bestMonId != PARTY_SIZE)
|
||||
return bestMonId;
|
||||
|
||||
bestMonId = GetBestMonDmg(party, firstId, lastId, invalidMons, opposingBattler);
|
||||
bestMonId = GetBestMonDmg(party, firstId, lastId, invalidMons, opposingBattler, checkSurvivability);
|
||||
if (bestMonId != PARTY_SIZE)
|
||||
return bestMonId;
|
||||
|
||||
return PARTY_SIZE;
|
||||
//Didn't find any good options first time around. Try again without checking survivabilty, better than a random mon
|
||||
if (checkSurvivability && bestMonId == PARTY_SIZE)
|
||||
{
|
||||
bestMonId = GetMostSuitableMonToSwitchInto(FALSE);
|
||||
return bestMonId;
|
||||
}
|
||||
|
||||
return bestMonId;
|
||||
}
|
||||
|
||||
static u8 GetAI_ItemType(u16 itemId, const u8 *itemEffect)
|
||||
@ -1178,3 +1195,24 @@ static bool32 AI_OpponentCanFaintAiWithMod(u32 healAmount)
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static bool32 AI_CheckSurvivabilty(bool8 checkSurvivability, int playerPokemon, int aiPokemon)
|
||||
{
|
||||
if (!checkSurvivability)
|
||||
return FALSE;
|
||||
|
||||
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_SMART_SWITCHING)
|
||||
{
|
||||
//Opponent can OHKO AI, don't send this Pokemon out
|
||||
if (CanTargetFaintAiWithMod(playerPokemon, aiPokemon, 0, 0))
|
||||
return TRUE;
|
||||
|
||||
//Opponent can 2HKO AI and AI cannot strike first
|
||||
//ToDo: Modify for switches when AI has already attacked (Volt Switch etc.)
|
||||
if (CanTargetFaintAiWithMod(playerPokemon, aiPokemon, 0, 2)
|
||||
&& GetWhoStrikesFirst(playerPokemon, aiPokemon, TRUE) == 0) //Player strikes first
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
@ -1676,8 +1676,7 @@ static void OpponentHandleChoosePokemon(void)
|
||||
|
||||
if (*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) == PARTY_SIZE)
|
||||
{
|
||||
chosenMonId = GetMostSuitableMonToSwitchInto();
|
||||
|
||||
chosenMonId = GetMostSuitableMonToSwitchInto(TRUE);
|
||||
if (chosenMonId == PARTY_SIZE)
|
||||
{
|
||||
s32 battler1, battler2, firstId, lastId;
|
||||
@ -1691,7 +1690,6 @@ static void OpponentHandleChoosePokemon(void)
|
||||
battler1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
|
||||
battler2 = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
|
||||
pokemonInBattle = 2;
|
||||
|
||||
}
|
||||
|
||||
GetAIPartyIndexes(gActiveBattler, &firstId, &lastId);
|
||||
@ -1699,6 +1697,7 @@ static void OpponentHandleChoosePokemon(void)
|
||||
for (chosenMonId = (lastId-1); chosenMonId >= firstId; chosenMonId--)
|
||||
{
|
||||
if (GetMonData(&gEnemyParty[chosenMonId], MON_DATA_HP) != 0
|
||||
&& GetMonData(&gEnemyParty[chosenMonId], MON_DATA_SPECIES2) != SPECIES_NONE
|
||||
&& chosenMonId != gBattlerPartyIndexes[battler1]
|
||||
&& chosenMonId != gBattlerPartyIndexes[battler2]
|
||||
&& (AI_THINKING_STRUCT->aiFlags & AI_FLAG_ACE_POKEMON
|
||||
|
@ -1548,7 +1548,7 @@ static void PlayerPartnerHandleChooseItem(void)
|
||||
|
||||
static void PlayerPartnerHandleChoosePokemon(void)
|
||||
{
|
||||
s32 chosenMonId = GetMostSuitableMonToSwitchInto();
|
||||
s32 chosenMonId = GetMostSuitableMonToSwitchInto(TRUE);
|
||||
|
||||
if (chosenMonId == 6) // just switch to the next mon
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user