sovereignx/test/battle/ai.c

958 lines
49 KiB
C
Raw Normal View History

#include "global.h"
#include "test/battle.h"
#include "battle_ai_util.h"
AI_SINGLE_BATTLE_TEST("AI gets baited by Protect Switch tactics") // This behavior is to be fixed.
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING);
PLAYER(SPECIES_STUNFISK);
PLAYER(SPECIES_PELIPPER);
OPPONENT(SPECIES_DARKRAI) { Moves(MOVE_TACKLE, MOVE_PECK, MOVE_EARTHQUAKE, MOVE_THUNDERBOLT); }
OPPONENT(SPECIES_SCIZOR) { Moves(MOVE_HYPER_BEAM, MOVE_FACADE, MOVE_GIGA_IMPACT, MOVE_EXTREME_SPEED); }
} WHEN {
TURN { MOVE(player, MOVE_PROTECT); EXPECT_MOVE(opponent, MOVE_EARTHQUAKE); } // E-quake
TURN { SWITCH(player, 1); EXPECT_MOVE(opponent, MOVE_EARTHQUAKE); } // E-quake
TURN { MOVE(player, MOVE_PROTECT); EXPECT_MOVE(opponent, MOVE_THUNDERBOLT); } // T-Bolt
TURN { SWITCH(player, 0); EXPECT_MOVE(opponent, MOVE_THUNDERBOLT); } // T-Bolt
TURN { MOVE(player, MOVE_PROTECT); EXPECT_MOVE(opponent, MOVE_EARTHQUAKE); } // E-quake
TURN { SWITCH(player, 1); EXPECT_MOVE(opponent, MOVE_EARTHQUAKE);} // E-quake
TURN { MOVE(player, MOVE_PROTECT); EXPECT_MOVE(opponent, MOVE_THUNDERBOLT); } // T-Bolt
}
}
AI_SINGLE_BATTLE_TEST("AI prefers Bubble over Water Gun if it's slower")
{
u32 speedPlayer, speedAi;
PARAMETRIZE { speedPlayer = 200; speedAi = 10; }
PARAMETRIZE { speedPlayer = 10; speedAi = 200; }
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_SCIZOR) { Speed(speedPlayer); }
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_WATER_GUN, MOVE_BUBBLE); Speed(speedAi); }
} WHEN {
if (speedPlayer > speedAi)
{
TURN { SCORE_GT(opponent, MOVE_BUBBLE, MOVE_WATER_GUN); }
TURN { SCORE_GT(opponent, MOVE_BUBBLE, MOVE_WATER_GUN); }
}
else
{
TURN { SCORE_EQ(opponent, MOVE_BUBBLE, MOVE_WATER_GUN); }
TURN { SCORE_EQ(opponent, MOVE_BUBBLE, MOVE_WATER_GUN); }
}
}
}
AI_SINGLE_BATTLE_TEST("AI prefers Water Gun over Bubble if it knows that foe has Contrary")
{
u32 abilityAI;
PARAMETRIZE { abilityAI = ABILITY_MOXIE; }
PARAMETRIZE { abilityAI = ABILITY_MOLD_BREAKER; } // Mold Breaker ignores Contrary.
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_SHUCKLE) { Ability(ABILITY_CONTRARY); }
OPPONENT(SPECIES_PINSIR) { Moves(MOVE_WATER_GUN, MOVE_BUBBLE); Ability(abilityAI); }
} WHEN {
TURN { MOVE(player, MOVE_DEFENSE_CURL); }
TURN { MOVE(player, MOVE_DEFENSE_CURL);
if (abilityAI == ABILITY_MOLD_BREAKER) { SCORE_EQ(opponent, MOVE_WATER_GUN, MOVE_BUBBLE); }
else { SCORE_GT(opponent, MOVE_WATER_GUN, MOVE_BUBBLE); }}
} SCENE {
MESSAGE("Shuckle's Defense fell!"); // Contrary activates
} THEN {
EXPECT(gBattleResources->aiData->abilities[B_POSITION_PLAYER_LEFT] == ABILITY_CONTRARY);
}
}
AI_SINGLE_BATTLE_TEST("AI prefers moves with better accuracy, but only if they both require the same number of hits to ko")
{
u16 move1 = MOVE_NONE, move2 = MOVE_NONE, move3 = MOVE_NONE, move4 = MOVE_NONE;
u16 hp, expectedMove, turns, abilityAtk, expectedMove2;
abilityAtk = ABILITY_NONE;
expectedMove2 = MOVE_NONE;
// Here it's a simple test, both Slam and Strength deal the same damage, but Strength always hits, whereas Slam often misses.
PARAMETRIZE { move1 = MOVE_SLAM; move2 = MOVE_STRENGTH; move3 = MOVE_TACKLE; hp = 490; expectedMove = MOVE_STRENGTH; turns = 4; }
PARAMETRIZE { move1 = MOVE_SLAM; move2 = MOVE_STRENGTH; move3 = MOVE_SWIFT; move4 = MOVE_TACKLE; hp = 365; expectedMove = MOVE_STRENGTH; turns = 3; }
PARAMETRIZE { move1 = MOVE_SLAM; move2 = MOVE_STRENGTH; move3 = MOVE_SWIFT; move4 = MOVE_TACKLE; hp = 245; expectedMove = MOVE_STRENGTH; turns = 2; }
PARAMETRIZE { move1 = MOVE_SLAM; move2 = MOVE_STRENGTH; move3 = MOVE_SWIFT; move4 = MOVE_TACKLE; hp = 125; expectedMove = MOVE_STRENGTH; turns = 1; }
// Mega Kick deals more damage, but can miss more often. Here, AI should choose Mega Kick if it can faint target in less number of turns than Strength. Otherwise, it should use Strength.
PARAMETRIZE { move1 = MOVE_MEGA_KICK; move2 = MOVE_STRENGTH; move3 = MOVE_SWIFT; move4 = MOVE_TACKLE; hp = 170; expectedMove = MOVE_MEGA_KICK; turns = 1; }
PARAMETRIZE { move1 = MOVE_MEGA_KICK; move2 = MOVE_STRENGTH; move3 = MOVE_SWIFT; move4 = MOVE_TACKLE; hp = 245; expectedMove = MOVE_STRENGTH; turns = 2; }
// Swift always hits and Guts has accuracy of 100%. Hustle lowers accuracy of all physical moves.
PARAMETRIZE { abilityAtk = ABILITY_HUSTLE; move1 = MOVE_MEGA_KICK; move2 = MOVE_STRENGTH; move3 = MOVE_SWIFT; move4 = MOVE_TACKLE; hp = 5; expectedMove = MOVE_SWIFT; turns = 1; }
PARAMETRIZE { abilityAtk = ABILITY_HUSTLE; move1 = MOVE_MEGA_KICK; move2 = MOVE_STRENGTH; move3 = MOVE_GUST; move4 = MOVE_TACKLE; hp = 5; expectedMove = MOVE_GUST; turns = 1; }
// Mega Kick and Slam both have lower accuracy. Gust and Tackle both have 100, so AI can choose either of them.
PARAMETRIZE { move1 = MOVE_MEGA_KICK; move2 = MOVE_SLAM; move3 = MOVE_TACKLE; move4 = MOVE_GUST; hp = 5; expectedMove = MOVE_GUST; expectedMove2 = MOVE_TACKLE; turns = 1; }
// All moves hit with No guard ability
PARAMETRIZE { move1 = MOVE_MEGA_KICK; move2 = MOVE_GUST; hp = 5; expectedMove = MOVE_MEGA_KICK; expectedMove2 = MOVE_GUST; turns = 1; }
// Tests to compare move that always hits and a beneficial effect. A move with higher acc should be chosen in this case.
PARAMETRIZE { move1 = MOVE_SHOCK_WAVE; move2 = MOVE_ICY_WIND; hp = 5; expectedMove = MOVE_SHOCK_WAVE; turns = 1; }
PARAMETRIZE { move1 = MOVE_SHOCK_WAVE; move2 = MOVE_ICY_WIND; move3 = MOVE_THUNDERBOLT; hp = 5; expectedMove = MOVE_SHOCK_WAVE; expectedMove2 = MOVE_THUNDERBOLT; turns = 1; }
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET) { HP(hp); }
PLAYER(SPECIES_WOBBUFFET);
Move data unification (#3999) * Made gBattleMoves handle the InGame name and description of battle moves No more multiple arrays in separate, individual files. Note: -Keep an eye on Task_LearnedMove. * Reintroduced move names Misc: -Fixed Trick-or-Treat and Light of Ruin's expanded names. -Introduced a new field for Z-Move names, and a constant for their name length. -Added a few TODOs to GetBattleMoveName. -Updated GetMaxMoveName and GetZMoveName. There's no reason not to let GetBattleMoveName handle everything on its own. * Updated GetBattleMoveName to handle Z-Move Names Misc: -Removed pointless TODO about MOVE_NAME_LENGTH. -The compiler doesn't allow to have a move name with a value higher than MOVE_NAME_LENGTH, therefore it's pointless to worry about it. * Fixed a couple of expanded move names * Removed zMoveName variable of struct BattleMove and extended the name variable's size * Ditched no longer used MOVE_NAME_LENGTH constant * Corrected the names of the max moves I should have done this after updating the size of the name variable of the struct BattleMove, but I didn't think about it at all until Cancer Fairy indirectly gave me the idea. * Fixed U-turn's name * Brought back MOVE_NAME_LENGTH I think it doesn't make sense to have a Z_MOVE_NAME_LENGTH because the length in question is used for all battle moves, not just the Z-Moves. * Introduced a union for Move/Z-Move names in the struct BattleMove * Fixed the union for gBattleMoves move names Also updated GetBattleMoveName to properly handle Max Move names. Also also renamed the "zMoveName" variable to "bigMoveName" which better reflects its purpose. Z-Move names weren't the only thing it covered, since it also handles Max Move names. * Removed deprecated GetZMoveName and GetMaxMoveName * Reintroduced mention to gMoveNames in sGFRomHeader * Fixed move names and ported move descriptions * Fused the struct ContestMove into the struct BattleMove * Removed no longer used Z_MOVE_NAME_LENGTH constant * Renamed the struct BattleMove's bigMoveName variable and introduced macros to prettify move names * Reintroduced the contest parameters for Pokémon moves * Renamed gBattleMoves to gMovesInfo This is consistent with gSpeciesInfo, the array that contains most of the species data. * Renamed the BattleMove struct to MovesInfo This is consistent with the struct SpeciesInfo, which contains the variables used by the gSpeciesInfo array. * Removed empty lines separating battle params from contest params in gMovesInfo * Renamed MovesInfo to MoveInfo * Added Cancer Fairy's HANDLE_EXPANDED_MOVE_NAME macro Used to handle moves with expanded names in a more comfortable manner. Also fixed Trick-or-Treat's expanded name. * Renamed GetBattleMoveName to GetMoveName * Added a comment pointing out that the shared move descriptions are shared move descriptions * Re-aligned one of the escape characters of CHECK_MOVE_FLAG * Renamed the battle_moves.h file to moves_info.h instead for consistency's sake * Applied Eduardo's adjustments * Using compound string for regular move names as well, saving 1180 bytes and making their use consistent * Move description formatting * Updated Pursuit test after merge * Renamed the BATTLE_CATEGORY constants to DAMAGE_CATEGORY --------- Co-authored-by: Nephrite <thechurchofcage@gmail.com> Co-authored-by: Bassoonian <iasperbassoonian@gmail.com> Co-authored-by: Eduardo Quezada D'Ottone <eduardo602002@gmail.com>
2024-01-29 11:51:32 +00:00
ASSUME(gMovesInfo[MOVE_SWIFT].accuracy == 0);
ASSUME(gMovesInfo[MOVE_SLAM].power == gMovesInfo[MOVE_STRENGTH].power);
ASSUME(gMovesInfo[MOVE_MEGA_KICK].power > gMovesInfo[MOVE_STRENGTH].power);
ASSUME(gMovesInfo[MOVE_SLAM].accuracy < gMovesInfo[MOVE_STRENGTH].accuracy);
ASSUME(gMovesInfo[MOVE_MEGA_KICK].accuracy < gMovesInfo[MOVE_STRENGTH].accuracy);
ASSUME(gMovesInfo[MOVE_TACKLE].accuracy == 100);
ASSUME(gMovesInfo[MOVE_GUST].accuracy == 100);
ASSUME(gMovesInfo[MOVE_SHOCK_WAVE].accuracy == 0);
ASSUME(gMovesInfo[MOVE_THUNDERBOLT].accuracy == 100);
ASSUME(gMovesInfo[MOVE_ICY_WIND].accuracy != 100);
ASSUME(gMovesInfo[MOVE_SLAM].category == DAMAGE_CATEGORY_PHYSICAL);
ASSUME(gMovesInfo[MOVE_STRENGTH].category == DAMAGE_CATEGORY_PHYSICAL);
ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL);
ASSUME(gMovesInfo[MOVE_MEGA_KICK].category == DAMAGE_CATEGORY_PHYSICAL);
ASSUME(gMovesInfo[MOVE_SWIFT].category == DAMAGE_CATEGORY_SPECIAL);
ASSUME(gMovesInfo[MOVE_SHOCK_WAVE].category == DAMAGE_CATEGORY_SPECIAL);
ASSUME(gMovesInfo[MOVE_ICY_WIND].category == DAMAGE_CATEGORY_SPECIAL);
ASSUME(gMovesInfo[MOVE_THUNDERBOLT].category == DAMAGE_CATEGORY_SPECIAL);
ASSUME(gMovesInfo[MOVE_GUST].category == DAMAGE_CATEGORY_SPECIAL);
OPPONENT(SPECIES_EXPLOUD) { Moves(move1, move2, move3, move4); Ability(abilityAtk); SpAttack(50); } // Low Sp.Atk, so Swift deals less damage than Strength.
} WHEN {
switch (turns)
{
case 1:
if (expectedMove2 != MOVE_NONE) {
TURN { EXPECT_MOVES(opponent, expectedMove, expectedMove2); SEND_OUT(player, 1); }
}
else {
TURN { EXPECT_MOVE(opponent, expectedMove); SEND_OUT(player, 1); }
}
break;
case 2:
TURN { EXPECT_MOVE(opponent, expectedMove); }
TURN { EXPECT_MOVE(opponent, expectedMove); SEND_OUT(player, 1); }
break;
case 3:
TURN { EXPECT_MOVE(opponent, expectedMove); }
TURN { EXPECT_MOVE(opponent, expectedMove); }
TURN { EXPECT_MOVE(opponent, expectedMove); SEND_OUT(player, 1); }
break;
case 4:
TURN { EXPECT_MOVE(opponent, expectedMove); }
TURN { EXPECT_MOVE(opponent, expectedMove); }
TURN { EXPECT_MOVE(opponent, expectedMove); }
TURN { EXPECT_MOVE(opponent, expectedMove); SEND_OUT(player, 1); }
break;
}
} SCENE {
MESSAGE("Wobbuffet fainted!");
}
}
AI_SINGLE_BATTLE_TEST("AI prefers moves which deal more damage instead of moves which are super-effective but deal less damage")
{
u8 turns = 0;
u16 move1 = MOVE_NONE, move2 = MOVE_NONE, move3 = MOVE_NONE, move4 = MOVE_NONE;
u16 expectedMove, abilityAtk, abilityDef;
abilityAtk = ABILITY_NONE;
// Scald and Poison Jab take 3 hits, Waterfall takes 2.
PARAMETRIZE { move1 = MOVE_WATERFALL; move2 = MOVE_SCALD; move3 = MOVE_POISON_JAB; move4 = MOVE_WATER_GUN; expectedMove = MOVE_WATERFALL; turns = 2; }
// Poison Jab takes 3 hits, Water gun 5. Immunity so there's no poison chip damage.
PARAMETRIZE { move1 = MOVE_POISON_JAB; move2 = MOVE_WATER_GUN; expectedMove = MOVE_POISON_JAB; abilityDef = ABILITY_IMMUNITY; turns = 3; }
GIVEN {
Move data unification (#3999) * Made gBattleMoves handle the InGame name and description of battle moves No more multiple arrays in separate, individual files. Note: -Keep an eye on Task_LearnedMove. * Reintroduced move names Misc: -Fixed Trick-or-Treat and Light of Ruin's expanded names. -Introduced a new field for Z-Move names, and a constant for their name length. -Added a few TODOs to GetBattleMoveName. -Updated GetMaxMoveName and GetZMoveName. There's no reason not to let GetBattleMoveName handle everything on its own. * Updated GetBattleMoveName to handle Z-Move Names Misc: -Removed pointless TODO about MOVE_NAME_LENGTH. -The compiler doesn't allow to have a move name with a value higher than MOVE_NAME_LENGTH, therefore it's pointless to worry about it. * Fixed a couple of expanded move names * Removed zMoveName variable of struct BattleMove and extended the name variable's size * Ditched no longer used MOVE_NAME_LENGTH constant * Corrected the names of the max moves I should have done this after updating the size of the name variable of the struct BattleMove, but I didn't think about it at all until Cancer Fairy indirectly gave me the idea. * Fixed U-turn's name * Brought back MOVE_NAME_LENGTH I think it doesn't make sense to have a Z_MOVE_NAME_LENGTH because the length in question is used for all battle moves, not just the Z-Moves. * Introduced a union for Move/Z-Move names in the struct BattleMove * Fixed the union for gBattleMoves move names Also updated GetBattleMoveName to properly handle Max Move names. Also also renamed the "zMoveName" variable to "bigMoveName" which better reflects its purpose. Z-Move names weren't the only thing it covered, since it also handles Max Move names. * Removed deprecated GetZMoveName and GetMaxMoveName * Reintroduced mention to gMoveNames in sGFRomHeader * Fixed move names and ported move descriptions * Fused the struct ContestMove into the struct BattleMove * Removed no longer used Z_MOVE_NAME_LENGTH constant * Renamed the struct BattleMove's bigMoveName variable and introduced macros to prettify move names * Reintroduced the contest parameters for Pokémon moves * Renamed gBattleMoves to gMovesInfo This is consistent with gSpeciesInfo, the array that contains most of the species data. * Renamed the BattleMove struct to MovesInfo This is consistent with the struct SpeciesInfo, which contains the variables used by the gSpeciesInfo array. * Removed empty lines separating battle params from contest params in gMovesInfo * Renamed MovesInfo to MoveInfo * Added Cancer Fairy's HANDLE_EXPANDED_MOVE_NAME macro Used to handle moves with expanded names in a more comfortable manner. Also fixed Trick-or-Treat's expanded name. * Renamed GetBattleMoveName to GetMoveName * Added a comment pointing out that the shared move descriptions are shared move descriptions * Re-aligned one of the escape characters of CHECK_MOVE_FLAG * Renamed the battle_moves.h file to moves_info.h instead for consistency's sake * Applied Eduardo's adjustments * Using compound string for regular move names as well, saving 1180 bytes and making their use consistent * Move description formatting * Updated Pursuit test after merge * Renamed the BATTLE_CATEGORY constants to DAMAGE_CATEGORY --------- Co-authored-by: Nephrite <thechurchofcage@gmail.com> Co-authored-by: Bassoonian <iasperbassoonian@gmail.com> Co-authored-by: Eduardo Quezada D'Ottone <eduardo602002@gmail.com>
2024-01-29 11:51:32 +00:00
ASSUME(gMovesInfo[MOVE_WATERFALL].category == DAMAGE_CATEGORY_PHYSICAL);
ASSUME(gMovesInfo[MOVE_SCALD].category == DAMAGE_CATEGORY_SPECIAL);
ASSUME(gMovesInfo[MOVE_POISON_JAB].category == DAMAGE_CATEGORY_PHYSICAL);
ASSUME(gMovesInfo[MOVE_WATER_GUN].category == DAMAGE_CATEGORY_SPECIAL);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_TYPHLOSION) { Ability(abilityDef); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_NIDOQUEEN) { Moves(move1, move2, move3, move4); Ability(abilityAtk); }
} WHEN {
switch (turns)
{
case 2:
TURN { EXPECT_MOVE(opponent, expectedMove); }
TURN { EXPECT_MOVE(opponent, expectedMove); SEND_OUT(player, 1); }
break;
case 3:
TURN { EXPECT_MOVE(opponent, expectedMove); }
TURN { EXPECT_MOVE(opponent, expectedMove); }
TURN { EXPECT_MOVE(opponent, expectedMove); SEND_OUT(player, 1); }
break;
}
} SCENE {
MESSAGE("Typhlosion fainted!");
}
}
AI_SINGLE_BATTLE_TEST("AI prefers Earthquake over Drill Run if both require the same number of hits to ko")
{
// Drill Run has less accuracy than E-quake, but can score a higher crit. However the chance is too small, so AI should ignore it.
GIVEN {
Move data unification (#3999) * Made gBattleMoves handle the InGame name and description of battle moves No more multiple arrays in separate, individual files. Note: -Keep an eye on Task_LearnedMove. * Reintroduced move names Misc: -Fixed Trick-or-Treat and Light of Ruin's expanded names. -Introduced a new field for Z-Move names, and a constant for their name length. -Added a few TODOs to GetBattleMoveName. -Updated GetMaxMoveName and GetZMoveName. There's no reason not to let GetBattleMoveName handle everything on its own. * Updated GetBattleMoveName to handle Z-Move Names Misc: -Removed pointless TODO about MOVE_NAME_LENGTH. -The compiler doesn't allow to have a move name with a value higher than MOVE_NAME_LENGTH, therefore it's pointless to worry about it. * Fixed a couple of expanded move names * Removed zMoveName variable of struct BattleMove and extended the name variable's size * Ditched no longer used MOVE_NAME_LENGTH constant * Corrected the names of the max moves I should have done this after updating the size of the name variable of the struct BattleMove, but I didn't think about it at all until Cancer Fairy indirectly gave me the idea. * Fixed U-turn's name * Brought back MOVE_NAME_LENGTH I think it doesn't make sense to have a Z_MOVE_NAME_LENGTH because the length in question is used for all battle moves, not just the Z-Moves. * Introduced a union for Move/Z-Move names in the struct BattleMove * Fixed the union for gBattleMoves move names Also updated GetBattleMoveName to properly handle Max Move names. Also also renamed the "zMoveName" variable to "bigMoveName" which better reflects its purpose. Z-Move names weren't the only thing it covered, since it also handles Max Move names. * Removed deprecated GetZMoveName and GetMaxMoveName * Reintroduced mention to gMoveNames in sGFRomHeader * Fixed move names and ported move descriptions * Fused the struct ContestMove into the struct BattleMove * Removed no longer used Z_MOVE_NAME_LENGTH constant * Renamed the struct BattleMove's bigMoveName variable and introduced macros to prettify move names * Reintroduced the contest parameters for Pokémon moves * Renamed gBattleMoves to gMovesInfo This is consistent with gSpeciesInfo, the array that contains most of the species data. * Renamed the BattleMove struct to MovesInfo This is consistent with the struct SpeciesInfo, which contains the variables used by the gSpeciesInfo array. * Removed empty lines separating battle params from contest params in gMovesInfo * Renamed MovesInfo to MoveInfo * Added Cancer Fairy's HANDLE_EXPANDED_MOVE_NAME macro Used to handle moves with expanded names in a more comfortable manner. Also fixed Trick-or-Treat's expanded name. * Renamed GetBattleMoveName to GetMoveName * Added a comment pointing out that the shared move descriptions are shared move descriptions * Re-aligned one of the escape characters of CHECK_MOVE_FLAG * Renamed the battle_moves.h file to moves_info.h instead for consistency's sake * Applied Eduardo's adjustments * Using compound string for regular move names as well, saving 1180 bytes and making their use consistent * Move description formatting * Updated Pursuit test after merge * Renamed the BATTLE_CATEGORY constants to DAMAGE_CATEGORY --------- Co-authored-by: Nephrite <thechurchofcage@gmail.com> Co-authored-by: Bassoonian <iasperbassoonian@gmail.com> Co-authored-by: Eduardo Quezada D'Ottone <eduardo602002@gmail.com>
2024-01-29 11:51:32 +00:00
ASSUME(gMovesInfo[MOVE_EARTHQUAKE].category == DAMAGE_CATEGORY_PHYSICAL); // Added because Geodude has to KO Typhlosion
ASSUME(gMovesInfo[MOVE_DRILL_RUN].category == DAMAGE_CATEGORY_PHYSICAL); // Added because Geodude has to KO Typhlosion
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_TYPHLOSION);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_GEODUDE) { Moves(MOVE_EARTHQUAKE, MOVE_DRILL_RUN); }
} WHEN {
TURN { EXPECT_MOVE(opponent, MOVE_EARTHQUAKE); }
TURN { EXPECT_MOVE(opponent, MOVE_EARTHQUAKE); SEND_OUT(player, 1); }
}
SCENE {
MESSAGE("Typhlosion fainted!");
}
}
AI_SINGLE_BATTLE_TEST("AI prefers a weaker move over a one with a downside effect if both require the same number of hits to ko")
{
u16 move1 = MOVE_NONE, move2 = MOVE_NONE, move3 = MOVE_NONE, move4 = MOVE_NONE;
u16 hp, expectedMove, turns;
// Both moves require the same number of turns but Flamethrower will be chosen over Overheat (powerful effect)
PARAMETRIZE { move1 = MOVE_OVERHEAT; move2 = MOVE_FLAMETHROWER; hp = 300; expectedMove = MOVE_FLAMETHROWER; turns = 2; }
// Overheat kill in least amount of turns
PARAMETRIZE { move1 = MOVE_OVERHEAT; move2 = MOVE_FLAMETHROWER; hp = 250; expectedMove = MOVE_OVERHEAT; turns = 1; }
GIVEN {
Move data unification (#3999) * Made gBattleMoves handle the InGame name and description of battle moves No more multiple arrays in separate, individual files. Note: -Keep an eye on Task_LearnedMove. * Reintroduced move names Misc: -Fixed Trick-or-Treat and Light of Ruin's expanded names. -Introduced a new field for Z-Move names, and a constant for their name length. -Added a few TODOs to GetBattleMoveName. -Updated GetMaxMoveName and GetZMoveName. There's no reason not to let GetBattleMoveName handle everything on its own. * Updated GetBattleMoveName to handle Z-Move Names Misc: -Removed pointless TODO about MOVE_NAME_LENGTH. -The compiler doesn't allow to have a move name with a value higher than MOVE_NAME_LENGTH, therefore it's pointless to worry about it. * Fixed a couple of expanded move names * Removed zMoveName variable of struct BattleMove and extended the name variable's size * Ditched no longer used MOVE_NAME_LENGTH constant * Corrected the names of the max moves I should have done this after updating the size of the name variable of the struct BattleMove, but I didn't think about it at all until Cancer Fairy indirectly gave me the idea. * Fixed U-turn's name * Brought back MOVE_NAME_LENGTH I think it doesn't make sense to have a Z_MOVE_NAME_LENGTH because the length in question is used for all battle moves, not just the Z-Moves. * Introduced a union for Move/Z-Move names in the struct BattleMove * Fixed the union for gBattleMoves move names Also updated GetBattleMoveName to properly handle Max Move names. Also also renamed the "zMoveName" variable to "bigMoveName" which better reflects its purpose. Z-Move names weren't the only thing it covered, since it also handles Max Move names. * Removed deprecated GetZMoveName and GetMaxMoveName * Reintroduced mention to gMoveNames in sGFRomHeader * Fixed move names and ported move descriptions * Fused the struct ContestMove into the struct BattleMove * Removed no longer used Z_MOVE_NAME_LENGTH constant * Renamed the struct BattleMove's bigMoveName variable and introduced macros to prettify move names * Reintroduced the contest parameters for Pokémon moves * Renamed gBattleMoves to gMovesInfo This is consistent with gSpeciesInfo, the array that contains most of the species data. * Renamed the BattleMove struct to MovesInfo This is consistent with the struct SpeciesInfo, which contains the variables used by the gSpeciesInfo array. * Removed empty lines separating battle params from contest params in gMovesInfo * Renamed MovesInfo to MoveInfo * Added Cancer Fairy's HANDLE_EXPANDED_MOVE_NAME macro Used to handle moves with expanded names in a more comfortable manner. Also fixed Trick-or-Treat's expanded name. * Renamed GetBattleMoveName to GetMoveName * Added a comment pointing out that the shared move descriptions are shared move descriptions * Re-aligned one of the escape characters of CHECK_MOVE_FLAG * Renamed the battle_moves.h file to moves_info.h instead for consistency's sake * Applied Eduardo's adjustments * Using compound string for regular move names as well, saving 1180 bytes and making their use consistent * Move description formatting * Updated Pursuit test after merge * Renamed the BATTLE_CATEGORY constants to DAMAGE_CATEGORY --------- Co-authored-by: Nephrite <thechurchofcage@gmail.com> Co-authored-by: Bassoonian <iasperbassoonian@gmail.com> Co-authored-by: Eduardo Quezada D'Ottone <eduardo602002@gmail.com>
2024-01-29 11:51:32 +00:00
ASSUME(gMovesInfo[MOVE_FLAMETHROWER].category == DAMAGE_CATEGORY_SPECIAL); // Added because Typhlosion has to KO Wobbuffet
ASSUME(gMovesInfo[MOVE_OVERHEAT].category == DAMAGE_CATEGORY_SPECIAL); // Added because Typhlosion has to KO Wobbuffet
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET) { HP(hp); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_TYPHLOSION) { Moves(move1, move2, move3, move4); }
} WHEN {
switch (turns)
{
case 1:
TURN { EXPECT_MOVE(opponent, expectedMove); SEND_OUT(player, 1); }
break;
case 2:
TURN { EXPECT_MOVE(opponent, expectedMove); }
TURN { EXPECT_MOVE(opponent, expectedMove); SEND_OUT(player, 1); }
break;
}
} SCENE {
MESSAGE("Wobbuffet fainted!");
}
}
AI_SINGLE_BATTLE_TEST("AI prefers moves with the best possible score, chosen randomly if tied")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET) { HP(5); };
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_THUNDERBOLT, MOVE_SLUDGE_BOMB, MOVE_TAKE_DOWN); }
} WHEN {
TURN { EXPECT_MOVES(opponent, MOVE_THUNDERBOLT, MOVE_SLUDGE_BOMB); SEND_OUT(player, 1); }
}
SCENE {
MESSAGE("Wobbuffet fainted!");
}
}
AI_SINGLE_BATTLE_TEST("AI can choose a status move that boosts the attack by two")
{
GIVEN {
Move data unification (#3999) * Made gBattleMoves handle the InGame name and description of battle moves No more multiple arrays in separate, individual files. Note: -Keep an eye on Task_LearnedMove. * Reintroduced move names Misc: -Fixed Trick-or-Treat and Light of Ruin's expanded names. -Introduced a new field for Z-Move names, and a constant for their name length. -Added a few TODOs to GetBattleMoveName. -Updated GetMaxMoveName and GetZMoveName. There's no reason not to let GetBattleMoveName handle everything on its own. * Updated GetBattleMoveName to handle Z-Move Names Misc: -Removed pointless TODO about MOVE_NAME_LENGTH. -The compiler doesn't allow to have a move name with a value higher than MOVE_NAME_LENGTH, therefore it's pointless to worry about it. * Fixed a couple of expanded move names * Removed zMoveName variable of struct BattleMove and extended the name variable's size * Ditched no longer used MOVE_NAME_LENGTH constant * Corrected the names of the max moves I should have done this after updating the size of the name variable of the struct BattleMove, but I didn't think about it at all until Cancer Fairy indirectly gave me the idea. * Fixed U-turn's name * Brought back MOVE_NAME_LENGTH I think it doesn't make sense to have a Z_MOVE_NAME_LENGTH because the length in question is used for all battle moves, not just the Z-Moves. * Introduced a union for Move/Z-Move names in the struct BattleMove * Fixed the union for gBattleMoves move names Also updated GetBattleMoveName to properly handle Max Move names. Also also renamed the "zMoveName" variable to "bigMoveName" which better reflects its purpose. Z-Move names weren't the only thing it covered, since it also handles Max Move names. * Removed deprecated GetZMoveName and GetMaxMoveName * Reintroduced mention to gMoveNames in sGFRomHeader * Fixed move names and ported move descriptions * Fused the struct ContestMove into the struct BattleMove * Removed no longer used Z_MOVE_NAME_LENGTH constant * Renamed the struct BattleMove's bigMoveName variable and introduced macros to prettify move names * Reintroduced the contest parameters for Pokémon moves * Renamed gBattleMoves to gMovesInfo This is consistent with gSpeciesInfo, the array that contains most of the species data. * Renamed the BattleMove struct to MovesInfo This is consistent with the struct SpeciesInfo, which contains the variables used by the gSpeciesInfo array. * Removed empty lines separating battle params from contest params in gMovesInfo * Renamed MovesInfo to MoveInfo * Added Cancer Fairy's HANDLE_EXPANDED_MOVE_NAME macro Used to handle moves with expanded names in a more comfortable manner. Also fixed Trick-or-Treat's expanded name. * Renamed GetBattleMoveName to GetMoveName * Added a comment pointing out that the shared move descriptions are shared move descriptions * Re-aligned one of the escape characters of CHECK_MOVE_FLAG * Renamed the battle_moves.h file to moves_info.h instead for consistency's sake * Applied Eduardo's adjustments * Using compound string for regular move names as well, saving 1180 bytes and making their use consistent * Move description formatting * Updated Pursuit test after merge * Renamed the BATTLE_CATEGORY constants to DAMAGE_CATEGORY --------- Co-authored-by: Nephrite <thechurchofcage@gmail.com> Co-authored-by: Bassoonian <iasperbassoonian@gmail.com> Co-authored-by: Eduardo Quezada D'Ottone <eduardo602002@gmail.com>
2024-01-29 11:51:32 +00:00
ASSUME(gMovesInfo[MOVE_STRENGTH].category == DAMAGE_CATEGORY_PHYSICAL);
ASSUME(gMovesInfo[MOVE_HORN_ATTACK].category == DAMAGE_CATEGORY_PHYSICAL);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET) { HP(277); };
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_KANGASKHAN) { Moves(MOVE_STRENGTH, MOVE_HORN_ATTACK, MOVE_SWORDS_DANCE); }
} WHEN {
TURN { EXPECT_MOVES(opponent, MOVE_STRENGTH, MOVE_SWORDS_DANCE); }
TURN { EXPECT_MOVE(opponent, MOVE_STRENGTH); SEND_OUT(player, 1); }
}
}
AI_SINGLE_BATTLE_TEST("AI chooses the safest option to faint the target, taking into account accuracy and move effect")
{
u16 move1 = MOVE_NONE, move2 = MOVE_NONE, move3 = MOVE_NONE, move4 = MOVE_NONE;
u16 expectedMove, expectedMove2 = MOVE_NONE;
u16 abilityAtk = ABILITY_NONE, holdItemAtk = ITEM_NONE;
// Psychic is not very effective, but always hits. Solarbeam requires a charging turn, Double Edge has recoil and Focus Blast can miss;
PARAMETRIZE { abilityAtk = ABILITY_STURDY; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SOLAR_BEAM; move3 = MOVE_PSYCHIC; move4 = MOVE_DOUBLE_EDGE; expectedMove = MOVE_PSYCHIC; }
// Same as above, but ai mon has rock head ability, so it can use Double Edge without taking recoil damage. Psychic can also lower Special Defense,
// but because it faints the target it doesn't matter.
PARAMETRIZE { abilityAtk = ABILITY_ROCK_HEAD; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SOLAR_BEAM; move3 = MOVE_PSYCHIC; move4 = MOVE_DOUBLE_EDGE;
expectedMove = MOVE_PSYCHIC; expectedMove2 = MOVE_DOUBLE_EDGE; }
// This time it's Solarbeam + Psychic, because the weather is sunny.
PARAMETRIZE { abilityAtk = ABILITY_DROUGHT; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SOLAR_BEAM; move3 = MOVE_PSYCHIC; move4 = MOVE_DOUBLE_EDGE;
expectedMove = MOVE_PSYCHIC; expectedMove2 = MOVE_SOLAR_BEAM; }
// Psychic and Solar Beam are chosen because user is holding Power Herb
PARAMETRIZE { abilityAtk = ABILITY_STURDY; holdItemAtk = ITEM_POWER_HERB; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SOLAR_BEAM; move3 = MOVE_PSYCHIC; move4 = MOVE_DOUBLE_EDGE;
expectedMove = MOVE_PSYCHIC; expectedMove2 = MOVE_SOLAR_BEAM; }
// Skull Bash is chosen because it's the most accurate and is holding Power Herb
PARAMETRIZE { abilityAtk = ABILITY_STURDY; holdItemAtk = ITEM_POWER_HERB; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SKULL_BASH; move3 = MOVE_SLAM; move4 = MOVE_DOUBLE_EDGE;
expectedMove = MOVE_SKULL_BASH; }
Secondary effects overhaul minor follow-up (#4062) * settwoturnstring command * Unified two-turn attacks and Meteor Beam To do: Solar Beam * Solar Beam Also fixed various function, removed EFFECT_GUST (who knows why that exists?) * Updated Solar Beam + tests * Redid two turn move + animations logic Removed pointless various function; to do: remove Skull Bash, fix AI test * Removed now-pointless flag * Removed Skull Bash And temporarily commented out failing AI tests * Removed Sky Uppercut effect Not sure when or why this was ever necessary * Removed BattleScript_EffectSemiInvulnerable Now uses BattleScript_EffectTwoTurnsAttack. Kept the effect; used the argument field to determine which STATUS3 such moves should apply but added a function to jump over weather checks in BattleScript_EffectTwoTurnsAttack if the current move is semi-invulnerable (since the instant-fire weather check and STATUS3 use the same field) * Applied review changes * Replaced VARIOUS with callnative Tried to fix test but couldn't :/ wtf is going on * Fixed one AI test Cant fix the other... * Added KNOWN_FAILING to failing AI tests Separated them out into their own test * Optimised script, overhauled charge turn string setting Condensed multiple confusing macros into one, jumpifweathercheckchargeeffects. Script now tweaked and trimmed, string ids for charge turns now added to argument along with status3 (thanks to compression macro) and instant-fire-weather for semi-invulnerable and two-turn moves respectively. Also introduced a savedStringId in gBattleScripting to make string selection work. * Unified two turn move tests + minor corrections * Added semi-invulnerable move tests Set the Razor Wind test to known failing - something to do with its animation? --------- Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com>
2024-02-04 22:02:59 +00:00
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET) { HP(5); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_GEODUDE) { Moves(move1, move2, move3, move4); Ability(abilityAtk); Item(holdItemAtk); }
} WHEN {
TURN { if (expectedMove2 == MOVE_NONE) { EXPECT_MOVE(opponent, expectedMove); SEND_OUT(player, 1); }
else {EXPECT_MOVES(opponent, expectedMove, expectedMove2); SCORE_EQ(opponent, expectedMove, expectedMove2); SEND_OUT(player, 1);}
}
}
SCENE {
MESSAGE("Wobbuffet fainted!");
}
}
AI_SINGLE_BATTLE_TEST("AI chooses the safest option to faint the target, taking into account accuracy and move effect failing")
{
u16 move1 = MOVE_NONE, move2 = MOVE_NONE, move3 = MOVE_NONE, move4 = MOVE_NONE;
u16 expectedMove, expectedMove2 = MOVE_NONE;
u16 abilityAtk = ABILITY_NONE, holdItemAtk = ITEM_NONE;
// Fiery Dance and Skull Bash are chosen because user is holding Power Herb
PARAMETRIZE { abilityAtk = ABILITY_STURDY; holdItemAtk = ITEM_POWER_HERB; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SKULL_BASH; move3 = MOVE_FIERY_DANCE; move4 = MOVE_DOUBLE_EDGE;
expectedMove = MOVE_FIERY_DANCE; expectedMove2 = MOVE_SKULL_BASH; }
// Crabhammer is chosen even if Skull Bash is more accurate, the user has no Power Herb
PARAMETRIZE { abilityAtk = ABILITY_STURDY; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SKULL_BASH; move3 = MOVE_SLAM; move4 = MOVE_CRABHAMMER;
expectedMove = MOVE_CRABHAMMER; }
Secondary effects overhaul minor follow-up (#4062) * settwoturnstring command * Unified two-turn attacks and Meteor Beam To do: Solar Beam * Solar Beam Also fixed various function, removed EFFECT_GUST (who knows why that exists?) * Updated Solar Beam + tests * Redid two turn move + animations logic Removed pointless various function; to do: remove Skull Bash, fix AI test * Removed now-pointless flag * Removed Skull Bash And temporarily commented out failing AI tests * Removed Sky Uppercut effect Not sure when or why this was ever necessary * Removed BattleScript_EffectSemiInvulnerable Now uses BattleScript_EffectTwoTurnsAttack. Kept the effect; used the argument field to determine which STATUS3 such moves should apply but added a function to jump over weather checks in BattleScript_EffectTwoTurnsAttack if the current move is semi-invulnerable (since the instant-fire weather check and STATUS3 use the same field) * Applied review changes * Replaced VARIOUS with callnative Tried to fix test but couldn't :/ wtf is going on * Fixed one AI test Cant fix the other... * Added KNOWN_FAILING to failing AI tests Separated them out into their own test * Optimised script, overhauled charge turn string setting Condensed multiple confusing macros into one, jumpifweathercheckchargeeffects. Script now tweaked and trimmed, string ids for charge turns now added to argument along with status3 (thanks to compression macro) and instant-fire-weather for semi-invulnerable and two-turn moves respectively. Also introduced a savedStringId in gBattleScripting to make string selection work. * Unified two turn move tests + minor corrections * Added semi-invulnerable move tests Set the Razor Wind test to known failing - something to do with its animation? --------- Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com>
2024-02-04 22:02:59 +00:00
KNOWN_FAILING;
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET) { HP(5); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_GEODUDE) { Moves(move1, move2, move3, move4); Ability(abilityAtk); Item(holdItemAtk); }
} WHEN {
TURN { if (expectedMove2 == MOVE_NONE) { EXPECT_MOVE(opponent, expectedMove); SEND_OUT(player, 1); }
else {EXPECT_MOVES(opponent, expectedMove, expectedMove2); SCORE_EQ(opponent, expectedMove, expectedMove2); SEND_OUT(player, 1);}
}
}
SCENE {
MESSAGE("Wobbuffet fainted!");
}
}
AI_SINGLE_BATTLE_TEST("AI won't use Solar Beam if there is no Sun up or the user is not holding Power Herb")
{
u16 abilityAtk = ABILITY_NONE;
u16 holdItemAtk = ITEM_NONE;
PARAMETRIZE { abilityAtk = ABILITY_DROUGHT; }
PARAMETRIZE { holdItemAtk = ITEM_POWER_HERB; }
PARAMETRIZE { }
GIVEN {
Move data unification (#3999) * Made gBattleMoves handle the InGame name and description of battle moves No more multiple arrays in separate, individual files. Note: -Keep an eye on Task_LearnedMove. * Reintroduced move names Misc: -Fixed Trick-or-Treat and Light of Ruin's expanded names. -Introduced a new field for Z-Move names, and a constant for their name length. -Added a few TODOs to GetBattleMoveName. -Updated GetMaxMoveName and GetZMoveName. There's no reason not to let GetBattleMoveName handle everything on its own. * Updated GetBattleMoveName to handle Z-Move Names Misc: -Removed pointless TODO about MOVE_NAME_LENGTH. -The compiler doesn't allow to have a move name with a value higher than MOVE_NAME_LENGTH, therefore it's pointless to worry about it. * Fixed a couple of expanded move names * Removed zMoveName variable of struct BattleMove and extended the name variable's size * Ditched no longer used MOVE_NAME_LENGTH constant * Corrected the names of the max moves I should have done this after updating the size of the name variable of the struct BattleMove, but I didn't think about it at all until Cancer Fairy indirectly gave me the idea. * Fixed U-turn's name * Brought back MOVE_NAME_LENGTH I think it doesn't make sense to have a Z_MOVE_NAME_LENGTH because the length in question is used for all battle moves, not just the Z-Moves. * Introduced a union for Move/Z-Move names in the struct BattleMove * Fixed the union for gBattleMoves move names Also updated GetBattleMoveName to properly handle Max Move names. Also also renamed the "zMoveName" variable to "bigMoveName" which better reflects its purpose. Z-Move names weren't the only thing it covered, since it also handles Max Move names. * Removed deprecated GetZMoveName and GetMaxMoveName * Reintroduced mention to gMoveNames in sGFRomHeader * Fixed move names and ported move descriptions * Fused the struct ContestMove into the struct BattleMove * Removed no longer used Z_MOVE_NAME_LENGTH constant * Renamed the struct BattleMove's bigMoveName variable and introduced macros to prettify move names * Reintroduced the contest parameters for Pokémon moves * Renamed gBattleMoves to gMovesInfo This is consistent with gSpeciesInfo, the array that contains most of the species data. * Renamed the BattleMove struct to MovesInfo This is consistent with the struct SpeciesInfo, which contains the variables used by the gSpeciesInfo array. * Removed empty lines separating battle params from contest params in gMovesInfo * Renamed MovesInfo to MoveInfo * Added Cancer Fairy's HANDLE_EXPANDED_MOVE_NAME macro Used to handle moves with expanded names in a more comfortable manner. Also fixed Trick-or-Treat's expanded name. * Renamed GetBattleMoveName to GetMoveName * Added a comment pointing out that the shared move descriptions are shared move descriptions * Re-aligned one of the escape characters of CHECK_MOVE_FLAG * Renamed the battle_moves.h file to moves_info.h instead for consistency's sake * Applied Eduardo's adjustments * Using compound string for regular move names as well, saving 1180 bytes and making their use consistent * Move description formatting * Updated Pursuit test after merge * Renamed the BATTLE_CATEGORY constants to DAMAGE_CATEGORY --------- Co-authored-by: Nephrite <thechurchofcage@gmail.com> Co-authored-by: Bassoonian <iasperbassoonian@gmail.com> Co-authored-by: Eduardo Quezada D'Ottone <eduardo602002@gmail.com>
2024-01-29 11:51:32 +00:00
ASSUME(gMovesInfo[MOVE_SOLAR_BEAM].category == DAMAGE_CATEGORY_SPECIAL);
ASSUME(gMovesInfo[MOVE_GRASS_PLEDGE].category == DAMAGE_CATEGORY_SPECIAL);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET) { HP(211); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_TYPHLOSION) { Moves(MOVE_SOLAR_BEAM, MOVE_GRASS_PLEDGE); Ability(abilityAtk); Item(holdItemAtk); }
} WHEN {
if (abilityAtk == ABILITY_DROUGHT) {
TURN { EXPECT_MOVES(opponent, MOVE_SOLAR_BEAM, MOVE_GRASS_PLEDGE); }
TURN { EXPECT_MOVES(opponent, MOVE_SOLAR_BEAM, MOVE_GRASS_PLEDGE); SEND_OUT(player, 1); }
} else if (holdItemAtk == ITEM_POWER_HERB) {
TURN { EXPECT_MOVES(opponent, MOVE_SOLAR_BEAM, MOVE_GRASS_PLEDGE); MOVE(player, MOVE_KNOCK_OFF); }
TURN { EXPECT_MOVE(opponent, MOVE_GRASS_PLEDGE); SEND_OUT(player, 1); }
} else {
TURN { EXPECT_MOVE(opponent, MOVE_GRASS_PLEDGE); }
TURN { EXPECT_MOVE(opponent, MOVE_GRASS_PLEDGE); SEND_OUT(player, 1); }
}
} SCENE {
MESSAGE("Wobbuffet fainted!");
}
}
AI_SINGLE_BATTLE_TEST("AI won't use ground type attacks against flying type Pokemon unless Gravity is in effect")
{
GIVEN {
Move data unification (#3999) * Made gBattleMoves handle the InGame name and description of battle moves No more multiple arrays in separate, individual files. Note: -Keep an eye on Task_LearnedMove. * Reintroduced move names Misc: -Fixed Trick-or-Treat and Light of Ruin's expanded names. -Introduced a new field for Z-Move names, and a constant for their name length. -Added a few TODOs to GetBattleMoveName. -Updated GetMaxMoveName and GetZMoveName. There's no reason not to let GetBattleMoveName handle everything on its own. * Updated GetBattleMoveName to handle Z-Move Names Misc: -Removed pointless TODO about MOVE_NAME_LENGTH. -The compiler doesn't allow to have a move name with a value higher than MOVE_NAME_LENGTH, therefore it's pointless to worry about it. * Fixed a couple of expanded move names * Removed zMoveName variable of struct BattleMove and extended the name variable's size * Ditched no longer used MOVE_NAME_LENGTH constant * Corrected the names of the max moves I should have done this after updating the size of the name variable of the struct BattleMove, but I didn't think about it at all until Cancer Fairy indirectly gave me the idea. * Fixed U-turn's name * Brought back MOVE_NAME_LENGTH I think it doesn't make sense to have a Z_MOVE_NAME_LENGTH because the length in question is used for all battle moves, not just the Z-Moves. * Introduced a union for Move/Z-Move names in the struct BattleMove * Fixed the union for gBattleMoves move names Also updated GetBattleMoveName to properly handle Max Move names. Also also renamed the "zMoveName" variable to "bigMoveName" which better reflects its purpose. Z-Move names weren't the only thing it covered, since it also handles Max Move names. * Removed deprecated GetZMoveName and GetMaxMoveName * Reintroduced mention to gMoveNames in sGFRomHeader * Fixed move names and ported move descriptions * Fused the struct ContestMove into the struct BattleMove * Removed no longer used Z_MOVE_NAME_LENGTH constant * Renamed the struct BattleMove's bigMoveName variable and introduced macros to prettify move names * Reintroduced the contest parameters for Pokémon moves * Renamed gBattleMoves to gMovesInfo This is consistent with gSpeciesInfo, the array that contains most of the species data. * Renamed the BattleMove struct to MovesInfo This is consistent with the struct SpeciesInfo, which contains the variables used by the gSpeciesInfo array. * Removed empty lines separating battle params from contest params in gMovesInfo * Renamed MovesInfo to MoveInfo * Added Cancer Fairy's HANDLE_EXPANDED_MOVE_NAME macro Used to handle moves with expanded names in a more comfortable manner. Also fixed Trick-or-Treat's expanded name. * Renamed GetBattleMoveName to GetMoveName * Added a comment pointing out that the shared move descriptions are shared move descriptions * Re-aligned one of the escape characters of CHECK_MOVE_FLAG * Renamed the battle_moves.h file to moves_info.h instead for consistency's sake * Applied Eduardo's adjustments * Using compound string for regular move names as well, saving 1180 bytes and making their use consistent * Move description formatting * Updated Pursuit test after merge * Renamed the BATTLE_CATEGORY constants to DAMAGE_CATEGORY --------- Co-authored-by: Nephrite <thechurchofcage@gmail.com> Co-authored-by: Bassoonian <iasperbassoonian@gmail.com> Co-authored-by: Eduardo Quezada D'Ottone <eduardo602002@gmail.com>
2024-01-29 11:51:32 +00:00
ASSUME(gMovesInfo[MOVE_EARTHQUAKE].category == DAMAGE_CATEGORY_PHYSICAL); // Otherwise, it doesn't KO Crobat
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_CROBAT);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_NIDOQUEEN) { Moves(MOVE_EARTHQUAKE, MOVE_TACKLE, MOVE_POISON_STING, MOVE_GUST); }
} WHEN {
TURN { NOT_EXPECT_MOVE(opponent, MOVE_EARTHQUAKE); }
TURN { MOVE(player, MOVE_GRAVITY); NOT_EXPECT_MOVE(opponent, MOVE_EARTHQUAKE); }
TURN { EXPECT_MOVE(opponent, MOVE_EARTHQUAKE); SEND_OUT(player, 1); }
} SCENE {
MESSAGE("Gravity intensified!");
}
}
AI_DOUBLE_BATTLE_TEST("AI won't use a Weather changing move if partner already chose such move")
{
u32 j, k;
static const u16 weatherMoves[] = {MOVE_SUNNY_DAY, MOVE_HAIL, MOVE_RAIN_DANCE, MOVE_SANDSTORM, MOVE_SNOWSCAPE};
u16 weatherMoveLeft = MOVE_NONE, weatherMoveRight = MOVE_NONE;
for (j = 0; j < ARRAY_COUNT(weatherMoves); j++)
{
for (k = 0; k < ARRAY_COUNT(weatherMoves); k++)
{
PARAMETRIZE { weatherMoveLeft = weatherMoves[j]; weatherMoveRight = weatherMoves[k]; }
}
}
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Moves(weatherMoveLeft); }
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_TACKLE, weatherMoveRight); }
} WHEN {
TURN { NOT_EXPECT_MOVE(opponentRight, weatherMoveRight);
SCORE_LT_VAL(opponentRight, weatherMoveRight, AI_SCORE_DEFAULT, target:playerLeft);
SCORE_LT_VAL(opponentRight, weatherMoveRight, AI_SCORE_DEFAULT, target:playerRight);
SCORE_LT_VAL(opponentRight, weatherMoveRight, AI_SCORE_DEFAULT, target:opponentLeft);
}
}
}
AI_DOUBLE_BATTLE_TEST("AI will not use Helping Hand if partner does not have any damage moves")
{
u16 move1 = MOVE_NONE, move2 = MOVE_NONE, move3 = MOVE_NONE, move4 = MOVE_NONE;
PARAMETRIZE{ move1 = MOVE_LEER; move2 = MOVE_TOXIC; }
PARAMETRIZE{ move1 = MOVE_HELPING_HAND; move2 = MOVE_PROTECT; }
PARAMETRIZE{ move1 = MOVE_ACUPRESSURE; move2 = MOVE_DOUBLE_TEAM; move3 = MOVE_TOXIC; move4 = MOVE_PROTECT; }
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_HELPING_HAND, MOVE_TACKLE); }
OPPONENT(SPECIES_WOBBUFFET) { Moves(move1, move2, move3, move4); }
} WHEN {
TURN { NOT_EXPECT_MOVE(opponentLeft, MOVE_HELPING_HAND);
SCORE_LT_VAL(opponentLeft, MOVE_HELPING_HAND, AI_SCORE_DEFAULT, target:playerLeft);
SCORE_LT_VAL(opponentLeft, MOVE_HELPING_HAND, AI_SCORE_DEFAULT, target:playerRight);
SCORE_LT_VAL(opponentLeft, MOVE_HELPING_HAND, AI_SCORE_DEFAULT, target:opponentLeft);
}
} SCENE {
NOT MESSAGE("Foe Wobbuffet used Helping Hand!");
}
}
AI_DOUBLE_BATTLE_TEST("AI will not use a status move if partner already chose Helping Hand")
{
s32 j;
u32 statusMove = MOVE_NONE;
for (j = MOVE_NONE + 1; j < MOVES_COUNT; j++)
{
Move data unification (#3999) * Made gBattleMoves handle the InGame name and description of battle moves No more multiple arrays in separate, individual files. Note: -Keep an eye on Task_LearnedMove. * Reintroduced move names Misc: -Fixed Trick-or-Treat and Light of Ruin's expanded names. -Introduced a new field for Z-Move names, and a constant for their name length. -Added a few TODOs to GetBattleMoveName. -Updated GetMaxMoveName and GetZMoveName. There's no reason not to let GetBattleMoveName handle everything on its own. * Updated GetBattleMoveName to handle Z-Move Names Misc: -Removed pointless TODO about MOVE_NAME_LENGTH. -The compiler doesn't allow to have a move name with a value higher than MOVE_NAME_LENGTH, therefore it's pointless to worry about it. * Fixed a couple of expanded move names * Removed zMoveName variable of struct BattleMove and extended the name variable's size * Ditched no longer used MOVE_NAME_LENGTH constant * Corrected the names of the max moves I should have done this after updating the size of the name variable of the struct BattleMove, but I didn't think about it at all until Cancer Fairy indirectly gave me the idea. * Fixed U-turn's name * Brought back MOVE_NAME_LENGTH I think it doesn't make sense to have a Z_MOVE_NAME_LENGTH because the length in question is used for all battle moves, not just the Z-Moves. * Introduced a union for Move/Z-Move names in the struct BattleMove * Fixed the union for gBattleMoves move names Also updated GetBattleMoveName to properly handle Max Move names. Also also renamed the "zMoveName" variable to "bigMoveName" which better reflects its purpose. Z-Move names weren't the only thing it covered, since it also handles Max Move names. * Removed deprecated GetZMoveName and GetMaxMoveName * Reintroduced mention to gMoveNames in sGFRomHeader * Fixed move names and ported move descriptions * Fused the struct ContestMove into the struct BattleMove * Removed no longer used Z_MOVE_NAME_LENGTH constant * Renamed the struct BattleMove's bigMoveName variable and introduced macros to prettify move names * Reintroduced the contest parameters for Pokémon moves * Renamed gBattleMoves to gMovesInfo This is consistent with gSpeciesInfo, the array that contains most of the species data. * Renamed the BattleMove struct to MovesInfo This is consistent with the struct SpeciesInfo, which contains the variables used by the gSpeciesInfo array. * Removed empty lines separating battle params from contest params in gMovesInfo * Renamed MovesInfo to MoveInfo * Added Cancer Fairy's HANDLE_EXPANDED_MOVE_NAME macro Used to handle moves with expanded names in a more comfortable manner. Also fixed Trick-or-Treat's expanded name. * Renamed GetBattleMoveName to GetMoveName * Added a comment pointing out that the shared move descriptions are shared move descriptions * Re-aligned one of the escape characters of CHECK_MOVE_FLAG * Renamed the battle_moves.h file to moves_info.h instead for consistency's sake * Applied Eduardo's adjustments * Using compound string for regular move names as well, saving 1180 bytes and making their use consistent * Move description formatting * Updated Pursuit test after merge * Renamed the BATTLE_CATEGORY constants to DAMAGE_CATEGORY --------- Co-authored-by: Nephrite <thechurchofcage@gmail.com> Co-authored-by: Bassoonian <iasperbassoonian@gmail.com> Co-authored-by: Eduardo Quezada D'Ottone <eduardo602002@gmail.com>
2024-01-29 11:51:32 +00:00
if (gMovesInfo[j].category == DAMAGE_CATEGORY_STATUS) {
PARAMETRIZE{ statusMove = j; }
}
}
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_HELPING_HAND); }
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_TACKLE, statusMove); }
} WHEN {
TURN { NOT_EXPECT_MOVE(opponentRight, statusMove);
SCORE_LT_VAL(opponentRight, statusMove, AI_SCORE_DEFAULT, target:playerLeft);
SCORE_LT_VAL(opponentRight, statusMove, AI_SCORE_DEFAULT, target:playerRight);
SCORE_LT_VAL(opponentRight, statusMove, AI_SCORE_DEFAULT, target:opponentLeft);
}
} SCENE {
MESSAGE("Foe Wobbuffet used Helping Hand!");
}
}
AI_SINGLE_BATTLE_TEST("AI without any flags chooses moves at random - singles")
{
GIVEN {
AI_FLAGS(0);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_NIDOQUEEN) { Moves(MOVE_SPLASH, MOVE_EXPLOSION, MOVE_RAGE, MOVE_HELPING_HAND); }
} WHEN {
TURN { EXPECT_MOVES(opponent, MOVE_SPLASH, MOVE_EXPLOSION, MOVE_RAGE, MOVE_HELPING_HAND);
SCORE_EQ_VAL(opponent, MOVE_SPLASH, AI_SCORE_DEFAULT);
SCORE_EQ_VAL(opponent, MOVE_EXPLOSION, AI_SCORE_DEFAULT);
SCORE_EQ_VAL(opponent, MOVE_RAGE, AI_SCORE_DEFAULT);
SCORE_EQ_VAL(opponent, MOVE_HELPING_HAND, AI_SCORE_DEFAULT);
}
}
}
AI_DOUBLE_BATTLE_TEST("AI without any flags chooses moves at random - doubles")
{
GIVEN {
AI_FLAGS(0);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_NIDOQUEEN) { Moves(MOVE_SPLASH, MOVE_EXPLOSION, MOVE_RAGE, MOVE_HELPING_HAND); }
OPPONENT(SPECIES_NIDOQUEEN) { Moves(MOVE_SPLASH, MOVE_EXPLOSION, MOVE_RAGE, MOVE_HELPING_HAND); }
} WHEN {
TURN { EXPECT_MOVES(opponentLeft, MOVE_SPLASH, MOVE_EXPLOSION, MOVE_RAGE, MOVE_HELPING_HAND);
EXPECT_MOVES(opponentRight, MOVE_SPLASH, MOVE_EXPLOSION, MOVE_RAGE, MOVE_HELPING_HAND);
SCORE_EQ_VAL(opponentLeft, MOVE_SPLASH, AI_SCORE_DEFAULT, target:playerLeft);
SCORE_EQ_VAL(opponentLeft, MOVE_EXPLOSION, AI_SCORE_DEFAULT, target:playerLeft);
SCORE_EQ_VAL(opponentLeft, MOVE_RAGE, AI_SCORE_DEFAULT, target:playerLeft);
SCORE_EQ_VAL(opponentLeft, MOVE_HELPING_HAND, AI_SCORE_DEFAULT, target:playerLeft);
SCORE_EQ_VAL(opponentRight, MOVE_SPLASH, AI_SCORE_DEFAULT, target:playerLeft);
SCORE_EQ_VAL(opponentRight, MOVE_EXPLOSION, AI_SCORE_DEFAULT, target:playerLeft);
SCORE_EQ_VAL(opponentRight, MOVE_RAGE, AI_SCORE_DEFAULT, target:playerLeft);
SCORE_EQ_VAL(opponentRight, MOVE_HELPING_HAND, AI_SCORE_DEFAULT, target:playerLeft);
}
}
}
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 * (https://github.com/rh-hideout/pokeemerald-expansion/commit/766a1a27a7298e50dd89c5fecc1989b3dd8b8ce3) 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>
2023-11-11 13:37:35 +00:00
AI_SINGLE_BATTLE_TEST("AI will choose either Rock Tomb or Bulldoze if Stat drop effect will activate and they kill with the same number of hits")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET) { HP(46); Speed(20); }
PLAYER(SPECIES_WYNAUT) { Speed(20); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(10); Moves(MOVE_BULLDOZE, MOVE_ROCK_TOMB); }
} WHEN {
TURN { EXPECT_MOVES(opponent, MOVE_BULLDOZE, MOVE_ROCK_TOMB); }
TURN { EXPECT_MOVES(opponent, MOVE_BULLDOZE, MOVE_ROCK_TOMB); SEND_OUT(player, 1); }
} SCENE {
MESSAGE("Wobbuffet fainted!");
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Number of hits to KO calculation checks whether incoming damage is less than recurring healing to avoid an infinite loop")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES);
PLAYER(SPECIES_VENUSAUR) { Level(30); Moves(MOVE_TACKLE); }
// Opponent party courtesy of Skolgrahd, who triggered the bug in the first place
OPPONENT(SPECIES_PIKACHU) { Level(100); Moves(MOVE_ZIPPY_ZAP, MOVE_EXTREME_SPEED, MOVE_IRON_TAIL, MOVE_KNOCK_OFF); }
OPPONENT(SPECIES_NINETALES_ALOLAN) { Level(100); Moves(MOVE_AURORA_VEIL, MOVE_BLIZZARD, MOVE_MOONBLAST, MOVE_DISABLE); }
OPPONENT(SPECIES_WEAVILE) { Level(100); Moves(MOVE_NIGHT_SLASH, MOVE_TRIPLE_AXEL, MOVE_ICE_SHARD, MOVE_FAKE_OUT); }
OPPONENT(SPECIES_DITTO) { Level(100); Moves(MOVE_TRANSFORM); }
OPPONENT(SPECIES_TYPHLOSION) { Level(100); Moves(MOVE_ERUPTION, MOVE_HEAT_WAVE, MOVE_FOCUS_BLAST, MOVE_EXTRASENSORY); }
OPPONENT(SPECIES_UMBREON) { Level(100); Item(ITEM_LEFTOVERS); Moves(MOVE_FOUL_PLAY, MOVE_SNARL, MOVE_HELPING_HAND, MOVE_THUNDER_WAVE); }
} WHEN {
TURN { MOVE(player, MOVE_TACKLE); EXPECT_MOVES(opponent, MOVE_ZIPPY_ZAP, MOVE_EXTREME_SPEED, MOVE_IRON_TAIL, MOVE_KNOCK_OFF); }
} SCENE {
MESSAGE("Venusaur fainted!");
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Number of hits to KO calculation checks whether incoming damage is zero to avoid an infinite loop")
{
GIVEN {
ASSUME(gItemsInfo[ITEM_LEFTOVERS].holdEffect == HOLD_EFFECT_LEFTOVERS);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES);
PLAYER(SPECIES_BULBASAUR) { Level(5); Moves(MOVE_SWORDS_DANCE, MOVE_WHIRLWIND, MOVE_SAND_ATTACK, MOVE_TAIL_WHIP); }
// Scenario courtesy of Duke, who triggered the bug in the first place
OPPONENT(SPECIES_GEODUDE) { Level(100); Moves(MOVE_TACKLE); }
OPPONENT(SPECIES_GEODUDE) { Level(100); Moves(MOVE_TACKLE); }
OPPONENT(SPECIES_NOSEPASS) { Level(100); Moves(MOVE_TACKLE); }
} WHEN {
TURN { MOVE(player, MOVE_SWORDS_DANCE); EXPECT_MOVES(opponent, MOVE_TACKLE); }
} SCENE {
MESSAGE("Bulbasaur fainted!");
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Avoid infinite loop if damage taken is equal to recurring healing")
{
GIVEN {
ASSUME(gItemsInfo[ITEM_LEFTOVERS].holdEffect == HOLD_EFFECT_LEFTOVERS);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES);
PLAYER(SPECIES_MEOWTH_GALARIAN) { Level(100); Moves(MOVE_GROWL, MOVE_FAKE_OUT, MOVE_HONE_CLAWS); }
// Scenario courtesy of Duke, who triggered the bug in the first place
OPPONENT(SPECIES_MEOWTH_GALARIAN) { Level(5); Moves(MOVE_GROWL, MOVE_FAKE_OUT, MOVE_HONE_CLAWS); }
OPPONENT(SPECIES_GEODUDE) { Level(5); Moves(MOVE_DOUBLE_EDGE); }
OPPONENT(SPECIES_GEODUDE) { Level(5); Moves(MOVE_DOUBLE_EDGE); }
OPPONENT(SPECIES_NOSEPASS) { Level(5); Moves(MOVE_DOUBLE_EDGE); }
OPPONENT(SPECIES_HOUNDSTONE) { Level(5); Moves(MOVE_NIGHT_SHADE, MOVE_BODY_PRESS, MOVE_WILL_O_WISP, MOVE_PROTECT); Item(ITEM_LEFTOVERS); }
} WHEN {
TURN { MOVE(player, MOVE_FAKE_OUT); EXPECT_MOVES(opponent, MOVE_FAKE_OUT); }
}
}
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 * (https://github.com/rh-hideout/pokeemerald-expansion/commit/766a1a27a7298e50dd89c5fecc1989b3dd8b8ce3) 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>
2023-11-11 13:37:35 +00:00
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 {
Move data unification (#3999) * Made gBattleMoves handle the InGame name and description of battle moves No more multiple arrays in separate, individual files. Note: -Keep an eye on Task_LearnedMove. * Reintroduced move names Misc: -Fixed Trick-or-Treat and Light of Ruin's expanded names. -Introduced a new field for Z-Move names, and a constant for their name length. -Added a few TODOs to GetBattleMoveName. -Updated GetMaxMoveName and GetZMoveName. There's no reason not to let GetBattleMoveName handle everything on its own. * Updated GetBattleMoveName to handle Z-Move Names Misc: -Removed pointless TODO about MOVE_NAME_LENGTH. -The compiler doesn't allow to have a move name with a value higher than MOVE_NAME_LENGTH, therefore it's pointless to worry about it. * Fixed a couple of expanded move names * Removed zMoveName variable of struct BattleMove and extended the name variable's size * Ditched no longer used MOVE_NAME_LENGTH constant * Corrected the names of the max moves I should have done this after updating the size of the name variable of the struct BattleMove, but I didn't think about it at all until Cancer Fairy indirectly gave me the idea. * Fixed U-turn's name * Brought back MOVE_NAME_LENGTH I think it doesn't make sense to have a Z_MOVE_NAME_LENGTH because the length in question is used for all battle moves, not just the Z-Moves. * Introduced a union for Move/Z-Move names in the struct BattleMove * Fixed the union for gBattleMoves move names Also updated GetBattleMoveName to properly handle Max Move names. Also also renamed the "zMoveName" variable to "bigMoveName" which better reflects its purpose. Z-Move names weren't the only thing it covered, since it also handles Max Move names. * Removed deprecated GetZMoveName and GetMaxMoveName * Reintroduced mention to gMoveNames in sGFRomHeader * Fixed move names and ported move descriptions * Fused the struct ContestMove into the struct BattleMove * Removed no longer used Z_MOVE_NAME_LENGTH constant * Renamed the struct BattleMove's bigMoveName variable and introduced macros to prettify move names * Reintroduced the contest parameters for Pokémon moves * Renamed gBattleMoves to gMovesInfo This is consistent with gSpeciesInfo, the array that contains most of the species data. * Renamed the BattleMove struct to MovesInfo This is consistent with the struct SpeciesInfo, which contains the variables used by the gSpeciesInfo array. * Removed empty lines separating battle params from contest params in gMovesInfo * Renamed MovesInfo to MoveInfo * Added Cancer Fairy's HANDLE_EXPANDED_MOVE_NAME macro Used to handle moves with expanded names in a more comfortable manner. Also fixed Trick-or-Treat's expanded name. * Renamed GetBattleMoveName to GetMoveName * Added a comment pointing out that the shared move descriptions are shared move descriptions * Re-aligned one of the escape characters of CHECK_MOVE_FLAG * Renamed the battle_moves.h file to moves_info.h instead for consistency's sake * Applied Eduardo's adjustments * Using compound string for regular move names as well, saving 1180 bytes and making their use consistent * Move description formatting * Updated Pursuit test after merge * Renamed the BATTLE_CATEGORY constants to DAMAGE_CATEGORY --------- Co-authored-by: Nephrite <thechurchofcage@gmail.com> Co-authored-by: Bassoonian <iasperbassoonian@gmail.com> Co-authored-by: Eduardo Quezada D'Ottone <eduardo602002@gmail.com>
2024-01-29 11:51:32 +00:00
ASSUME(gMovesInfo[MOVE_PSYCHIC].category == DAMAGE_CATEGORY_SPECIAL);
ASSUME(gMovesInfo[MOVE_FOCUS_BLAST].category == DAMAGE_CATEGORY_SPECIAL);
ASSUME(gMovesInfo[MOVE_BUBBLE_BEAM].category == DAMAGE_CATEGORY_SPECIAL);
ASSUME(gMovesInfo[MOVE_WATER_GUN].category == DAMAGE_CATEGORY_SPECIAL);
ASSUME(gMovesInfo[MOVE_STRENGTH].category == DAMAGE_CATEGORY_PHYSICAL);
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 * (https://github.com/rh-hideout/pokeemerald-expansion/commit/766a1a27a7298e50dd89c5fecc1989b3dd8b8ce3) 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>
2023-11-11 13:37:35 +00:00
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
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 * (https://github.com/rh-hideout/pokeemerald-expansion/commit/766a1a27a7298e50dd89c5fecc1989b3dd8b8ce3) 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>
2023-11-11 13:37:35 +00:00
}
}
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); }
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 * (https://github.com/rh-hideout/pokeemerald-expansion/commit/766a1a27a7298e50dd89c5fecc1989b3dd8b8ce3) 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>
2023-11-11 13:37:35 +00:00
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); }
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 * (https://github.com/rh-hideout/pokeemerald-expansion/commit/766a1a27a7298e50dd89c5fecc1989b3dd8b8ce3) 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>
2023-11-11 13:37:35 +00:00
}
}
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); }
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 * (https://github.com/rh-hideout/pokeemerald-expansion/commit/766a1a27a7298e50dd89c5fecc1989b3dd8b8ce3) 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>
2023-11-11 13:37:35 +00:00
OPPONENT(SPECIES_PONYTA) { Level(1); Moves(MOVE_NONE); Speed(4); } // Forces switchout
OPPONENT(SPECIES_ARON) { Level(30); Moves(MOVE_HEADBUTT); Speed(4); SpDefense(41); } // Mid battle, AI sends out Aron
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 * (https://github.com/rh-hideout/pokeemerald-expansion/commit/766a1a27a7298e50dd89c5fecc1989b3dd8b8ce3) 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>
2023-11-11 13:37:35 +00:00
OPPONENT(SPECIES_ELECTRODE) { Level(30); Moves(MOVE_CHARGE_BEAM); Speed(6); }
} WHEN {
TURN { MOVE(player, MOVE_WING_ATTACK); EXPECT_SWITCH(opponent, 1); }
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 * (https://github.com/rh-hideout/pokeemerald-expansion/commit/766a1a27a7298e50dd89c5fecc1989b3dd8b8ce3) 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>
2023-11-11 13:37:35 +00:00
}
}
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); }
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 * (https://github.com/rh-hideout/pokeemerald-expansion/commit/766a1a27a7298e50dd89c5fecc1989b3dd8b8ce3) 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>
2023-11-11 13:37:35 +00:00
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); }
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 * (https://github.com/rh-hideout/pokeemerald-expansion/commit/766a1a27a7298e50dd89c5fecc1989b3dd8b8ce3) 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>
2023-11-11 13:37:35 +00:00
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI switches out after sufficient stat drops")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING);
PLAYER(SPECIES_HITMONTOP) { Level(30); Moves(MOVE_CHARM, MOVE_TACKLE); Ability(ABILITY_INTIMIDATE); Speed(5); }
OPPONENT(SPECIES_GRIMER) { Level(30); Moves(MOVE_TACKLE); Speed(4); }
OPPONENT(SPECIES_PONYTA) { Level(30); Moves(MOVE_HEADBUTT); Speed(4); }
} WHEN {
TURN { MOVE(player, MOVE_CHARM); }
TURN { MOVE(player, MOVE_TACKLE); EXPECT_SWITCH(opponent, 1); }
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will not switch out if Pokemon would faint to hazards unless party member can clear them")
{
u32 move1;
PARAMETRIZE{ move1 = MOVE_TACKLE; }
PARAMETRIZE{ move1 = MOVE_RAPID_SPIN; }
GIVEN {
Move data unification (#3999) * Made gBattleMoves handle the InGame name and description of battle moves No more multiple arrays in separate, individual files. Note: -Keep an eye on Task_LearnedMove. * Reintroduced move names Misc: -Fixed Trick-or-Treat and Light of Ruin's expanded names. -Introduced a new field for Z-Move names, and a constant for their name length. -Added a few TODOs to GetBattleMoveName. -Updated GetMaxMoveName and GetZMoveName. There's no reason not to let GetBattleMoveName handle everything on its own. * Updated GetBattleMoveName to handle Z-Move Names Misc: -Removed pointless TODO about MOVE_NAME_LENGTH. -The compiler doesn't allow to have a move name with a value higher than MOVE_NAME_LENGTH, therefore it's pointless to worry about it. * Fixed a couple of expanded move names * Removed zMoveName variable of struct BattleMove and extended the name variable's size * Ditched no longer used MOVE_NAME_LENGTH constant * Corrected the names of the max moves I should have done this after updating the size of the name variable of the struct BattleMove, but I didn't think about it at all until Cancer Fairy indirectly gave me the idea. * Fixed U-turn's name * Brought back MOVE_NAME_LENGTH I think it doesn't make sense to have a Z_MOVE_NAME_LENGTH because the length in question is used for all battle moves, not just the Z-Moves. * Introduced a union for Move/Z-Move names in the struct BattleMove * Fixed the union for gBattleMoves move names Also updated GetBattleMoveName to properly handle Max Move names. Also also renamed the "zMoveName" variable to "bigMoveName" which better reflects its purpose. Z-Move names weren't the only thing it covered, since it also handles Max Move names. * Removed deprecated GetZMoveName and GetMaxMoveName * Reintroduced mention to gMoveNames in sGFRomHeader * Fixed move names and ported move descriptions * Fused the struct ContestMove into the struct BattleMove * Removed no longer used Z_MOVE_NAME_LENGTH constant * Renamed the struct BattleMove's bigMoveName variable and introduced macros to prettify move names * Reintroduced the contest parameters for Pokémon moves * Renamed gBattleMoves to gMovesInfo This is consistent with gSpeciesInfo, the array that contains most of the species data. * Renamed the BattleMove struct to MovesInfo This is consistent with the struct SpeciesInfo, which contains the variables used by the gSpeciesInfo array. * Removed empty lines separating battle params from contest params in gMovesInfo * Renamed MovesInfo to MoveInfo * Added Cancer Fairy's HANDLE_EXPANDED_MOVE_NAME macro Used to handle moves with expanded names in a more comfortable manner. Also fixed Trick-or-Treat's expanded name. * Renamed GetBattleMoveName to GetMoveName * Added a comment pointing out that the shared move descriptions are shared move descriptions * Re-aligned one of the escape characters of CHECK_MOVE_FLAG * Renamed the battle_moves.h file to moves_info.h instead for consistency's sake * Applied Eduardo's adjustments * Using compound string for regular move names as well, saving 1180 bytes and making their use consistent * Move description formatting * Updated Pursuit test after merge * Renamed the BATTLE_CATEGORY constants to DAMAGE_CATEGORY --------- Co-authored-by: Nephrite <thechurchofcage@gmail.com> Co-authored-by: Bassoonian <iasperbassoonian@gmail.com> Co-authored-by: Eduardo Quezada D'Ottone <eduardo602002@gmail.com>
2024-01-29 11:51:32 +00:00
ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL);
ASSUME(gMovesInfo[MOVE_RAPID_SPIN].category == DAMAGE_CATEGORY_PHYSICAL);
ASSUME(gMovesInfo[MOVE_EARTHQUAKE].category == DAMAGE_CATEGORY_PHYSICAL);
ASSUME(gMovesInfo[MOVE_HEADBUTT].category == DAMAGE_CATEGORY_PHYSICAL);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING);
PLAYER(SPECIES_HITMONTOP) { Level(30); Moves(MOVE_CHARM, MOVE_TACKLE, MOVE_STEALTH_ROCK, MOVE_EARTHQUAKE); Ability(ABILITY_INTIMIDATE); Speed(5); }
OPPONENT(SPECIES_GRIMER) { Level(30); Moves(MOVE_TACKLE); Item(ITEM_FOCUS_SASH); Speed(4); }
OPPONENT(SPECIES_PONYTA) { Level(30); Moves(MOVE_HEADBUTT, move1); Speed(4); }
} WHEN {
TURN { MOVE(player, MOVE_STEALTH_ROCK); }
TURN { MOVE(player, MOVE_EARTHQUAKE); }
TURN { MOVE(player, MOVE_CHARM); }
TURN { // If the AI has a mon that can remove hazards, don't prevent them switching out
MOVE(player, MOVE_CHARM);
if (move1 == MOVE_RAPID_SPIN)
EXPECT_SWITCH(opponent, 1);
else if (move1 == MOVE_TACKLE)
EXPECT_MOVE(opponent, MOVE_TACKLE);
}
}
}
AI_SINGLE_BATTLE_TEST("First Impression is preferred on the first turn of the species if it's the best dmg move")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_FIRST_IMPRESSION].effect == EFFECT_FIRST_TURN_ONLY);
ASSUME(gMovesInfo[MOVE_FIRST_IMPRESSION].power == 90);
ASSUME(gMovesInfo[MOVE_LUNGE].power == 80);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_KANGASKHAN);
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_FIRST_IMPRESSION, MOVE_LUNGE); }
} WHEN {
TURN { EXPECT_MOVE(opponent, MOVE_FIRST_IMPRESSION); }
TURN { EXPECT_MOVE(opponent, MOVE_LUNGE); }
}
}
AI_SINGLE_BATTLE_TEST("First Impression is not chosen if it's blocked by certain abilities")
{
u16 species;
u16 ability;
PARAMETRIZE { species = SPECIES_BRUXISH; ability = ABILITY_DAZZLING; }
PARAMETRIZE { species = SPECIES_FARIGIRAF; ability = ABILITY_ARMOR_TAIL; }
PARAMETRIZE { species = SPECIES_TSAREENA; ability = ABILITY_QUEENLY_MAJESTY; }
GIVEN {
ASSUME(gMovesInfo[MOVE_FIRST_IMPRESSION].effect == EFFECT_FIRST_TURN_ONLY);
ASSUME(gMovesInfo[MOVE_FIRST_IMPRESSION].power == 90);
ASSUME(gMovesInfo[MOVE_LUNGE].power == 80);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT);
PLAYER(species) { Ability(ability); }
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_FIRST_IMPRESSION, MOVE_LUNGE); }
} WHEN {
TURN { EXPECT_MOVE(opponent, MOVE_LUNGE); }
}
}
AI_DOUBLE_BATTLE_TEST("AI will not try to switch for the same pokemon for 2 spots in a double battle")
{
u32 flags;
PARAMETRIZE {flags = AI_FLAG_SMART_SWITCHING; }
PARAMETRIZE {flags = 0; }
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | flags);
PLAYER(SPECIES_RATTATA);
PLAYER(SPECIES_RATTATA);
// No moves to damage player.
OPPONENT(SPECIES_GENGAR) { Moves(MOVE_SHADOW_BALL); }
OPPONENT(SPECIES_HAUNTER) { Moves(MOVE_SHADOW_BALL); }
OPPONENT(SPECIES_GENGAR) { Moves(MOVE_SHADOW_BALL); }
OPPONENT(SPECIES_RATICATE) { Moves(MOVE_HEADBUTT); }
} WHEN {
TURN { EXPECT_SWITCH(opponentLeft, 3); };
} SCENE {
MESSAGE("{PKMN} TRAINER LEAF withdrew Gengar!");
MESSAGE("{PKMN} TRAINER LEAF sent out Raticate!");
NONE_OF {
MESSAGE("{PKMN} TRAINER LEAF withdrew Haunter!");
MESSAGE("{PKMN} TRAINER LEAF sent out Raticate!");
}
}
}
AI_SINGLE_BATTLE_TEST("AI will not choose Burn Up if the user lost the Fire typing")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_BURN_UP].effect == EFFECT_FAIL_IF_NOT_ARG_TYPE);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_CYNDAQUIL) { Moves(MOVE_BURN_UP, MOVE_EXTRASENSORY, MOVE_FLAMETHROWER); }
} WHEN {
TURN { EXPECT_MOVE(opponent, MOVE_BURN_UP); }
TURN { EXPECT_MOVE(opponent, MOVE_FLAMETHROWER); }
}
}
AI_SINGLE_BATTLE_TEST("AI will choose Surf over Thunderbolt and Ice Beam if the opposing mon has Volt Absorb")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_THUNDERBOLT].type == TYPE_ELECTRIC);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_LANTURN) { Ability(ABILITY_VOLT_ABSORB); };
OPPONENT(SPECIES_LANTURN) { Moves(MOVE_THUNDERBOLT, MOVE_ICE_BEAM, MOVE_SURF); }
} WHEN {
TURN { EXPECT_MOVE(opponent, MOVE_SURF); }
}
}
AI_SINGLE_BATTLE_TEST("AI will choose Scratch over Power-up Punch with Contrary")
{
u32 ability;
PARAMETRIZE {ability = ABILITY_SUCTION_CUPS; }
PARAMETRIZE {ability = ABILITY_CONTRARY; }
GIVEN {
ASSUME(gMovesInfo[MOVE_SCRATCH].power == 40);
ASSUME(gMovesInfo[MOVE_SCRATCH].type == TYPE_NORMAL);
ASSUME(gMovesInfo[MOVE_POWER_UP_PUNCH].power == 40);
ASSUME(gMovesInfo[MOVE_POWER_UP_PUNCH].type == TYPE_FIGHTING);
ASSUME(gSpeciesInfo[SPECIES_SQUIRTLE].types[0] == TYPE_WATER);
ASSUME(gSpeciesInfo[SPECIES_SQUIRTLE].types[1] == TYPE_WATER);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_SQUIRTLE) { };
OPPONENT(SPECIES_MALAMAR) { Ability(ability); Moves(MOVE_SCRATCH, MOVE_POWER_UP_PUNCH); }
} WHEN {
TURN {
if (ability != ABILITY_CONTRARY)
EXPECT_MOVE(opponent, MOVE_POWER_UP_PUNCH);
else
EXPECT_MOVE(opponent, MOVE_SCRATCH);
}
}
}
AI_SINGLE_BATTLE_TEST("AI will choose Superpower over Outrage with Contrary")
{
u32 ability;
PARAMETRIZE {ability = ABILITY_SUCTION_CUPS; }
PARAMETRIZE {ability = ABILITY_CONTRARY; }
GIVEN {
ASSUME(gMovesInfo[MOVE_SUPERPOWER].power == 120);
ASSUME(gMovesInfo[MOVE_SUPERPOWER].type == TYPE_FIGHTING);
ASSUME(gMovesInfo[MOVE_OUTRAGE].power == 120);
ASSUME(gMovesInfo[MOVE_OUTRAGE].type == TYPE_DRAGON);
ASSUME(gSpeciesInfo[SPECIES_SQUIRTLE].types[0] == TYPE_WATER);
ASSUME(gSpeciesInfo[SPECIES_SQUIRTLE].types[1] == TYPE_WATER);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_SQUIRTLE) { };
OPPONENT(SPECIES_MALAMAR) { Ability(ability); Moves(MOVE_OUTRAGE, MOVE_SUPERPOWER); }
} WHEN {
TURN {
if (ability != ABILITY_CONTRARY)
EXPECT_MOVE(opponent, MOVE_OUTRAGE);
else
EXPECT_MOVE(opponent, MOVE_SUPERPOWER);
}
}
}
AI_DOUBLE_BATTLE_TEST("AI will not choose Earthquake if it damages the partner")
{
u32 species;
PARAMETRIZE { species = SPECIES_CHARIZARD; }
PARAMETRIZE { species = SPECIES_CHARMANDER; }
PARAMETRIZE { species = SPECIES_CHIKORITA; }
GIVEN {
ASSUME(gMovesInfo[MOVE_EARTHQUAKE].target == MOVE_TARGET_FOES_AND_ALLY);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_PHANPY) { Moves(MOVE_EARTHQUAKE, MOVE_TACKLE); }
OPPONENT(species) { Moves(MOVE_CELEBRATE); }
} WHEN {
if (species == SPECIES_CHARIZARD)
TURN { EXPECT_MOVE(opponentLeft, MOVE_EARTHQUAKE); }
else
TURN { EXPECT_MOVE(opponentLeft, MOVE_TACKLE, target: playerLeft); }
}
}
AI_DOUBLE_BATTLE_TEST("AI will choose Earthquake if partner is not alive")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_EARTHQUAKE].target == MOVE_TARGET_FOES_AND_ALLY);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_EARTHQUAKE, MOVE_TACKLE); }
OPPONENT(SPECIES_PIKACHU) { HP(1); Moves(MOVE_CELEBRATE); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_TACKLE, target: opponentRight); }
TURN { EXPECT_MOVE(opponentLeft, MOVE_EARTHQUAKE); }
}
}
AI_DOUBLE_BATTLE_TEST("AI will choose Earthquake if it kill an opposing mon and does 1/3 of damage to AI")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_EARTHQUAKE].target == MOVE_TARGET_FOES_AND_ALLY);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET) { HP(1); }
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_EARTHQUAKE, MOVE_TACKLE); }
OPPONENT(SPECIES_PARAS) { Moves(MOVE_CELEBRATE); }
} WHEN {
TURN { EXPECT_MOVE(opponentLeft, MOVE_EARTHQUAKE); }
}
}
AI_DOUBLE_BATTLE_TEST("AI will the see a corresponding absorbing ability on partner to one of its moves")
{
u32 ability;
PARAMETRIZE { ability = ABILITY_LIGHTNING_ROD; }
PARAMETRIZE { ability = ABILITY_STATIC; }
GIVEN {
ASSUME(gMovesInfo[MOVE_DISCHARGE].target == MOVE_TARGET_FOES_AND_ALLY);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_DISCHARGE, MOVE_TACKLE); }
OPPONENT(SPECIES_PIKACHU) { HP(1); Ability(ability); Moves(MOVE_CELEBRATE); }
} WHEN {
if (ability == ABILITY_LIGHTNING_ROD)
TURN { EXPECT_MOVE(opponentLeft, MOVE_DISCHARGE); }
else
TURN { EXPECT_MOVE(opponentLeft, MOVE_TACKLE); }
}
}