Smarter SwitchAI Mon Choices | HasBadOdds Switch Check (#3253)
* SwitchAI makes much smarter mon choices
* Add HasHadOdds check to ShouldSwitch decision
* Remove early return
* Rework Baton Pass check as per discussion with Alex
* Forgot to adjust a comment
* Don't program before breakfast lol (if / else if fix)
* Switch AI_CalcDamage for AI_DATA->simulatedDmg in HasBadOdds
Thanks Alex! :D
* Typo in a hitToKO comparison
* Remove and replace AI_CalcPartyMonBestMoveDamage and IsAiPartyMonOHKOBy from https://github.com/rh-hideout/pokeemerald-expansion/pull/3146
See https://discord.com/channels/419213663107416084/1144447521960251472 for details
* Major refactor, new struct, switchin considers damage / healing from hazards / status / held item / weather
* Forgot Snow exists and heals Ice Body, haven't played Switch games lol
* (766a1a27a7
) Compatibility, use new struct field instead of function call
* Fixing oversight from previous upstream merge
* Improve TSpikes handling to make GetSwitchinHazardDamage more applicable
Small fixes:
- EFFECT_EXPLOSION typo (!= to ==)
- Order of if statements near bestResistEffective
- Spacing of terms in big HasBadOdds if statements
* Forgot to uncomment blocks disabled for debugging what turned out to be vanilla behaviour lol
* Remove another holdover from debugging, sorry :/
* Lastly, undoing my debug trainer
* Type matchup based on species type rather than current type
Suggested by BLourenco on Discord, the idea is that a mon that's had its type affected by a move like Soak will still have moves as though it was its regular typing, and so prioritizing the temporary typing wouldn't be ideal.
https://discord.com/channels/419213663107416084/1144447521960251472/1146644578141736970
* gActiveBattler upcoming merge fixes
* Egg changes part 1
* Egg changes part 2, just need to address EWRAM still
* Move SwitchinCandidate struct to AiLogicData
* Consider Steel type when checking TSpikes
* Comment about CanBePoisoned compatibility
* Changes for Egg's 2nd review
* Put period back in comment, whoops lol
* Latest upcoming merge fixes
* Missed a few u32 updates
* Combine GetBestMonIntegrate functions / flags, some modularization
* Fix merge error
* Make modern fixes
* Two tests done, two to go
* Accidentally pushed reference test, removing it
* Type matchup switching tests
* Tests for defensive vs offense switches
---------
Co-authored-by: DizzyEggg <jajkodizzy@wp.pl>
This commit is contained in:
parent
3fc47d137a
commit
b93dfb9d59
13 changed files with 1033 additions and 136 deletions
|
@ -290,6 +290,12 @@ struct AIPartyData // Opposing battlers - party mons.
|
|||
u8 count[NUM_BATTLE_SIDES];
|
||||
};
|
||||
|
||||
struct SwitchinCandidate
|
||||
{
|
||||
struct BattlePokemon battleMon;
|
||||
bool8 hypotheticalStatus;
|
||||
};
|
||||
|
||||
// Ai Data used when deciding which move to use, computed only once before each turn's start.
|
||||
struct AiLogicData
|
||||
{
|
||||
|
@ -308,6 +314,8 @@ struct AiLogicData
|
|||
bool8 shouldSwitchMon; // Because all available moves have no/little effect. Each bit per battler.
|
||||
u8 monToSwitchId[MAX_BATTLERS_COUNT]; // ID of the mon to switch.
|
||||
bool8 weatherHasEffect; // The same as WEATHER_HAS_EFFECT. Stored here, so it's called only once.
|
||||
u8 mostSuitableMonId; // Stores result of GetMostSuitableMonToSwitchInto, which decides which generic mon the AI would switch into if they decide to switch. This can be overruled by specific mons found in ShouldSwitch; the final resulting mon is stored in AI_monToSwitchIntoId.
|
||||
struct SwitchinCandidate switchinCandidate; // Struct used for deciding which mon to switch to in battle_ai_switch_items.c
|
||||
};
|
||||
|
||||
struct AI_ThinkingStruct
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId);
|
||||
void AI_TrySwitchOrUseItem(u32 battler);
|
||||
u8 GetMostSuitableMonToSwitchInto(u32 battler);
|
||||
u8 GetMostSuitableMonToSwitchInto(u32 battler, bool32 switchAfterMonKOd);
|
||||
bool32 ShouldSwitch(u32 battler);
|
||||
|
||||
#endif // GUARD_BATTLE_AI_SWITCH_ITEMS_H
|
||||
|
|
|
@ -174,7 +174,6 @@ bool32 ShouldUseWishAromatherapy(u32 battlerAtk, u32 battlerDef, u32 move);
|
|||
// party logic
|
||||
struct BattlePokemon *AllocSaveBattleMons(void);
|
||||
void FreeRestoreBattleMons(struct BattlePokemon *savedBattleMons);
|
||||
s32 AI_CalcPartyMonBestMoveDamage(u32 battlerAtk, u32 battlerDef, struct Pokemon *attackerMon, struct Pokemon *targetMon);
|
||||
s32 CountUsablePartyMons(u32 battlerId);
|
||||
bool32 IsPartyFullyHealedExceptBattler(u32 battler);
|
||||
bool32 PartyHasMoveSplit(u32 battlerId, u32 split);
|
||||
|
@ -189,4 +188,6 @@ void IncreaseSleepScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score);
|
|||
void IncreaseConfusionScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score);
|
||||
void IncreaseFrostbiteScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score);
|
||||
|
||||
s32 AI_CalcPartyMonDamage(u32 move, u32 battlerAtk, u32 battlerDef, struct BattlePokemon switchinCandidate, bool8 isPartyMonAttacker);
|
||||
|
||||
#endif //GUARD_BATTLE_AI_UTIL_H
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#define AI_FLAG_SMART_SWITCHING (1 << 15) // AI includes a lot more switching checks
|
||||
#define AI_FLAG_ACE_POKEMON (1 << 16) // AI has an Ace Pokemon. The last Pokemon in the party will not be used until it's the last one remaining.
|
||||
#define AI_FLAG_OMNISCIENT (1 << 17) // AI has full knowledge of player moves, abilities, hold items
|
||||
#define AI_FLAG_SMART_MON_CHOICES (1 << 18) // AI will make smarter decisions when choosing which mon to send out mid-battle and after a KO, which are separate decisions. Pairs very well with AI_FLAG_SMART_SWITCHING.
|
||||
|
||||
#define AI_FLAG_COUNT 18
|
||||
|
||||
|
|
|
@ -607,6 +607,20 @@
|
|||
#define ITEM_UTILITY_UMBRELLA 513
|
||||
|
||||
// Berries
|
||||
#if B_CONFUSE_BERRIES_HEAL >= GEN_8
|
||||
#define CONFUSE_BERRY_HEAL_FRACTION 3
|
||||
#elif B_CONFUSE_BERRIES_HEAL == GEN_7
|
||||
#define CONFUSE_BERRY_HEAL_FRACTION 2
|
||||
#else
|
||||
#define CONFUSE_BERRY_HEAL_FRACTION 8
|
||||
#endif
|
||||
|
||||
#if B_CONFUSE_BERRIES_HEAL >= GEN_7
|
||||
#define CONFUSE_BERRY_HP_FRACTION 4
|
||||
#else
|
||||
#define CONFUSE_BERRY_HP_FRACTION 2
|
||||
#endif
|
||||
|
||||
#define ITEM_CHERI_BERRY 514
|
||||
#define ITEM_CHESTO_BERRY 515
|
||||
#define ITEM_PECHA_BERRY 516
|
||||
|
|
|
@ -416,7 +416,7 @@ void SetAiLogicDataForTurn(struct AiLogicData *aiData)
|
|||
|
||||
static bool32 AI_SwitchMonIfSuitable(u32 battler)
|
||||
{
|
||||
u32 monToSwitchId = GetMostSuitableMonToSwitchInto(battler);
|
||||
u32 monToSwitchId = AI_DATA->mostSuitableMonId;
|
||||
if (monToSwitchId != PARTY_SIZE)
|
||||
{
|
||||
AI_DATA->shouldSwitchMon |= gBitTable[battler];
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -3366,32 +3366,16 @@ void FreeRestoreBattleMons(struct BattlePokemon *savedBattleMons)
|
|||
}
|
||||
|
||||
// party logic
|
||||
s32 AI_CalcPartyMonBestMoveDamage(u32 battlerAtk, u32 battlerDef, struct Pokemon *attackerMon, struct Pokemon *targetMon)
|
||||
s32 AI_CalcPartyMonDamage(u32 move, u32 battlerAtk, u32 battlerDef, struct BattlePokemon switchinCandidate, bool8 isPartyMonAttacker)
|
||||
{
|
||||
s32 i, move, bestDmg, dmg = 0;
|
||||
s32 dmg;
|
||||
u8 effectiveness;
|
||||
struct BattlePokemon *savedBattleMons = AllocSaveBattleMons();
|
||||
|
||||
if (attackerMon != NULL)
|
||||
PokemonToBattleMon(attackerMon, &gBattleMons[battlerAtk]);
|
||||
if (targetMon != NULL)
|
||||
PokemonToBattleMon(targetMon, &gBattleMons[battlerDef]);
|
||||
|
||||
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[GetBattlerSide(battlerAtk)][gBattlerPartyIndexes[battlerAtk]].moves[i];
|
||||
|
||||
if (move != MOVE_NONE && gBattleMoves[move].power != 0)
|
||||
{
|
||||
dmg = AI_CalcDamageSaveBattlers(move, battlerAtk, battlerDef, &effectiveness, FALSE);
|
||||
if (dmg > bestDmg)
|
||||
bestDmg = dmg;
|
||||
}
|
||||
}
|
||||
|
||||
if(isPartyMonAttacker)
|
||||
gBattleMons[battlerAtk] = switchinCandidate;
|
||||
else
|
||||
gBattleMons[battlerDef] = switchinCandidate;
|
||||
dmg = AI_CalcDamage(move, battlerAtk, battlerDef, &effectiveness, FALSE, AI_GetWeather(AI_DATA));
|
||||
FreeRestoreBattleMons(savedBattleMons);
|
||||
return dmg;
|
||||
}
|
||||
|
|
|
@ -656,7 +656,7 @@ static void OpponentHandleChoosePokemon(u32 battler)
|
|||
// Switching out
|
||||
else if (*(gBattleStruct->AI_monToSwitchIntoId + battler) == PARTY_SIZE)
|
||||
{
|
||||
chosenMonId = GetMostSuitableMonToSwitchInto(battler);
|
||||
chosenMonId = GetMostSuitableMonToSwitchInto(battler, TRUE);
|
||||
if (chosenMonId == PARTY_SIZE)
|
||||
{
|
||||
s32 battler1, battler2, firstId, lastId;
|
||||
|
|
|
@ -399,7 +399,7 @@ static void PlayerPartnerHandleChoosePokemon(u32 battler)
|
|||
// Switching out
|
||||
else if (gBattleStruct->monToSwitchIntoId[battler] >= PARTY_SIZE || !IsValidForBattle(&gPlayerParty[gBattleStruct->monToSwitchIntoId[battler]]))
|
||||
{
|
||||
chosenMonId = GetMostSuitableMonToSwitchInto(battler);
|
||||
chosenMonId = GetMostSuitableMonToSwitchInto(battler, TRUE);
|
||||
|
||||
if (chosenMonId == PARTY_SIZE || !IsValidForBattle(&gPlayerParty[chosenMonId])) // just switch to the next mon
|
||||
{
|
||||
|
|
|
@ -4052,6 +4052,7 @@ static void HandleTurnActionSelectionState(void)
|
|||
if ((gBattleTypeFlags & BATTLE_TYPE_HAS_AI || IsWildMonSmart())
|
||||
&& (BattlerHasAi(battler) && !(gBattleTypeFlags & BATTLE_TYPE_PALACE)))
|
||||
{
|
||||
AI_DATA->mostSuitableMonId = GetMostSuitableMonToSwitchInto(battler, FALSE);
|
||||
gBattleStruct->aiMoveOrAction[battler] = ComputeBattleAiScores(battler);
|
||||
}
|
||||
// fallthrough
|
||||
|
|
|
@ -6434,14 +6434,6 @@ const struct Item gItems[] =
|
|||
.flingPower = 10,
|
||||
},
|
||||
|
||||
#if B_CONFUSE_BERRIES_HEAL >= GEN_8
|
||||
#define CONFUSE_BERRY_HEAL_FRACTION 3
|
||||
#elif B_CONFUSE_BERRIES_HEAL == GEN_7
|
||||
#define CONFUSE_BERRY_HEAL_FRACTION 2
|
||||
#else
|
||||
#define CONFUSE_BERRY_HEAL_FRACTION 8
|
||||
#endif
|
||||
|
||||
[ITEM_FIGY_BERRY] =
|
||||
{
|
||||
.name = _("Figy Berry"),
|
||||
|
@ -6507,8 +6499,6 @@ const struct Item gItems[] =
|
|||
.flingPower = 10,
|
||||
},
|
||||
|
||||
#undef CONFUSE_BERRY_HEAL_FRACTION
|
||||
|
||||
[ITEM_RAZZ_BERRY] =
|
||||
{
|
||||
.name = _("Razz Berry"),
|
||||
|
|
132
test/battle/ai.c
132
test/battle/ai.c
|
@ -373,23 +373,6 @@ AI_SINGLE_BATTLE_TEST("AI will not switch in a Pokemon which is slower and gets
|
|||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI switches if Perish Song is about to kill")
|
||||
{
|
||||
GIVEN {
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET) {Moves(MOVE_TACKLE); }
|
||||
OPPONENT(SPECIES_CROBAT) {Moves(MOVE_TACKLE); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_PERISH_SONG); }
|
||||
TURN { ; }
|
||||
TURN { ; }
|
||||
TURN { EXPECT_SWITCH(opponent, 1); }
|
||||
} SCENE {
|
||||
MESSAGE("{PKMN} TRAINER LEAF sent out Crobat!");
|
||||
}
|
||||
}
|
||||
|
||||
AI_DOUBLE_BATTLE_TEST("AI won't use a Weather changing move if partner already chose such move")
|
||||
{
|
||||
u32 j, k;
|
||||
|
@ -511,3 +494,118 @@ AI_DOUBLE_BATTLE_TEST("AI without any flags chooses moves at random - doubles")
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI will not switch in a Pokemon which is slower and gets 1HKOed after fainting")
|
||||
{
|
||||
bool32 alakazamFirst;
|
||||
u32 speedAlakazm;
|
||||
u32 aiSmartSwitchFlags = 0;
|
||||
|
||||
PARAMETRIZE{ speedAlakazm = 200; alakazamFirst = TRUE; } // AI will always send out Alakazan as it sees a KO with Focus Blast, even if Alakazam dies before it can get it off
|
||||
PARAMETRIZE{ speedAlakazm = 200; alakazamFirst = FALSE; aiSmartSwitchFlags = AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES; } // AI_FLAG_SMART_MON_CHOICES lets AI see that Alakazam would be KO'd before it can KO, and won't switch it in
|
||||
PARAMETRIZE{ speedAlakazm = 400; alakazamFirst = TRUE; aiSmartSwitchFlags = AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES; } // AI_FLAG_SMART_MON_CHOICES recognizes that Alakazam is faster and can KO, and will switch it in
|
||||
|
||||
GIVEN {
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiSmartSwitchFlags);
|
||||
PLAYER(SPECIES_WEAVILE) { Speed(300); Ability(ABILITY_SHADOW_TAG); } // Weavile has Shadow Tag, so AI can't switch on the first turn, but has to do it after fainting.
|
||||
OPPONENT(SPECIES_KADABRA) { Speed(200); Moves(MOVE_PSYCHIC, MOVE_DISABLE, MOVE_TAUNT, MOVE_CALM_MIND); }
|
||||
OPPONENT(SPECIES_ALAKAZAM) { Speed(speedAlakazm); Moves(MOVE_FOCUS_BLAST, MOVE_PSYCHIC); } // Alakazam has a move which OHKOes Weavile, but it doesn't matter if he's getting KO-ed first.
|
||||
OPPONENT(SPECIES_BLASTOISE) { Speed(200); Moves(MOVE_BUBBLE_BEAM, MOVE_WATER_GUN, MOVE_LEER, MOVE_STRENGTH); } // Can't OHKO, but survives a hit from Weavile's Night Slash.
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_NIGHT_SLASH) ; EXPECT_SEND_OUT(opponent, alakazamFirst ? 1 : 2); } // AI doesn't send out Alakazam if it gets outsped
|
||||
} SCENE {
|
||||
MESSAGE("Foe Kadabra fainted!");
|
||||
if (alakazamFirst) {
|
||||
MESSAGE("{PKMN} TRAINER LEAF sent out Alakazam!");
|
||||
} else {
|
||||
MESSAGE("{PKMN} TRAINER LEAF sent out Blastoise!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI switches if Perish Song is about to kill")
|
||||
{
|
||||
GIVEN {
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET) {Moves(MOVE_TACKLE); }
|
||||
OPPONENT(SPECIES_CROBAT) {Moves(MOVE_TACKLE); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_PERISH_SONG); }
|
||||
TURN { ; }
|
||||
TURN { ; }
|
||||
TURN { EXPECT_SWITCH(opponent, 1); }
|
||||
} SCENE {
|
||||
MESSAGE("{PKMN} TRAINER LEAF sent out Crobat!");
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI considers hazard damage when choosing which Pokemon to switch in")
|
||||
{
|
||||
u32 aiIsSmart = 0;
|
||||
u32 aiSmartSwitchFlags = 0;
|
||||
|
||||
PARAMETRIZE{ aiIsSmart = 0; aiSmartSwitchFlags = 0; } // AI doesn't care about hazard damage resulting in Pokemon being KO'd
|
||||
PARAMETRIZE{ aiIsSmart = 1; aiSmartSwitchFlags = AI_FLAG_SMART_MON_CHOICES; } // AI_FLAG_SMART_MON_CHOICES avoids being KO'd as a result of hazards damage
|
||||
|
||||
GIVEN {
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiSmartSwitchFlags);
|
||||
PLAYER(SPECIES_MEGANIUM) { Speed(100); SpDefense(328); SpAttack(265); Moves(MOVE_STEALTH_ROCK, MOVE_SURF); } // Meganium does ~56% minimum ~66% maximum, enough to KO Charizard after rocks and never KO Typhlosion after rocks
|
||||
OPPONENT(SPECIES_PONYTA) { Level(5); Speed(5); Moves(MOVE_TACKLE); }
|
||||
OPPONENT(SPECIES_CHARIZARD) { Speed(200); Moves(MOVE_FLAMETHROWER); SpAttack(317); SpDefense(207); MaxHP(297); } // Outspeends and 2HKOs Meganium
|
||||
OPPONENT(SPECIES_TYPHLOSION) { Speed(200); Moves(MOVE_FLAMETHROWER); SpAttack(317); SpDefense(207); MaxHP(297); } // Outspeends and 2HKOs Meganium
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_STEALTH_ROCK) ;}
|
||||
TURN { MOVE(player, MOVE_SURF) ; EXPECT_SEND_OUT(opponent, aiIsSmart ? 2 : 1); } // AI sends out Typhlosion to get the KO with the flag rather than Charizard
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Mid-battle switches prioritize type matchup + SE move, then type matchup")
|
||||
{
|
||||
u32 aiSmartSwitchFlags = 0;
|
||||
u32 move1;
|
||||
u32 move2;
|
||||
u32 expectedIndex;
|
||||
|
||||
PARAMETRIZE{ expectedIndex = 3; move1 = MOVE_TACKLE; move2 = MOVE_TACKLE; aiSmartSwitchFlags = 0; } // When not smart, AI will only switch in a defensive mon if it has a SE move, otherwise will just default to damage
|
||||
PARAMETRIZE{ expectedIndex = 1; move1 = MOVE_GIGA_DRAIN; move2 = MOVE_TACKLE; aiSmartSwitchFlags = 0; }
|
||||
PARAMETRIZE{ expectedIndex = 2; move1 = MOVE_TACKLE; move2 = MOVE_TACKLE; aiSmartSwitchFlags = AI_FLAG_SMART_MON_CHOICES; } // When smart, AI will prioritize SE move, but still switch in good type matchup without SE move
|
||||
PARAMETRIZE{ expectedIndex = 1; move1 = MOVE_GIGA_DRAIN; move2 = MOVE_TACKLE; aiSmartSwitchFlags = AI_FLAG_SMART_MON_CHOICES; }
|
||||
|
||||
GIVEN {
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiSmartSwitchFlags);
|
||||
PLAYER(SPECIES_MARSHTOMP) { Level(30); Moves(MOVE_MUD_BOMB, MOVE_WATER_GUN, MOVE_GROWL, MOVE_MUD_SHOT); Speed(5); }
|
||||
OPPONENT(SPECIES_PONYTA) { Level(1); Moves(MOVE_NONE); Speed(6); } // Forces switchout
|
||||
OPPONENT(SPECIES_TANGELA) { Level(30); Moves(move1); Speed(4); }
|
||||
OPPONENT(SPECIES_LOMBRE) { Level(30); Moves(move2); Speed(4); }
|
||||
OPPONENT(SPECIES_HARIYAMA) { Level(30); Moves(MOVE_VITAL_THROW); Speed(4); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_GROWL) ; EXPECT_SWITCH(opponent, expectedIndex); }
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Mid-battle switches prioritize defensive options")
|
||||
{
|
||||
GIVEN {
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_MON_CHOICES);
|
||||
PLAYER(SPECIES_SWELLOW) { Level(30); Moves(MOVE_WING_ATTACK, MOVE_BOOMBURST); Speed(5); }
|
||||
OPPONENT(SPECIES_PONYTA) { Level(1); Moves(MOVE_NONE); Speed(4); } // Forces switchout
|
||||
OPPONENT(SPECIES_ARON) { Level(30); Moves(MOVE_HEADBUTT); Speed(4); } // Mid battle, AI sends out Aron
|
||||
OPPONENT(SPECIES_ELECTRODE) { Level(30); Moves(MOVE_CHARGE_BEAM); Speed(6); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_WING_ATTACK) ; EXPECT_SWITCH(opponent, 1); }
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Post-KO switches prioritize offensive options")
|
||||
{
|
||||
GIVEN {
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_MON_CHOICES);
|
||||
PLAYER(SPECIES_SWELLOW) { Level(30); Moves(MOVE_WING_ATTACK, MOVE_BOOMBURST); Speed(5); }
|
||||
OPPONENT(SPECIES_PONYTA) { Level(1); Moves(MOVE_TACKLE); Speed(4); }
|
||||
OPPONENT(SPECIES_ARON) { Level(30); Moves(MOVE_HEADBUTT); Speed(4); } // Mid battle, AI sends out Aron
|
||||
OPPONENT(SPECIES_ELECTRODE) { Level(30); Moves(MOVE_CHARGE_BEAM); Speed(6); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_WING_ATTACK) ; EXPECT_SEND_OUT(opponent, 2); }
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue