Recommit to Dev branch

This commit is contained in:
Ct11217 2022-09-15 22:57:29 -06:00
parent 7825153bb2
commit cc4a9b9f4b
5 changed files with 109 additions and 72 deletions

View File

@ -33,7 +33,7 @@ enum {
void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId); void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId);
void AI_TrySwitchOrUseItem(void); void AI_TrySwitchOrUseItem(void);
u8 GetMostSuitableMonToSwitchInto(void); u8 GetMostSuitableMonToSwitchInto(bool8);
bool32 ShouldSwitch(void); bool32 ShouldSwitch(void);
#endif // GUARD_BATTLE_AI_SWITCH_ITEMS_H #endif // GUARD_BATTLE_AI_SWITCH_ITEMS_H

View File

@ -356,7 +356,7 @@ static u8 ChooseMoveOrAction_Singles(void)
break; break;
} }
if (i == MAX_MON_MOVES && GetMostSuitableMonToSwitchInto() != PARTY_SIZE) if (i == MAX_MON_MOVES && GetMostSuitableMonToSwitchInto(TRUE) != PARTY_SIZE)
{ {
AI_THINKING_STRUCT->switchMon = TRUE; AI_THINKING_STRUCT->switchMon = TRUE;
return AI_CHOICE_SWITCH; return AI_CHOICE_SWITCH;
@ -370,7 +370,7 @@ static u8 ChooseMoveOrAction_Singles(void)
&& gDisableStructs[sBattler_AI].truantCounter && gDisableStructs[sBattler_AI].truantCounter
&& gBattleMons[sBattler_AI].hp >= gBattleMons[sBattler_AI].maxHP / 2) && gBattleMons[sBattler_AI].hp >= gBattleMons[sBattler_AI].maxHP / 2)
{ {
if (GetMostSuitableMonToSwitchInto() != PARTY_SIZE) if (GetMostSuitableMonToSwitchInto(TRUE) != PARTY_SIZE)
{ {
AI_THINKING_STRUCT->switchMon = TRUE; AI_THINKING_STRUCT->switchMon = TRUE;
return AI_CHOICE_SWITCH; return AI_CHOICE_SWITCH;

View File

@ -26,6 +26,7 @@ static bool8 ShouldUseItem(void);
static bool32 AiExpectsToFaintPlayer(void); static bool32 AiExpectsToFaintPlayer(void);
static bool32 AI_ShouldHeal(u32 healAmount); static bool32 AI_ShouldHeal(u32 healAmount);
static bool32 AI_OpponentCanFaintAiWithMod(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) void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId)
{ {
@ -414,12 +415,12 @@ static bool8 ShouldSwitchIfAbilityBenefit(void)
moduloChance = 4; //25% moduloChance = 4; //25%
//Attempt to cure bad ailment //Attempt to cure bad ailment
if (gBattleMons[gActiveBattler].status1 & (STATUS1_SLEEP | STATUS1_FREEZE | STATUS1_TOXIC_POISON) if (gBattleMons[gActiveBattler].status1 & (STATUS1_SLEEP | STATUS1_FREEZE | STATUS1_TOXIC_POISON)
&& GetMostSuitableMonToSwitchInto() != PARTY_SIZE) && GetMostSuitableMonToSwitchInto(TRUE) != PARTY_SIZE)
break; break;
//Attempt to cure lesser ailment //Attempt to cure lesser ailment
if ((gBattleMons[gActiveBattler].status1 & STATUS1_ANY) if ((gBattleMons[gActiveBattler].status1 & STATUS1_ANY)
&& (gBattleMons[gActiveBattler].hp >= gBattleMons[gActiveBattler].maxHP / 2) && (gBattleMons[gActiveBattler].hp >= gBattleMons[gActiveBattler].maxHP / 2)
&& GetMostSuitableMonToSwitchInto() != PARTY_SIZE && GetMostSuitableMonToSwitchInto(TRUE) != PARTY_SIZE
&& Random() % (moduloChance*chanceReducer) == 0) && Random() % (moduloChance*chanceReducer) == 0)
break; break;
@ -431,7 +432,7 @@ static bool8 ShouldSwitchIfAbilityBenefit(void)
if (gBattleMons[gActiveBattler].status1 & STATUS1_ANY) if (gBattleMons[gActiveBattler].status1 & STATUS1_ANY)
return FALSE; return FALSE;
if ((gBattleMons[gActiveBattler].hp <= ((gBattleMons[gActiveBattler].maxHP * 2) / 3)) if ((gBattleMons[gActiveBattler].hp <= ((gBattleMons[gActiveBattler].maxHP * 2) / 3))
&& GetMostSuitableMonToSwitchInto() != PARTY_SIZE && GetMostSuitableMonToSwitchInto(TRUE) != PARTY_SIZE
&& Random() % (moduloChance*chanceReducer) == 0) && Random() % (moduloChance*chanceReducer) == 0)
break; break;
@ -726,7 +727,7 @@ void AI_TrySwitchOrUseItem(void)
{ {
if (*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) == PARTY_SIZE) if (*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) == PARTY_SIZE)
{ {
s32 monToSwitchId = GetMostSuitableMonToSwitchInto(); s32 monToSwitchId = GetMostSuitableMonToSwitchInto(TRUE);
if (monToSwitchId == PARTY_SIZE) if (monToSwitchId == PARTY_SIZE)
{ {
if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
@ -746,6 +747,8 @@ void AI_TrySwitchOrUseItem(void)
{ {
if (GetMonData(&party[monToSwitchId], MON_DATA_HP) == 0) if (GetMonData(&party[monToSwitchId], MON_DATA_HP) == 0)
continue; continue;
if (GetMonData(&party[monToSwitchId], MON_DATA_SPECIES2) == SPECIES_NONE)
continue;
if (monToSwitchId == gBattlerPartyIndexes[battlerIn1]) if (monToSwitchId == gBattlerPartyIndexes[battlerIn1])
continue; continue;
if (monToSwitchId == gBattlerPartyIndexes[battlerIn2]) 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 // 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. // 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; int i, j, bits = 0;
@ -788,6 +791,9 @@ static u32 GetBestMonBatonPass(struct Pokemon *party, int firstId, int lastId, u
if (invalidMons & gBitTable[i]) if (invalidMons & gBitTable[i])
continue; continue;
if (AI_CheckSurvivabilty(checkSurvivability, BATTLE_OPPOSITE(gActiveBattler), i))
continue;
for (j = 0; j < MAX_MON_MOVES; j++) for (j = 0; j < MAX_MON_MOVES; j++)
{ {
if (GetMonData(&party[i], MON_DATA_MOVE1 + j, NULL) == MOVE_BATON_PASS) 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; 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; int i, j = 0;
u32 bestResist = UQ_4_12(1.0);
while (bits != 0x3F) // All mons were checked. 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); u16 species = GetMonData(&party[i], MON_DATA_SPECIES);
int bestMonId = PARTY_SIZE; u32 typeEffectiveness = UQ_4_12(1.0);
// Find the mon whose type is the most suitable defensively.
for (i = firstId; i < lastId; i++) 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)) typeEffectiveness *= GetTypeModifier(atkType1, defType2);
{ if (atkType2 != atkType1)
u16 species = GetMonData(&party[i], MON_DATA_SPECIES); typeEffectiveness *= GetTypeModifier(atkType2, defType2);
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;
}
}
} }
if ((typeEffectiveness < bestResist)
// Ok, we know the mon has the right typing but does it have at least one super effective move? || ((typeEffectiveness <= bestResist) && !checkSurvivability)) //Fine with a nuetral matchup on second time through
if (bestMonId != PARTY_SIZE)
{ {
for (i = 0; i < MAX_MON_MOVES; i++) bestResist = typeEffectiveness;
{ bestMonId = 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.
} }
} }
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 i, j;
int bestDmg = 0; int bestDmg = 0;
@ -884,6 +892,8 @@ static u32 GetBestMonDmg(struct Pokemon *party, int firstId, int lastId, u8 inva
{ {
if (gBitTable[i] & invalidMons) if (gBitTable[i] & invalidMons)
continue; continue;
if (AI_CheckSurvivabilty(checkSurvivability, BATTLE_OPPOSITE(gActiveBattler), i))
continue;
for (j = 0; j < MAX_MON_MOVES; j++) 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; return bestMonId;
} }
u8 GetMostSuitableMonToSwitchInto(void) u8 GetMostSuitableMonToSwitchInto(bool8 checkSurvivability)
{ {
u32 opposingBattler = 0; u32 opposingBattler = 0;
u32 bestMonId = 0; u32 bestMonId = PARTY_SIZE;
u8 battlerIn1 = 0, battlerIn2 = 0; u8 battlerIn1 = 0, battlerIn2 = 0;
s32 firstId = 0; s32 firstId = 0;
s32 lastId = 0; // + 1 s32 lastId = 0; // + 1
@ -948,7 +958,7 @@ u8 GetMostSuitableMonToSwitchInto(void)
// Get invalid slots ids. // Get invalid slots ids.
for (i = firstId; i < lastId; i++) 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 || GetMonData(&party[i], MON_DATA_HP) == 0
|| gBattlerPartyIndexes[battlerIn1] == i || gBattlerPartyIndexes[battlerIn1] == i
|| gBattlerPartyIndexes[battlerIn2] == i || gBattlerPartyIndexes[battlerIn2] == i
@ -962,19 +972,26 @@ u8 GetMostSuitableMonToSwitchInto(void)
aliveCount++; aliveCount++;
} }
bestMonId = GetBestMonBatonPass(party, firstId, lastId, invalidMons, aliveCount); bestMonId = GetBestMonBatonPass(party, firstId, lastId, invalidMons, aliveCount, checkSurvivability);
if (bestMonId != PARTY_SIZE) if (bestMonId != PARTY_SIZE)
return bestMonId; return bestMonId;
bestMonId = GetBestMonTypeMatchup(party, firstId, lastId, invalidMons, opposingBattler); bestMonId = GetBestMonTypeMatchup(party, firstId, lastId, invalidMons, opposingBattler, checkSurvivability);
if (bestMonId != PARTY_SIZE) if (bestMonId != PARTY_SIZE)
return bestMonId; return bestMonId;
bestMonId = GetBestMonDmg(party, firstId, lastId, invalidMons, opposingBattler); bestMonId = GetBestMonDmg(party, firstId, lastId, invalidMons, opposingBattler, checkSurvivability);
if (bestMonId != PARTY_SIZE) if (bestMonId != PARTY_SIZE)
return bestMonId; 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) static u8 GetAI_ItemType(u16 itemId, const u8 *itemEffect)
@ -1178,3 +1195,24 @@ static bool32 AI_OpponentCanFaintAiWithMod(u32 healAmount)
} }
return FALSE; 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;
}

View File

@ -1676,8 +1676,7 @@ static void OpponentHandleChoosePokemon(void)
if (*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) == PARTY_SIZE) if (*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) == PARTY_SIZE)
{ {
chosenMonId = GetMostSuitableMonToSwitchInto(); chosenMonId = GetMostSuitableMonToSwitchInto(TRUE);
if (chosenMonId == PARTY_SIZE) if (chosenMonId == PARTY_SIZE)
{ {
s32 battler1, battler2, firstId, lastId; s32 battler1, battler2, firstId, lastId;
@ -1691,7 +1690,6 @@ static void OpponentHandleChoosePokemon(void)
battler1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); battler1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
battler2 = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); battler2 = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
pokemonInBattle = 2; pokemonInBattle = 2;
} }
GetAIPartyIndexes(gActiveBattler, &firstId, &lastId); GetAIPartyIndexes(gActiveBattler, &firstId, &lastId);
@ -1699,6 +1697,7 @@ static void OpponentHandleChoosePokemon(void)
for (chosenMonId = (lastId-1); chosenMonId >= firstId; chosenMonId--) for (chosenMonId = (lastId-1); chosenMonId >= firstId; chosenMonId--)
{ {
if (GetMonData(&gEnemyParty[chosenMonId], MON_DATA_HP) != 0 if (GetMonData(&gEnemyParty[chosenMonId], MON_DATA_HP) != 0
&& GetMonData(&gEnemyParty[chosenMonId], MON_DATA_SPECIES2) != SPECIES_NONE
&& chosenMonId != gBattlerPartyIndexes[battler1] && chosenMonId != gBattlerPartyIndexes[battler1]
&& chosenMonId != gBattlerPartyIndexes[battler2] && chosenMonId != gBattlerPartyIndexes[battler2]
&& (AI_THINKING_STRUCT->aiFlags & AI_FLAG_ACE_POKEMON && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_ACE_POKEMON

View File

@ -1548,7 +1548,7 @@ static void PlayerPartnerHandleChooseItem(void)
static void PlayerPartnerHandleChoosePokemon(void) static void PlayerPartnerHandleChoosePokemon(void)
{ {
s32 chosenMonId = GetMostSuitableMonToSwitchInto(); s32 chosenMonId = GetMostSuitableMonToSwitchInto(TRUE);
if (chosenMonId == 6) // just switch to the next mon if (chosenMonId == 6) // just switch to the next mon
{ {