New AI flag for marking the two last Pokémon as Ace Pokémon. (#5587)

This commit is contained in:
GhoulMage 2024-10-27 16:32:45 +01:00 committed by GitHub
parent 5d4ab8cc7e
commit 6819dd3c37
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 109 additions and 1 deletions

View file

@ -136,6 +136,9 @@ Affects when the AI chooses to switch. AI will make smarter decisions about when
## `AI_FLAG_ACE_POKEMON`
Marks the last Pokemon in the party as the Ace Pokemon. It will not be used unless it is the last one remaining, or is forced to be switched in (Roar, U-Turn with 1 mon remaining, etc.)
## `AI_FLAG_DOUBLE_ACE_POKEMON`
Marks the last two Pokémon in the party as Ace Pokémon, with the same behaviour as `AI_FLAG_ACE_POKEMON`. Intented for double battles where you battle one trainer id that represents two trainers, ie Twins, Couples.
## `AI_FLAG_OMNISCIENT`
AI has full knowledge of player moves, abilities, and hold items, and can use this knowledge when making decisions.

View file

@ -48,8 +48,9 @@
#define AI_FLAG_SMART_MON_CHOICES (1 << 17) // AI will make smarter decisions when choosing which mon to send out mid-battle and after a KO, which are separate decisions. Automatically included by AI_FLAG_SMART_SWITCHING.
#define AI_FLAG_CONSERVATIVE (1 << 18) // AI assumes all moves will low roll damage.
#define AI_FLAG_SEQUENCE_SWITCHING (1 << 19) // AI switches in mons in exactly party order, and never switches mid-battle.
#define AI_FLAG_DOUBLE_ACE_POKEMON (1 << 20) // AI has *two* Ace Pokémon. The last two Pokémons in the party won't be used unless they're the last ones remaining. Goes well in battles where the trainer ID equals to twins, couples, etc.
#define AI_FLAG_COUNT 20
#define AI_FLAG_COUNT 21
// The following options are enough to have a basic/smart trainer. Any other addtion could make the trainer worse/better depending on the flag
#define AI_FLAG_BASIC_TRAINER (AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY)

View file

@ -43,6 +43,10 @@ static bool32 IsAceMon(u32 battler, u32 monPartyId)
&& !(gBattleStruct->forcedSwitch & (1u << battler))
&& monPartyId == CalculateEnemyPartyCount()-1)
return TRUE;
if (AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_DOUBLE_ACE_POKEMON
&& !(gBattleStruct->forcedSwitch & (1u << battler))
&& (monPartyId == CalculateEnemyPartyCount()-1 || monPartyId == CalculateEnemyPartyCount()-2))
return TRUE;
return FALSE;
}

View file

@ -688,6 +688,10 @@ static void OpponentHandleChoosePokemon(u32 battler)
if ((AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_ACE_POKEMON)
&& ((chosenMonId != CalculateEnemyPartyCount() - 1) || CountAIAliveNonEggMonsExcept(PARTY_SIZE) == pokemonInBattle))
continue;
if ((AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_DOUBLE_ACE_POKEMON)
&& (((chosenMonId != CalculateEnemyPartyCount() - 1) || (chosenMonId != CalculateEnemyPartyCount() - 1))
|| CountAIAliveNonEggMonsExcept(PARTY_SIZE) == pokemonInBattle))
continue;
// mon is valid
break;
}

View file

@ -0,0 +1,96 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS {
ASSUME(gMovesInfo[MOVE_U_TURN].effect == EFFECT_HIT_ESCAPE);
ASSUME(gMovesInfo[MOVE_CRUNCH].type == TYPE_DARK);
ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[0] == TYPE_PSYCHIC);
ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[1] == TYPE_PSYCHIC);
}
AI_DOUBLE_BATTLE_TEST("AI_FLAG_DOUBLE_ACE_POKEMON: U-Turn won't send out any of the Ace Mons if other options exist")
{
u32 flag;
PARAMETRIZE { flag = AI_FLAG_DOUBLE_ACE_POKEMON; }
PARAMETRIZE { flag = 0; }
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_SMART_SWITCHING | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_MON_CHOICES | flag);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_GASTLY) { Moves(MOVE_U_TURN); }
OPPONENT(SPECIES_DUSKULL) { Moves(MOVE_U_TURN); }
OPPONENT(SPECIES_HAUNTER) { Moves(MOVE_U_TURN); }
OPPONENT(SPECIES_GENGAR) { Moves(MOVE_U_TURN); }
// Aces
// Crunch is super effective against Wobbuffet Psychic type, so normally the AI would switch them in
OPPONENT(SPECIES_POOCHYENA) { Moves(MOVE_CRUNCH); }
OPPONENT(SPECIES_MIGHTYENA) { Moves(MOVE_CRUNCH); }
} WHEN {
TURN {
EXPECT_MOVE(opponentLeft, MOVE_U_TURN);
EXPECT_MOVE(opponentRight, MOVE_U_TURN);
if(flag == AI_FLAG_DOUBLE_ACE_POKEMON) {
EXPECT_SEND_OUT(opponentLeft, 3);
EXPECT_SEND_OUT(opponentRight, 2);
} else {
EXPECT_SEND_OUT(opponentLeft, 4);
EXPECT_SEND_OUT(opponentRight, 5);
}
}
}
}
AI_DOUBLE_BATTLE_TEST("AI_FLAG_DOUBLE_ACE_POKEMON: U-Turn will send out an Ace Mon if no other options remain")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_SMART_SWITCHING | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_DOUBLE_ACE_POKEMON);
PLAYER(SPECIES_WOBBUFFET) { Level(50); }
PLAYER(SPECIES_WOBBUFFET) { Level(50); }
OPPONENT(SPECIES_GASTLY) { Moves(MOVE_U_TURN); Level(50); }
OPPONENT(SPECIES_DUSKULL) { Moves(MOVE_U_TURN); Level(5); }
// Aces
// Should choose Poochyena as its level is higher.
OPPONENT(SPECIES_MIGHTYENA) { Moves(MOVE_CRUNCH); Level(5); }
OPPONENT(SPECIES_POOCHYENA) { Moves(MOVE_CRUNCH); Level(50); }
} WHEN {
TURN {
EXPECT_MOVE(opponentLeft, MOVE_U_TURN);
EXPECT_MOVE(opponentRight, MOVE_U_TURN);
EXPECT_SEND_OUT(opponentLeft, 3);
EXPECT_SEND_OUT(opponentRight, 0);
}
}
}
AI_DOUBLE_BATTLE_TEST("AI_FLAG_DOUBLE_ACE_POKEMON: Ace mons won't be switched in even if they are the best candidates")
{
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_GENGAR].types[0] == TYPE_GHOST);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_SMART_SWITCHING | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_DOUBLE_ACE_POKEMON);
PLAYER(SPECIES_GENGAR) { Level(10); }
PLAYER(SPECIES_GENGAR) { Level(10); }
OPPONENT(SPECIES_RATTATA) { Moves(MOVE_TACKLE); Level(10); }
OPPONENT(SPECIES_PSYDUCK) { Moves(MOVE_TACKLE); Level(10); }
OPPONENT(SPECIES_ABRA) { Moves(MOVE_ABSORB); Level(20); }
// Aces
OPPONENT(SPECIES_MIGHTYENA) { Moves(MOVE_CRUNCH); Level(50); }
OPPONENT(SPECIES_POOCHYENA) { Moves(MOVE_CRUNCH); Level(50); }
} WHEN {
TURN { EXPECT_SWITCH(opponentLeft, 2); }
}
}