Added AI_FLAG_ACE_POKEMON

When this flag is set, the trainer will have an "Ace" pokemon that will
always be sent out last.
If this flag is set, the last Pokemon in the party will be considered the
"Ace" Pokemon.

This is similar to the gym leader functionality found in Sword & Shield.
This commit is contained in:
Ct11217 2022-08-17 18:23:12 -06:00
parent bb978764f3
commit a4b53126f6
3 changed files with 51 additions and 4 deletions

View File

@ -56,6 +56,7 @@
#define AI_FLAG_STALL (1 << 13) // AI stalls battle and prefers secondary damage/trapping/etc. TODO not finished #define AI_FLAG_STALL (1 << 13) // AI stalls battle and prefers secondary damage/trapping/etc. TODO not finished
#define AI_FLAG_SCREENER (1 << 14) // AI prefers screening effects like reflect, mist, etc. TODO unfinished #define AI_FLAG_SCREENER (1 << 14) // AI prefers screening effects like reflect, mist, etc. TODO unfinished
#define AI_FLAG_SMART_SWITCHING (1 << 15) // AI includes a lot more switching checks #define AI_FLAG_SMART_SWITCHING (1 << 15) // AI includes a lot more switching checks
#define AI_FLAG_ACE_POKEMON (1 << 16) // AI has Ace Pokemon -- Saves until last
// 'other' ai logic flags // 'other' ai logic flags
#define AI_FLAG_ROAMING (1 << 29) #define AI_FLAG_ROAMING (1 << 29)

View File

@ -199,6 +199,10 @@ static bool8 FindMonThatAbsorbsOpponentsMove(void)
continue; continue;
if (i == *(gBattleStruct->monToSwitchIntoId + battlerIn2)) if (i == *(gBattleStruct->monToSwitchIntoId + battlerIn2))
continue; continue;
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_ACE_POKEMON
&& i == (CalculateEnemyPartyCount()-1))
continue;
species = GetMonData(&party[i], MON_DATA_SPECIES); species = GetMonData(&party[i], MON_DATA_SPECIES);
if (GetMonData(&party[i], MON_DATA_ABILITY_NUM) != 0) if (GetMonData(&party[i], MON_DATA_ABILITY_NUM) != 0)
@ -276,6 +280,10 @@ static bool8 ShouldSwitchIfGameStatePrompt(void)
for (i = firstId; i < lastId; i++) for (i = firstId; i < lastId; i++)
{ {
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_ACE_POKEMON
&& i == (CalculateEnemyPartyCount()-1))
break;
//Look for mon in party that is able to be switched into and has ability that sets terrain //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 if (GetMonData(&party[i], MON_DATA_HP) != 0
&& GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_NONE && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_NONE
@ -562,6 +570,10 @@ static bool8 FindMonWithFlagsAndSuperEffective(u16 flags, u8 moduloPercent)
continue; continue;
if (i == *(gBattleStruct->monToSwitchIntoId + battlerIn2)) if (i == *(gBattleStruct->monToSwitchIntoId + battlerIn2))
continue; continue;
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_ACE_POKEMON
&& i == (CalculateEnemyPartyCount()-1))
continue;
species = GetMonData(&party[i], MON_DATA_SPECIES); species = GetMonData(&party[i], MON_DATA_SPECIES);
if (GetMonData(&party[i], MON_DATA_ABILITY_NUM) != 0) if (GetMonData(&party[i], MON_DATA_ABILITY_NUM) != 0)
@ -650,6 +662,9 @@ bool32 ShouldSwitch(void)
continue; continue;
if (i == *(gBattleStruct->monToSwitchIntoId + battlerIn2)) if (i == *(gBattleStruct->monToSwitchIntoId + battlerIn2))
continue; continue;
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_ACE_POKEMON
&& i == (CalculateEnemyPartyCount()-1))
continue;
availableToSwitch++; availableToSwitch++;
} }
@ -712,7 +727,7 @@ void AI_TrySwitchOrUseItem(void)
GetAIPartyIndexes(gActiveBattler, &firstId, &lastId); GetAIPartyIndexes(gActiveBattler, &firstId, &lastId);
for (monToSwitchId = firstId; monToSwitchId < lastId; monToSwitchId++) for (monToSwitchId = (lastId-1); monToSwitchId >= firstId; monToSwitchId--)
{ {
if (GetMonData(&party[monToSwitchId], MON_DATA_HP) == 0) if (GetMonData(&party[monToSwitchId], MON_DATA_HP) == 0)
continue; continue;
@ -724,6 +739,9 @@ void AI_TrySwitchOrUseItem(void)
continue; continue;
if (monToSwitchId == *(gBattleStruct->monToSwitchIntoId + battlerIn2)) if (monToSwitchId == *(gBattleStruct->monToSwitchIntoId + battlerIn2))
continue; continue;
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_ACE_POKEMON
&& monToSwitchId == (CalculateEnemyPartyCount()-1))
continue;
break; break;
} }
@ -921,7 +939,9 @@ u8 GetMostSuitableMonToSwitchInto(void)
|| gBattlerPartyIndexes[battlerIn2] == i || gBattlerPartyIndexes[battlerIn2] == i
|| i == *(gBattleStruct->monToSwitchIntoId + battlerIn1) || i == *(gBattleStruct->monToSwitchIntoId + battlerIn1)
|| i == *(gBattleStruct->monToSwitchIntoId + battlerIn2) || i == *(gBattleStruct->monToSwitchIntoId + battlerIn2)
|| (GetMonAbility(&party[i]) == ABILITY_TRUANT && IsTruantMonVulnerable(gActiveBattler, opposingBattler))) // While not really invalid per say, not really wise to switch into this mon. || (GetMonAbility(&party[i]) == ABILITY_TRUANT && IsTruantMonVulnerable(gActiveBattler, opposingBattler)) // While not really invalid per say, not really wise to switch into this mon.
|| (AI_THINKING_STRUCT->aiFlags & AI_FLAG_ACE_POKEMON
&& i == (CalculateEnemyPartyCount()-1))) //Save Ace Pokemon for last
invalidMons |= gBitTable[i]; invalidMons |= gBitTable[i];
else else
aliveCount++; aliveCount++;

View File

@ -2,6 +2,7 @@
#include "battle.h" #include "battle.h"
#include "battle_ai_main.h" #include "battle_ai_main.h"
#include "battle_ai_util.h" #include "battle_ai_util.h"
#include "constants/battle_ai.h"
#include "battle_anim.h" #include "battle_anim.h"
#include "battle_arena.h" #include "battle_arena.h"
#include "battle_controllers.h" #include "battle_controllers.h"
@ -94,6 +95,7 @@ static void OpponentHandleResetActionMoveSelection(void);
static void OpponentHandleEndLinkBattle(void); static void OpponentHandleEndLinkBattle(void);
static void OpponentHandleDebugMenu(void); static void OpponentHandleDebugMenu(void);
static void OpponentCmdEnd(void); static void OpponentCmdEnd(void);
static u8 CountAIAliveNonEggMonsExcept(u8 slotToIgnore);
static void OpponentBufferRunCommand(void); static void OpponentBufferRunCommand(void);
static void OpponentBufferExecCompleted(void); static void OpponentBufferExecCompleted(void);
@ -1670,6 +1672,7 @@ static void OpponentHandleChooseItem(void)
static void OpponentHandleChoosePokemon(void) static void OpponentHandleChoosePokemon(void)
{ {
s32 chosenMonId; s32 chosenMonId;
s32 pokemonInBattle = 1;
if (*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) == PARTY_SIZE) if (*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) == PARTY_SIZE)
{ {
@ -1687,15 +1690,20 @@ 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;
} }
GetAIPartyIndexes(gActiveBattler, &firstId, &lastId); GetAIPartyIndexes(gActiveBattler, &firstId, &lastId);
for (chosenMonId = firstId; chosenMonId < lastId; chosenMonId++) for (chosenMonId = (lastId-1); chosenMonId >= firstId; chosenMonId--)
{ {
if (GetMonData(&gEnemyParty[chosenMonId], MON_DATA_HP) != 0 if (GetMonData(&gEnemyParty[chosenMonId], MON_DATA_HP) != 0
&& chosenMonId != gBattlerPartyIndexes[battler1] && chosenMonId != gBattlerPartyIndexes[battler1]
&& chosenMonId != gBattlerPartyIndexes[battler2]) && chosenMonId != gBattlerPartyIndexes[battler2]
&& (AI_THINKING_STRUCT->aiFlags & AI_FLAG_ACE_POKEMON
&& (!(chosenMonId == (CalculateEnemyPartyCount()-1))
|| CountAIAliveNonEggMonsExcept(PARTY_SIZE) == pokemonInBattle)))
{ {
break; break;
} }
@ -1714,6 +1722,24 @@ static void OpponentHandleChoosePokemon(void)
OpponentBufferExecCompleted(); OpponentBufferExecCompleted();
} }
static u8 CountAIAliveNonEggMonsExcept(u8 slotToIgnore)
{
u16 i, count;
for (i = 0, count = 0; i < PARTY_SIZE; i++)
{
if (i != slotToIgnore
&& GetMonData(&gEnemyParty[i], MON_DATA_SPECIES) != SPECIES_NONE
&& !GetMonData(&gEnemyParty[i], MON_DATA_IS_EGG)
&& GetMonData(&gEnemyParty[i], MON_DATA_HP) != 0)
{
count++;
}
}
return count;
}
static void OpponentHandleCmd23(void) static void OpponentHandleCmd23(void)
{ {
OpponentBufferExecCompleted(); OpponentBufferExecCompleted();