update tests/test framework + failed test

This commit is contained in:
cawtds
2024-07-23 19:54:49 +02:00
parent f2174c41d4
commit 48552f6c12
94 changed files with 2340 additions and 441 deletions

View File

@@ -99,7 +99,7 @@ void *AllocInternal(void *heapStart, u32 size, const char *location)
block = block->next;
}
while (block != head);
Test_ExitWithResult(TEST_RESULT_ERROR, "%s: OOM allocating %d bytes", location, size);
Test_ExitWithResult(TEST_RESULT_ERROR, SourceLine(0), ":L%s:%d, %s: OOM allocating %d bytes", gTestRunnerState.test->filename, SourceLine(0), location, size);
#endif
AGB_ASSERT_EX(0, ABSPATH("gflib/malloc.c"), 174);
return NULL;

View File

@@ -48,11 +48,12 @@ struct TrainerMon
u8 nature:5;
bool8 gender:2;
bool8 isShiny:1;
u8 useGimmick:4;
u8 dynamaxLevel:4;
u8 teraType:5;
bool8 gigantamaxFactor:1;
u8 padding:2;
u8 shouldUseDynamax:1;
u8 padding1:1;
u8 dynamaxLevel:4;
u8 padding2:4;
};
#define TRAINER_PARTY(partyArray) partyArray, .partySize = ARRAY_COUNT(partyArray)

View File

@@ -507,7 +507,7 @@
// or loop.
#define BATTLE_TEST_STACK_SIZE 1024
#define MAX_TURNS 16
#define MAX_QUEUED_EVENTS 25
#define MAX_QUEUED_EVENTS 30
#define MAX_EXPECTED_ACTIONS 10
enum { BATTLE_TEST_SINGLES, BATTLE_TEST_DOUBLES, BATTLE_TEST_WILD, BATTLE_TEST_AI_SINGLES, BATTLE_TEST_AI_DOUBLES };
@@ -518,7 +518,6 @@ typedef void (*DoubleBattleTestFunction)(void *, const u32, struct BattlePokemon
struct BattleTest
{
u8 type;
u16 sourceLine;
union
{
SingleBattleTestFunction singles;
@@ -744,10 +743,10 @@ extern struct BattleTestRunnerState *const gBattleTestRunnerState;
.name = _name, \
.filename = __FILE__, \
.runner = &gBattleTestRunner, \
.sourceLine = __LINE__, \
.data = (void *)&(const struct BattleTest) \
{ \
.type = _type, \
.sourceLine = __LINE__, \
.function = { .singles = (SingleBattleTestFunction)CAT(Test, __LINE__) }, \
.resultsSize = sizeof(struct CAT(Result, __LINE__)), \
}, \
@@ -762,10 +761,10 @@ extern struct BattleTestRunnerState *const gBattleTestRunnerState;
.name = _name, \
.filename = __FILE__, \
.runner = &gBattleTestRunner, \
.sourceLine = __LINE__, \
.data = (void *)&(const struct BattleTest) \
{ \
.type = _type, \
.sourceLine = __LINE__, \
.function = { .doubles = (DoubleBattleTestFunction)CAT(Test, __LINE__) }, \
.resultsSize = sizeof(struct CAT(Result, __LINE__)), \
}, \
@@ -995,6 +994,8 @@ void SendOut(u32 sourceLine, struct BattlePokemon *, u32 partyIndex);
// Static const is needed to make the modern compiler put the pattern variable in the .rodata section, instead of putting it on stack(which can break the game).
#define MESSAGE(pattern) do {static const u8 msg[] = _(pattern); QueueMessage(__LINE__, msg);} while (0)
#define STATUS_ICON(battler, status) QueueStatus(__LINE__, battler, (struct StatusEventContext) { status })
#define FREEZE_OR_FROSTBURN_STATUS(battler, isFrostbite) \
(B_USE_FROSTBITE ? STATUS_ICON(battler, frostbite: isFrostbite) : STATUS_ICON(battler, freeze: isFrostbite))
#define SWITCH_OUT_MESSAGE(name) ONE_OF { \
MESSAGE(name ", that's enough! Come back!"); \
@@ -1092,7 +1093,7 @@ void ValidateFinally(u32 sourceLine);
s32 _am = Q_4_12_TO_INT(_a * _m); \
s32 _t = max(Q_4_12_TO_INT(abs(_m) + Q_4_12_ROUND), 1); \
if (abs(_am-_b) > _t) \
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT_MUL_EQ(%d, %q, %d) failed: %d not in [%d..%d]", gTestRunnerState.test->filename, __LINE__, _a, _m, _b, _am, _b-_t, _b+_t); \
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_MUL_EQ(%d, %q, %d) failed: %d not in [%d..%d]", gTestRunnerState.test->filename, __LINE__, _a, _m, _b, _am, _b-_t, _b+_t); \
} while (0)
#endif

View File

@@ -33,6 +33,7 @@ struct Test
const char *filename;
const struct TestRunner *runner;
void *data;
u16 sourceLine;
};
struct TestRunnerState
@@ -40,6 +41,7 @@ struct TestRunnerState
u8 state;
u8 exitCode;
const char *skipFilename;
u32 failedAssumptionsBlockLine;
const struct Test *test;
u32 processCosts[MAX_PROCESSES];
@@ -73,7 +75,9 @@ void CB2_TestRunner(void);
void Test_ExpectedResult(enum TestResult);
void Test_ExpectLeaks(bool32);
void Test_ExitWithResult(enum TestResult, const char *fmt, ...);
void Test_ExitWithResult(enum TestResult, u32 stopLine, const char *fmt, ...);
u32 SourceLine(u32 sourceLineOffset);
u32 SourceLineOffset(u32 sourceLine);
s32 Test_MgbaPrintf(const char *fmt, ...);
@@ -84,6 +88,7 @@ s32 Test_MgbaPrintf(const char *fmt, ...);
.name = _name, \
.filename = __FILE__, \
.runner = &gFunctionTestRunner, \
.sourceLine = __LINE__, \
.data = (void *)CAT(Test, __LINE__), \
}; \
static void CAT(Test, __LINE__)(void)
@@ -95,6 +100,7 @@ s32 Test_MgbaPrintf(const char *fmt, ...);
.name = "ASSUMPTIONS: " __FILE__, \
.filename = __FILE__, \
.runner = &gAssumptionsRunner, \
.sourceLine = __LINE__, \
.data = Assumptions, \
}; \
static void Assumptions(void)
@@ -103,14 +109,14 @@ s32 Test_MgbaPrintf(const char *fmt, ...);
do \
{ \
if (!(c)) \
Test_ExitWithResult(TEST_RESULT_ASSUMPTION_FAIL, "%s:%d: ASSUME failed", gTestRunnerState.test->filename, __LINE__); \
Test_ExitWithResult(TEST_RESULT_ASSUMPTION_FAIL, __LINE__, ":L%s:%d: ASSUME failed", gTestRunnerState.test->filename, __LINE__); \
} while (0)
#define EXPECT(c) \
do \
{ \
if (!(c)) \
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT failed", gTestRunnerState.test->filename, __LINE__); \
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT failed", gTestRunnerState.test->filename, __LINE__); \
} while (0)
#define EXPECT_EQ(a, b) \
@@ -118,7 +124,7 @@ s32 Test_MgbaPrintf(const char *fmt, ...);
{ \
typeof(a) _a = (a), _b = (b); \
if (_a != _b) \
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT_EQ(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_EQ(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
} while (0)
#define EXPECT_NE(a, b) \
@@ -126,7 +132,7 @@ s32 Test_MgbaPrintf(const char *fmt, ...);
{ \
typeof(a) _a = (a), _b = (b); \
if (_a == _b) \
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT_NE(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_NE(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
} while (0)
#define EXPECT_LT(a, b) \
@@ -134,7 +140,7 @@ s32 Test_MgbaPrintf(const char *fmt, ...);
{ \
typeof(a) _a = (a), _b = (b); \
if (_a >= _b) \
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT_LT(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_LT(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
} while (0)
#define EXPECT_LE(a, b) \
@@ -142,7 +148,7 @@ s32 Test_MgbaPrintf(const char *fmt, ...);
{ \
typeof(a) _a = (a), _b = (b); \
if (_a > _b) \
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT_LE(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_LE(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
} while (0)
#define EXPECT_GT(a, b) \
@@ -150,7 +156,7 @@ s32 Test_MgbaPrintf(const char *fmt, ...);
{ \
typeof(a) _a = (a), _b = (b); \
if (_a <= _b) \
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT_GT(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_GT(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
} while (0)
#define EXPECT_GE(a, b) \
@@ -158,7 +164,7 @@ s32 Test_MgbaPrintf(const char *fmt, ...);
{ \
typeof(a) _a = (a), _b = (b); \
if (_a < _b) \
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT_GE(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_GE(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
} while (0)
struct Benchmark { s32 ticks; };
@@ -195,7 +201,7 @@ static inline struct Benchmark BenchmarkStop(void)
u32 a_ = (a).ticks; u32 b_ = (b).ticks; \
Test_MgbaPrintf(#a ": %d ticks, " #b ": %d ticks", a_, b_); \
if (((a_ - BENCHMARK_ABS) * BENCHMARK_REL) >= (b_ * 100)) \
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT_FASTER(" #a ", " #b ") failed", gTestRunnerState.test->filename, __LINE__); \
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_FASTER(" #a ", " #b ") failed", gTestRunnerState.test->filename, __LINE__); \
} while (0)
#define EXPECT_SLOWER(a, b) \
@@ -204,7 +210,7 @@ static inline struct Benchmark BenchmarkStop(void)
u32 a_ = (a).ticks; u32 b_ = (b).ticks; \
Test_MgbaPrintf(#a ": %d ticks, " #b ": %d ticks", a_, b_); \
if ((a_ * 100) <= ((b_ - BENCHMARK_ABS) * BENCHMARK_REL)) \
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT_SLOWER(" #a ", " #b ") failed", gTestRunnerState.test->filename, __LINE__); \
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_SLOWER(" #a ", " #b ") failed", gTestRunnerState.test->filename, __LINE__); \
} while (0)
#define KNOWN_FAILING \
@@ -220,7 +226,7 @@ static inline struct Benchmark BenchmarkStop(void)
#define TO_DO \
do { \
Test_ExpectedResult(TEST_RESULT_TODO); \
Test_ExitWithResult(TEST_RESULT_TODO, "%s:%d: EXPECT_TO_DO", gTestRunnerState.test->filename, __LINE__); \
Test_ExitWithResult(TEST_RESULT_TODO, __LINE__, ":L%s:%d: EXPECT_TO_DO", gTestRunnerState.test->filename, __LINE__); \
} while (0)
#endif

View File

@@ -82,8 +82,14 @@ bool32 ShouldTrainerBattlerUseGimmick(u32 battler, enum Gimmick gimmick)
bool32 isSecondTrainer = (GetBattlerPosition(battler) == B_POSITION_OPPONENT_RIGHT) && (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) && !BATTLE_TWO_VS_ONE_OPPONENT;
u16 trainerId = isSecondTrainer ? gTrainerBattleOpponent_B : gTrainerBattleOpponent_A;
const struct TrainerMon *mon = &GetTrainerPartyFromId(trainerId)[isSecondTrainer ? gBattlerPartyIndexes[battler] - MULTI_PARTY_SIZE : gBattlerPartyIndexes[battler]];
return mon->useGimmick == gimmick;
if (gimmick == GIMMICK_TERA && mon->teraType != TYPE_NONE)
return TRUE;
if (gimmick == GIMMICK_DYNAMAX && mon->shouldUseDynamax)
return TRUE;
}
return FALSE;
}
// Returns whether a trainer has used a gimmick during a battle.
@@ -290,7 +296,7 @@ static inline u32 GetIndicatorSpriteId(u32 healthboxId)
u32 GetIndicatorTileTag(u32 battler)
{
u32 gimmick = GetActiveGimmick(battler);
if (IsBattlerPrimalReverted(battler))
{
if (gBattleMons[battler].species == SPECIES_GROUDON_PRIMAL)
@@ -330,6 +336,9 @@ void UpdateIndicatorVisibilityAndType(u32 healthboxId, bool32 invisible)
u32 palTag = GetIndicatorPalTag(battler);
struct Sprite *sprite = &gSprites[GetIndicatorSpriteId(healthboxId)];
if (GetIndicatorSpriteId(healthboxId) == 0) // safari zone means the player doesn't have an indicator sprite id
return;
if (tileTag != TAG_NONE && palTag != TAG_NONE)
{
sprite->oam.tileNum = GetSpriteTileStartByTag(tileTag);

View File

@@ -34,7 +34,7 @@ void ActivateTera(u32 battler)
{
FlagClear(B_FLAG_TERA_ORB_CHARGED);
}
// Execute battle script.
PREPARE_TYPE_BUFFER(gBattleTextBuff1, GetBattlerTeraType(battler));
if (TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_TERASTALLIZATION))
@@ -63,16 +63,21 @@ bool32 CanTerastallize(u32 battler)
{
u32 holdEffect = GetBattlerHoldEffect(battler, FALSE);
// Check if Player has Tera Orb and has charge.
if (!TESTING && !CheckBagHasItem(ITEM_TERA_ORB, 1))
return FALSE;
if (!TESTING
&& !(B_FLAG_TERA_ORB_NO_COST != 0 && FlagGet(B_FLAG_TERA_ORB_NO_COST))
&& (battler == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && battler == B_POSITION_PLAYER_RIGHT)))
if (TESTING || GetBattlerSide(battler) == B_SIDE_OPPONENT)
{
if (B_FLAG_TERA_ORB_CHARGED != 0 && !FlagGet(B_FLAG_TERA_ORB_CHARGED))
return FALSE;
// Skip all other checks in this block, go to HasTrainerUsedGimmick
}
else if (!CheckBagHasItem(ITEM_TERA_ORB, 1))
{
return FALSE;
}
else if (FlagGet(B_FLAG_TERA_ORB_NO_COST))
{
// Tera Orb is not depleted, go to HasTrainerUsedGimmick
}
else if (!FlagGet(B_FLAG_TERA_ORB_CHARGED))
{
return FALSE;
}
// Check if Trainer has already Terastallized.
@@ -104,7 +109,7 @@ u32 GetBattlerTeraType(u32 battler)
// Uses up a type's Stellar boost.
void ExpendTypeStellarBoost(u32 battler, u32 type)
{
if (type < 32) // avoid OOB access
if (type < 32 && gBattleMons[battler].species != SPECIES_TERAPAGOS_STELLAR) // avoid OOB access
gBattleStruct->stellarBoostFlags[GetBattlerSide(battler)] |= gBitTable[type];
}

View File

@@ -76,11 +76,7 @@ const struct SpeciesInfo gSpeciesInfo[] =
.categoryName = _("Unknown"),
.height = 0,
.weight = 0,
.description = COMPOUND_STRING(
"This is a newly discovered Pokémon.\n"
"It is currently under investigation.\n"
"No detailed information is available\n"
"at this time."),
.description = gFallbackPokedexText,
.pokemonScale = 256,
.pokemonOffset = 0,
.trainerScale = 256,

View File

@@ -1,3 +1,10 @@
// fallback
const u8 gFallbackPokedexText[] = _(
"This is a newly discovered Pokémon.\n"
"It is currently under investigation.\n"
"No detailed information is available\n"
"at this time.");
// Gen 1 families
const u8 gRaticateAlolanPokedexText[] = _(
"It forms a group of Rattata, which it \n"
@@ -279,4 +286,4 @@ const u8 gOgerponCornerstoneMaskPokedexText[] = _(
"In this form, it draws on the power\n"
"of stone. Its body is rock-solid,\n"
"protecting it from all manner of\n"
"attacks.");
"attacks.");

View File

@@ -46,9 +46,9 @@ SINGLE_BATTLE_TEST("Beads of Ruin's message displays correctly after all battler
ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, opponent);
// Everyone faints.
MESSAGE("Go! Chi-Yu!");
MESSAGE("2 sent out Wobbuffet!");
ABILITY_POPUP(player, ABILITY_BEADS_OF_RUIN);
MESSAGE("Chi-Yu's Beads of Ruin weakened the Sp. Def of all surrounding Pokémon!");
MESSAGE("2 sent out Wobbuffet!");
}
}

View File

@@ -0,0 +1,54 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Comatose prevents status-inducing moves")
{
u32 move;
PARAMETRIZE { move = MOVE_TOXIC; }
PARAMETRIZE { move = MOVE_POISONPOWDER; }
PARAMETRIZE { move = MOVE_SLEEP_POWDER; }
PARAMETRIZE { move = MOVE_THUNDER_WAVE; }
GIVEN {
PLAYER(SPECIES_KOMALA) { Ability(ABILITY_COMATOSE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, move); }
} SCENE {
MESSAGE("Komala is drowsing!");
NOT ANIMATION(ANIM_TYPE_MOVE, move, opponent);
ABILITY_POPUP(player, ABILITY_COMATOSE);
MESSAGE("It doesn't affect Komala…");
}
}
SINGLE_BATTLE_TEST("Comatose may be suppressed if pokemon transformed into a pokemon with Comatose ability and was under the effects of Gastro Acid")
{
u32 move;
PARAMETRIZE { move = MOVE_TOXIC; }
PARAMETRIZE { move = MOVE_POISONPOWDER; }
PARAMETRIZE { move = MOVE_SLEEP_POWDER; }
PARAMETRIZE { move = MOVE_THUNDER_WAVE; }
GIVEN {
PLAYER(SPECIES_KOMALA) { Ability(ABILITY_COMATOSE); Speed(30); }
OPPONENT(SPECIES_DITTO) { Speed(20); }
} WHEN {
TURN { MOVE(player, MOVE_GASTRO_ACID); MOVE(opponent, MOVE_TRANSFORM); }
TURN { MOVE(player, move); }
} SCENE {
MESSAGE("Komala is drowsing!");
MESSAGE("Komala used Gastro Acid!");
MESSAGE("Foe Ditto used Transform!");
MESSAGE("Foe Ditto transformed into Komala!");
ANIMATION(ANIM_TYPE_MOVE, move, player);
if (move == MOVE_POISONPOWDER) { STATUS_ICON(opponent, poison: TRUE); }
else if (move == MOVE_TOXIC) { STATUS_ICON(opponent, badPoison: TRUE); }
else if (move == MOVE_THUNDER_WAVE) { STATUS_ICON(opponent, paralysis: TRUE); }
else if (move == MOVE_SLEEP_POWDER) { STATUS_ICON(opponent, sleep: TRUE); }
}
}

View File

@@ -106,23 +106,122 @@ SINGLE_BATTLE_TEST("If a Poison- or Steel-type Pokémon with Corrosion holds a T
SINGLE_BATTLE_TEST("If a Poison- or Steel-type Pokémon with Corrosion poisons a target with Synchronize, Synchronize will not poison Poison- or Steel-type Pokémon")
{
u16 move;
PARAMETRIZE { move = MOVE_TOXIC; }
PARAMETRIZE { move = MOVE_POISON_POWDER; }
GIVEN {
ASSUME(gMovesInfo[MOVE_TOXIC].effect == EFFECT_TOXIC);
ASSUME(gMovesInfo[MOVE_POISON_POWDER].effect == EFFECT_POISON);
PLAYER(SPECIES_SALANDIT) { Ability(ABILITY_CORROSION); }
OPPONENT(SPECIES_ABRA) { Ability(ABILITY_SYNCHRONIZE); }
} WHEN {
TURN { MOVE(player, MOVE_TOXIC); }
TURN { MOVE(player, move); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, player);
ANIMATION(ANIM_TYPE_MOVE, move, player);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent);
STATUS_ICON(opponent, badPoison: TRUE);
if (move == MOVE_TOXIC)
STATUS_ICON(opponent, badPoison: TRUE);
else
STATUS_ICON(opponent, poison: TRUE);
NONE_OF {
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, player);
STATUS_ICON(player, badPoison: TRUE);
STATUS_ICON(player, poison: TRUE);
}
}
}
TO_DO_BATTLE_TEST("Corrosion cannot bypass moves or Abilities that prevent poisoning, such as Safeguard or Immunity");
TO_DO_BATTLE_TEST("If the Pokémon with this Ability uses Magic Coat to reflect a status move that inflicts poison, the reflected move will be able to poison Poison- or Steel-type Pokémon.");
TO_DO_BATTLE_TEST("Moves used by a Pokémon with Corrosion that are reflected by Magic Coat or Magic Bounce do not retain the ability to poison Poison- or Steel-type Pokémon.")
SINGLE_BATTLE_TEST("Corrosion cannot bypass moves that prevent poisoning such as Safeguard")
{
u16 move;
PARAMETRIZE { move = MOVE_TOXIC; }
PARAMETRIZE { move = MOVE_POISON_POWDER; }
GIVEN {
ASSUME(gMovesInfo[MOVE_TOXIC].effect == EFFECT_TOXIC);
ASSUME(gMovesInfo[MOVE_POISON_POWDER].effect == EFFECT_POISON);
PLAYER(SPECIES_SALANDIT) { Ability(ABILITY_CORROSION); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_SAFEGUARD); MOVE(player, move); }
} SCENE {
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, player);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent);
STATUS_ICON(opponent, badPoison: TRUE);
STATUS_ICON(opponent, poison: TRUE);
}
}
}
SINGLE_BATTLE_TEST("Corrosion cannot bypass abilities that prevent poisoning such as Immunity")
{
u16 move;
PARAMETRIZE { move = MOVE_TOXIC; }
PARAMETRIZE { move = MOVE_POISON_POWDER; }
GIVEN {
ASSUME(gMovesInfo[MOVE_TOXIC].effect == EFFECT_TOXIC);
ASSUME(gMovesInfo[MOVE_POISON_POWDER].effect == EFFECT_POISON);
PLAYER(SPECIES_SALANDIT) { Ability(ABILITY_CORROSION); }
OPPONENT(SPECIES_SNORLAX) { Ability(ABILITY_IMMUNITY); }
} WHEN {
TURN { MOVE(player, move); }
} SCENE {
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, player);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent);
STATUS_ICON(opponent, badPoison: TRUE);
STATUS_ICON(opponent, poison: TRUE);
}
}
}
SINGLE_BATTLE_TEST("Corrosion allows the Pokémon with the ability to poison a Steel or Poison-type opponent by using Magic Coat")
{
u16 move;
PARAMETRIZE { move = MOVE_TOXIC; }
PARAMETRIZE { move = MOVE_POISON_POWDER; }
GIVEN {
ASSUME(gMovesInfo[MOVE_TOXIC].effect == EFFECT_TOXIC);
ASSUME(gMovesInfo[MOVE_POISON_POWDER].effect == EFFECT_POISON);
ASSUME(gMovesInfo[MOVE_MAGIC_COAT].effect == EFFECT_MAGIC_COAT);
PLAYER(SPECIES_SALANDIT) { Ability(ABILITY_CORROSION); }
OPPONENT(SPECIES_BELDUM);
} WHEN {
TURN { MOVE(player, MOVE_MAGIC_COAT); MOVE(opponent, move); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_MAGIC_COAT, player);
ANIMATION(ANIM_TYPE_MOVE, move, player); // Bounced by Magic Coat
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent);
if (move == MOVE_TOXIC)
STATUS_ICON(opponent, badPoison: TRUE);
else
STATUS_ICON(opponent, poison: TRUE);
}
}
SINGLE_BATTLE_TEST("Corrosion's effect is lost if the move used by the Pokémon with the ability is reflected by Magic Coat")
{
u16 move;
PARAMETRIZE { move = MOVE_TOXIC; }
PARAMETRIZE { move = MOVE_POISON_POWDER; }
GIVEN {
ASSUME(gMovesInfo[MOVE_TOXIC].effect == EFFECT_TOXIC);
ASSUME(gMovesInfo[MOVE_POISON_POWDER].effect == EFFECT_POISON);
ASSUME(gMovesInfo[MOVE_MAGIC_COAT].effect == EFFECT_MAGIC_COAT);
PLAYER(SPECIES_SALANDIT) { Ability(ABILITY_CORROSION); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_MAGIC_COAT); MOVE(player, move); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_MAGIC_COAT, opponent);
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, move, player);
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, player);
if (move == MOVE_TOXIC)
STATUS_ICON(opponent, badPoison: TRUE);
else
STATUS_ICON(opponent, poison: TRUE);
}
}
}

View File

@@ -0,0 +1,17 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Cursed Body triggers 30% of the time")
{
PASSES_RANDOMLY(3, 10, RNG_CURSED_BODY);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_FRILLISH) { Ability(ABILITY_CURSED_BODY); }
} WHEN {
TURN { MOVE(player, MOVE_AQUA_JET); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_AQUA_JET, player);
ABILITY_POPUP(opponent, ABILITY_CURSED_BODY);
MESSAGE("Wobbuffet's Aqua Jet was disabled by Foe Frillish's Cursed Body!");
}
}

View File

@@ -56,6 +56,7 @@ SINGLE_BATTLE_TEST("Download raises Sp.Attack if enemy has lower Sp. Def than De
SINGLE_BATTLE_TEST("Download doesn't activate if target hasn't been sent out yet", s16 damagePhysical, s16 damageSpecial)
{
u32 ability;
PARAMETRIZE { ability = ABILITY_TRACE; }
PARAMETRIZE { ability = ABILITY_DOWNLOAD; }
GIVEN {
@@ -73,13 +74,13 @@ SINGLE_BATTLE_TEST("Download doesn't activate if target hasn't been sent out yet
// Everyone faints.
SEND_IN_MESSAGE("Porygon");
MESSAGE("2 sent out Porygon2!");
NONE_OF {
ABILITY_POPUP(player, ABILITY_DOWNLOAD);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Porygon's Download raised its Attack!");
}
MESSAGE("2 sent out Porygon2!");
if (ability == ABILITY_DOWNLOAD)
{
ABILITY_POPUP(opponent, ABILITY_DOWNLOAD);

View File

@@ -0,0 +1,4 @@
#include "global.h"
#include "test/battle.h"
// Tests for Full Metal Body are handled in test/battle/ability/clear_body.c

View File

@@ -22,7 +22,7 @@ SINGLE_BATTLE_TEST("Ice Body recovers 1/16th of Max HP in hail.")
TURN { MOVE(opponent, MOVE_HAIL); }
} SCENE {
ABILITY_POPUP(player, ABILITY_ICE_BODY);
MESSAGE("Glalie's Ice Body healed it a little bit!");
HP_BAR(player, damage: -(100 / 16));
MESSAGE("Glalie's Ice Body healed it a little bit!");
}
}

View File

@@ -0,0 +1,24 @@
#include "global.h"
#include "test/battle.h"
// This test is eyes on only
SINGLE_BATTLE_TEST("Illusion can only imitate Normal Form terapagos")
{
GIVEN {
PLAYER(SPECIES_ZOROARK) { Moves(MOVE_CELEBRATE); }
PLAYER(SPECIES_TERAPAGOS) { Moves(MOVE_CELEBRATE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
// Zoroark is out, should be normal form Terapagos
// Switch to Terapagos which enters Terastal Form
TURN { SWITCH(player, 1); }
// Switch back to Zoroark, should not be Terastal Terapagos
TURN { SWITCH(player, 0); MOVE(opponent, MOVE_TACKLE);}
// Switch back to Terapagos
TURN { SWITCH(player, 1); }
// Terapagos Stellar, Zoroark gets Roared in, should not be Stellar Terapagos
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); MOVE(opponent, MOVE_ROAR); }
// Reveal the Zoroark
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_TACKLE); }
}
}

View File

@@ -78,23 +78,29 @@ DOUBLE_BATTLE_TEST("Intimidate doesn't activate on an empty field in a double ba
// Everyone faints.
SEND_IN_MESSAGE("Ekans");
MESSAGE("2 sent out Arbok!");
SEND_IN_MESSAGE("Abra");
MESSAGE("2 sent out Wynaut!");
NONE_OF {
ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
MESSAGE("Ekans's Intimidate cuts Foe Arbok's attack!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
MESSAGE("Ekans's Intimidate cuts Foe Wynaut's attack!");
}
MESSAGE("2 sent out Arbok!");
NONE_OF {
ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
MESSAGE("Foe Arbok's Intimidate cuts Ekans's attack!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
MESSAGE("Foe Arbok's Intimidate cuts Abra's attack!");
}
SEND_IN_MESSAGE("Abra");
MESSAGE("2 sent out Wynaut!");
// Intimidate activates after all battlers have been brought out
ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
MESSAGE("Ekans's Intimidate cuts Foe Arbok's attack!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
MESSAGE("Ekans's Intimidate cuts Foe Wynaut's attack!");
ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
MESSAGE("Foe Arbok's Intimidate cuts Ekans's attack!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
MESSAGE("Foe Arbok's Intimidate cuts Abra's attack!");
}
}
@@ -211,6 +217,35 @@ SINGLE_BATTLE_TEST("Intimidate can not further lower opponents Atk stat if it is
}
}
DOUBLE_BATTLE_TEST("Intimidate is not going to trigger if a mon switches out through u-turn and the opposing field is empty")
{
GIVEN {
PLAYER(SPECIES_WYNAUT);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); }
OPPONENT(SPECIES_WYNAUT) { HP(1); }
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_TREECKO);
OPPONENT(SPECIES_TORCHIC);
} WHEN {
TURN {
MOVE(opponentRight, MOVE_HEALING_WISH);
MOVE(playerLeft, MOVE_U_TURN, target: opponentLeft);
SEND_OUT(playerLeft, 2);
SEND_OUT(opponentLeft, 2);
SEND_OUT(opponentRight, 3);
}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerRight);
ANIMATION(ANIM_TYPE_MOVE, MOVE_HEALING_WISH, opponentRight);
ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, playerLeft);
HP_BAR(opponentLeft);
MESSAGE("2 sent out Treecko!");
MESSAGE("2 sent out Torchic!");
NOT ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE);
}
}
SINGLE_BATTLE_TEST("Intimidate activates when it's no longer effected by Neutralizing Gas")
{
GIVEN {

View File

@@ -0,0 +1,73 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Lightning Rod absorbs Electric-type moves and increases the Sp. Attack [Gen5+]")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_THUNDERBOLT].type == TYPE_ELECTRIC);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_RAICHU) { Ability(ABILITY_LIGHTNING_ROD); }
} WHEN {
TURN { MOVE(player, MOVE_THUNDERBOLT); MOVE(opponent, MOVE_CELEBRATE); }
} SCENE {
if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5) {
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, player);
HP_BAR(opponent);
};
ABILITY_POPUP(opponent, ABILITY_LIGHTNING_ROD);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Foe Raichu's Sp. Atk rose!");
} else {
NONE_OF {
ABILITY_POPUP(opponent, ABILITY_LIGHTNING_ROD);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Foe Raichu's Sp. Atk rose!");
};
ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, player);
HP_BAR(opponent);
}
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
}
}
DOUBLE_BATTLE_TEST("Lightning Rod forces single-target Electric-type moves to target the Pokémon with this Ability.")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_THUNDERBOLT].type == TYPE_ELECTRIC);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_RAICHU) { Ability(ABILITY_LIGHTNING_ROD); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN {
MOVE(playerLeft, MOVE_THUNDERBOLT, target: opponentRight);
MOVE(playerRight, MOVE_THUNDERBOLT, target: opponentRight);
MOVE(opponentLeft, MOVE_CELEBRATE);
MOVE(opponentRight, MOVE_CELEBRATE);
}
} SCENE {
if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5) {
NONE_OF {
HP_BAR(opponentLeft);
HP_BAR(opponentRight);
};
ABILITY_POPUP(opponentLeft, ABILITY_LIGHTNING_ROD);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
MESSAGE("Foe Raichu's Sp. Atk rose!");
ABILITY_POPUP(opponentLeft, ABILITY_LIGHTNING_ROD);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
MESSAGE("Foe Raichu's Sp. Atk rose!");
} else {
NONE_OF {
HP_BAR(opponentRight);
};
ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, playerLeft);
HP_BAR(opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, playerRight);
HP_BAR(opponentLeft);
}
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentRight);
}
}

View File

@@ -49,8 +49,8 @@ AI_SINGLE_BATTLE_TEST("AI doesn't use accuracy-lowering moves if it knows that t
for (j = MOVE_NONE + 1; j < MOVES_COUNT; j++)
{
if (gMovesInfo[j].effect == EFFECT_ACCURACY_DOWN || gMovesInfo[j].effect == EFFECT_ACCURACY_DOWN_2) {
PARAMETRIZE{ moveAI = j; abilityAI = ABILITY_SWIFT_SWIM; }
PARAMETRIZE{ moveAI = j; abilityAI = ABILITY_MOLD_BREAKER; }
PARAMETRIZE { moveAI = j; abilityAI = ABILITY_SWIFT_SWIM; }
PARAMETRIZE { moveAI = j; abilityAI = ABILITY_MOLD_BREAKER; }
}
}

View File

@@ -16,16 +16,16 @@ SINGLE_BATTLE_TEST("Opportunist only copies foe's positive stat changes in a tur
OPPONENT(SPECIES_ESPATHRA) { Speed(5); Ability(ability); }
} WHEN {
TURN { MOVE(player, MOVE_SHELL_SMASH); }
TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_TACKLE); }
TURN { MOVE(opponent, MOVE_TACKLE); }
} SCENE {
if (ability == ABILITY_FRISK) {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_SMASH, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
HP_BAR(player, captureDamage: &results[i].damage);
} else {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_SMASH, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
HP_BAR(player, captureDamage: &results[i].damage);
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
}
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[1].damage);
@@ -78,12 +78,10 @@ DOUBLE_BATTLE_TEST("Opportunist raises Attack only once when partner has Intimid
if ((abilityLeft == ABILITY_CONTRARY && abilityRight != ABILITY_CONTRARY)
|| (abilityLeft != ABILITY_CONTRARY && abilityRight == ABILITY_CONTRARY)) {
ABILITY_POPUP(playerRight, ABILITY_OPPORTUNIST);
MESSAGE("Espathra copied its opponent's stat changes!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
MESSAGE("Espathra's Attack rose!");
} else if (abilityLeft == ABILITY_CONTRARY && abilityRight == ABILITY_CONTRARY) {
ABILITY_POPUP(playerRight, ABILITY_OPPORTUNIST);
MESSAGE("Espathra copied its opponent's stat changes!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
MESSAGE("Espathra's Attack sharply rose!");
}
@@ -124,6 +122,177 @@ SINGLE_BATTLE_TEST("Opportunist does not accumulate opposing mon's stat changes"
}
}
TO_DO_BATTLE_TEST("Opportunist doesn't copy ally stat increases");
TO_DO_BATTLE_TEST("Opportunist doesn't copy foe stat increases gained via Opportunist");
TO_DO_BATTLE_TEST("Opportunist copies foe stat increased gained via Swagger and Flatter");
SINGLE_BATTLE_TEST("Opportunist copies each stat increase individually from ability and move")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_ZACIAN) { Ability(ABILITY_INTREPID_SWORD); }
OPPONENT(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); }
} WHEN {
TURN { SWITCH(player, 1); }
TURN { MOVE(player, MOVE_SWORDS_DANCE); }
} SCENE {
ABILITY_POPUP(player, ABILITY_INTREPID_SWORD);
ABILITY_POPUP(opponent, ABILITY_OPPORTUNIST);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, player);
ABILITY_POPUP(opponent, ABILITY_OPPORTUNIST);
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 3);
}
}
SINGLE_BATTLE_TEST("Opportunist doesn't copy foe stat increases gained via Opportunist")
{
GIVEN {
PLAYER(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); }
OPPONENT(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); }
} WHEN {
TURN { MOVE(player, MOVE_SWORDS_DANCE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, player);
ABILITY_POPUP(opponent, ABILITY_OPPORTUNIST);
NOT ABILITY_POPUP(player, ABILITY_OPPORTUNIST);
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], player->statStages[STAT_ATK]);
}
}
SINGLE_BATTLE_TEST("Opportunist copies foe stat increase gained via Swagger and Flatter")
{
GIVEN {
PLAYER(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); }
OPPONENT(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); }
} WHEN {
TURN { MOVE(opponent, MOVE_FLATTER); }
TURN { MOVE(opponent, MOVE_SWAGGER); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_FLATTER, opponent);
ABILITY_POPUP(opponent, ABILITY_OPPORTUNIST);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SWAGGER, opponent);
ABILITY_POPUP(opponent, ABILITY_OPPORTUNIST);
} THEN {
EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 2);
}
}
DOUBLE_BATTLE_TEST("Opportunist doesn't copy ally stat increases")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); }
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(playerLeft, MOVE_SWORDS_DANCE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, playerLeft);
NOT ABILITY_POPUP(playerRight, ABILITY_OPPORTUNIST);
} THEN {
EXPECT_EQ(playerRight->statStages[STAT_SPATK], DEFAULT_STAT_STAGE );
}
}
DOUBLE_BATTLE_TEST("Opportunist copies the stat increase of each opposing mon")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); }
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponentRight, MOVE_SWORDS_DANCE); MOVE(opponentLeft, MOVE_SWORDS_DANCE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, opponentRight);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, opponentLeft);
ABILITY_POPUP(playerRight, ABILITY_OPPORTUNIST);
} THEN {
EXPECT_EQ(playerRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 4);
}
}
DOUBLE_BATTLE_TEST("Opportunist copies the stat of each pokemon that were raised at the same time")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); }
OPPONENT(SPECIES_ZACIAN) { Ability(ABILITY_INTREPID_SWORD); }
OPPONENT(SPECIES_ZACIAN) { Ability(ABILITY_INTREPID_SWORD); }
} WHEN {
TURN { }
} SCENE {
ABILITY_POPUP(opponentLeft, ABILITY_INTREPID_SWORD);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
ABILITY_POPUP(opponentRight, ABILITY_INTREPID_SWORD);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
ABILITY_POPUP(playerRight, ABILITY_OPPORTUNIST);
MESSAGE("Espathra's Attack sharply rose!");
} THEN {
EXPECT_EQ(playerRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 2);
}
}
SINGLE_BATTLE_TEST("Opportunist copies the increase not the stages")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); }
} WHEN {
TURN { MOVE(player, MOVE_CHARM); MOVE(opponent, MOVE_CHARM); }
TURN { MOVE(player, MOVE_CHARM); MOVE(opponent, MOVE_CHARM); }
TURN { MOVE(player, MOVE_CHARM); MOVE(opponent, MOVE_GROWL); }
TURN { MOVE(player, MOVE_BELLY_DRUM); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CHARM, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CHARM, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CHARM, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CHARM, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CHARM, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_GROWL, opponent);
ABILITY_POPUP(opponent, ABILITY_OPPORTUNIST);
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 5); // + 11
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 6); // + 11
}
}
SINGLE_BATTLE_TEST("Opportunist copies the stat increase from the incoming mon")
{
GIVEN {
PLAYER(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); }
OPPONENT(SPECIES_WOBBUFFET) { HP(1); }
OPPONENT(SPECIES_ZACIAN) { Ability(ABILITY_INTREPID_SWORD); }
} WHEN {
TURN { MOVE(player, MOVE_TACKLE); SEND_OUT(opponent, 1); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
ABILITY_POPUP(opponent, ABILITY_INTREPID_SWORD);
ABILITY_POPUP(player, ABILITY_OPPORTUNIST);
} THEN {
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
}
}
SINGLE_BATTLE_TEST("Opportunist and Mirror Herb stack stat increases")
{
GIVEN {
PLAYER(SPECIES_ZACIAN) { Ability(ABILITY_INTREPID_SWORD); }
OPPONENT(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); Item(ITEM_MIRROR_HERB); }
} WHEN {
TURN { }
} SCENE {
ABILITY_POPUP(player, ABILITY_INTREPID_SWORD);
ABILITY_POPUP(opponent, ABILITY_OPPORTUNIST);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 2);
EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPEED], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE);
}
}

View File

@@ -56,6 +56,23 @@ SINGLE_BATTLE_TEST("Own Tempo prevents confusion from moves by the user")
}
}
SINGLE_BATTLE_TEST("Own Tempo is ignored by Mold Breaker")
{
KNOWN_FAILING; // Ideally the func CanBeConfused should be split into AttackerCanBeConfused and TargetCanBeConfused or we do it in the same func but have a check for when battlerAtk == battlerDef
GIVEN {
ASSUME(gMovesInfo[MOVE_CONFUSE_RAY].effect == EFFECT_CONFUSE);
PLAYER(SPECIES_PINSIR) { Ability(ABILITY_MOLD_BREAKER); }
OPPONENT(SPECIES_SLOWPOKE) { Ability(ABILITY_OWN_TEMPO); };
} WHEN {
TURN { MOVE(player, MOVE_CONFUSE_RAY); }
} SCENE {
NONE_OF {
ABILITY_POPUP(opponent, ABILITY_OWN_TEMPO);
MESSAGE("Foe Slowpoke's Own Tempo prevents confusion!");
}
}
}
SINGLE_BATTLE_TEST("Own Tempo cures confusion obtained from an opponent with Mold Breaker")
{
KNOWN_FAILING;

View File

@@ -239,6 +239,30 @@ SINGLE_BATTLE_TEST("Parental Bond has no affect on multi hit moves and they stil
}
}
SINGLE_BATTLE_TEST("Parental Bond Smack Down effect triggers after 2nd hit")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_SMACK_DOWN].category != DAMAGE_CATEGORY_STATUS);
ASSUME(gMovesInfo[MOVE_SMACK_DOWN].strikeCount < 2);
ASSUME(MoveHasAdditionalEffect(MOVE_SMACK_DOWN, MOVE_EFFECT_SMACK_DOWN));
PLAYER(SPECIES_KANGASKHAN) { Item(ITEM_KANGASKHANITE); }
OPPONENT(SPECIES_SKARMORY);
} WHEN {
TURN { MOVE(player, MOVE_SMACK_DOWN, gimmick: GIMMICK_MEGA); }
} SCENE {
MESSAGE("Kangaskhan's Kangaskhanite is reacting to 1's Mega Ring!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, player);
MESSAGE("Kangaskhan has Mega Evolved into Mega Kangaskhan!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SMACK_DOWN, player);
HP_BAR(opponent);
NOT MESSAGE("Foe Skarmory fell straight down!");
HP_BAR(opponent);
MESSAGE("Foe Skarmory fell straight down!");
} THEN {
EXPECT_EQ(player->species, SPECIES_KANGASKHAN_MEGA);
}
}
TO_DO_BATTLE_TEST("Parental Bond tests");
// Temporary TODO: Convert Bulbapedia description into tests.

View File

@@ -0,0 +1,77 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Poison Touch has a 30% chance to poison when attacking with contact moves")
{
PASSES_RANDOMLY(3, 10, RNG_POISON_TOUCH);
GIVEN {
ASSUME(gMovesInfo[MOVE_TACKLE].power > 0);
ASSUME(gMovesInfo[MOVE_TACKLE].makesContact);
PLAYER(SPECIES_GRIMER) { Ability(ABILITY_POISON_TOUCH); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TACKLE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
ABILITY_POPUP(player, ABILITY_POISON_TOUCH);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent);
MESSAGE("Foe Wobbuffet was poisoned by Grimer's Poison Touch!");
STATUS_ICON(opponent, poison: TRUE);
}
}
SINGLE_BATTLE_TEST("Poison Touch only applies when using contact moves")
{
u32 move;
PARAMETRIZE { move = MOVE_TACKLE; }
PARAMETRIZE { move = MOVE_SWIFT; }
GIVEN {
ASSUME(gMovesInfo[MOVE_TACKLE].makesContact);
ASSUME(!gMovesInfo[MOVE_SWIFT].makesContact);
PLAYER(SPECIES_GRIMER) { Ability(ABILITY_POISON_TOUCH); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, move); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, move, player);
if (gMovesInfo[move].makesContact) {
ABILITY_POPUP(player, ABILITY_POISON_TOUCH);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent);
MESSAGE("Foe Wobbuffet was poisoned by Grimer's Poison Touch!");
STATUS_ICON(opponent, poison: TRUE);
} else {
NONE_OF {
ABILITY_POPUP(player, ABILITY_POISON_TOUCH);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent);
MESSAGE("Foe Wobbuffet was poisoned by Grimer's Poison Touch!");
STATUS_ICON(opponent, poison: TRUE);
}
}
}
}
SINGLE_BATTLE_TEST("Poison Touch applies between multi-hit move hits")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_ARM_THRUST].effect == EFFECT_MULTI_HIT);
ASSUME(gMovesInfo[MOVE_ARM_THRUST].makesContact);
ASSUME(gItemsInfo[ITEM_PECHA_BERRY].holdEffect == HOLD_EFFECT_CURE_PSN);
PLAYER(SPECIES_GRIMER) { Ability(ABILITY_POISON_TOUCH); }
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_PECHA_BERRY); };
} WHEN {
TURN { MOVE(player, MOVE_ARM_THRUST); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ARM_THRUST, player);
ABILITY_POPUP(player, ABILITY_POISON_TOUCH);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent);
MESSAGE("Foe Wobbuffet was poisoned by Grimer's Poison Touch!");
STATUS_ICON(opponent, poison: TRUE);
MESSAGE("Foe Wobbuffet's Pecha Berry cured poison!");
STATUS_ICON(opponent, poison: FALSE);
ABILITY_POPUP(player, ABILITY_POISON_TOUCH);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent);
MESSAGE("Foe Wobbuffet was poisoned by Grimer's Poison Touch!");
STATUS_ICON(opponent, poison: TRUE);
}
}

View File

@@ -0,0 +1,18 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Sticky Hold prevents item theft")
{
GIVEN {
ASSUME(MoveHasAdditionalEffect(MOVE_THIEF, MOVE_EFFECT_STEAL_ITEM));
PLAYER(SPECIES_URSALUNA) { Item(ITEM_NONE); }
OPPONENT(SPECIES_GASTRODON) { Ability(ABILITY_STICKY_HOLD); Item(ITEM_LIFE_ORB); }
} WHEN {
TURN { MOVE(player, MOVE_THIEF); }
} SCENE {
MESSAGE("Ursaluna used Thief!");
ABILITY_POPUP(opponent, ABILITY_STICKY_HOLD);
MESSAGE("Foe Gastrodon's Sticky Hold made Thief ineffective!");
}
}

View File

@@ -0,0 +1,73 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Storm Drain absorbs Water-type moves and increases the Sp. Attack [Gen5+]")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_WATER_GUN].type == TYPE_WATER);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_GASTRODON_EAST_SEA) { Ability(ABILITY_STORM_DRAIN); }
} WHEN {
TURN { MOVE(player, MOVE_WATER_GUN); MOVE(opponent, MOVE_CELEBRATE); }
} SCENE {
if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5) {
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, player);
HP_BAR(opponent);
};
ABILITY_POPUP(opponent, ABILITY_STORM_DRAIN);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Foe Gastrodon's Sp. Atk rose!");
} else {
NONE_OF {
ABILITY_POPUP(opponent, ABILITY_STORM_DRAIN);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Foe Gastrodon's Sp. Atk rose!");
};
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, player);
HP_BAR(opponent);
}
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
}
}
DOUBLE_BATTLE_TEST("Storm Drain forces single-target Water-type moves to target the Pokémon with this Ability.")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_WATER_GUN].type == TYPE_WATER);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_GASTRODON_EAST_SEA) { Ability(ABILITY_STORM_DRAIN); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN {
MOVE(playerLeft, MOVE_WATER_GUN, target: opponentRight);
MOVE(playerRight, MOVE_WATER_GUN, target: opponentRight);
MOVE(opponentLeft, MOVE_CELEBRATE);
MOVE(opponentRight, MOVE_CELEBRATE);
}
} SCENE {
if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5) {
NONE_OF {
HP_BAR(opponentLeft);
HP_BAR(opponentRight);
};
ABILITY_POPUP(opponentLeft, ABILITY_STORM_DRAIN);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
MESSAGE("Foe Gastrodon's Sp. Atk rose!");
ABILITY_POPUP(opponentLeft, ABILITY_STORM_DRAIN);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
MESSAGE("Foe Gastrodon's Sp. Atk rose!");
} else {
NONE_OF {
HP_BAR(opponentRight);
};
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, playerLeft);
HP_BAR(opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, playerRight);
HP_BAR(opponentLeft);
}
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentRight);
}
}

View File

@@ -107,9 +107,9 @@ SINGLE_BATTLE_TEST("Supreme Overlord's message displays correctly after all batt
ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, opponent);
// Everyone faints.
SEND_IN_MESSAGE("Kingambit");
MESSAGE("2 sent out Wobbuffet!");
ABILITY_POPUP(player, ABILITY_SUPREME_OVERLORD);
MESSAGE("Kingambit gained strength from the fallen!");
MESSAGE("2 sent out Wobbuffet!");
}
}

View File

@@ -0,0 +1,128 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Switch-in abilities trigger in Speed Order at the battle's start - Single Battle")
{
u32 spdPlayer, spdOpponent;
PARAMETRIZE { spdPlayer = 5; spdOpponent = 1; }
PARAMETRIZE { spdOpponent = 5; spdPlayer = 1; }
GIVEN {
PLAYER(SPECIES_EKANS) { Speed(spdPlayer); Ability(ABILITY_INTIMIDATE); }
OPPONENT(SPECIES_NINETALES) { Speed(spdOpponent); Ability(ABILITY_DROUGHT); }
} WHEN {
TURN { ; }
} SCENE {
if (spdPlayer > spdOpponent) {
ABILITY_POPUP(player, ABILITY_INTIMIDATE);
ABILITY_POPUP(opponent, ABILITY_DROUGHT);
} else {
ABILITY_POPUP(opponent, ABILITY_DROUGHT);
ABILITY_POPUP(player, ABILITY_INTIMIDATE);
}
}
}
DOUBLE_BATTLE_TEST("Switch-in abilities trigger in Speed Order at the battle's start - Double Battle")
{
u32 spdPlayer1, spdPlayer2, spdOpponent1, spdOpponent2;
PARAMETRIZE { spdPlayer1 = 5; spdPlayer2 = 4; spdOpponent1 = 3; spdOpponent2 = 2; }
PARAMETRIZE { spdPlayer1 = 2; spdPlayer2 = 3; spdOpponent1 = 4; spdOpponent2 = 5; }
PARAMETRIZE { spdPlayer1 = 4; spdPlayer2 = 3; spdOpponent1 = 5; spdOpponent2 = 2; }
GIVEN {
PLAYER(SPECIES_KYOGRE) { Speed(spdPlayer1); Ability(ABILITY_DRIZZLE); }
PLAYER(SPECIES_GYARADOS) { Speed(spdPlayer2); Ability(ABILITY_INTIMIDATE); }
OPPONENT(SPECIES_PORYGON2) { Speed(spdOpponent1); Ability(ABILITY_DOWNLOAD); }
OPPONENT(SPECIES_PINSIR) { Speed(spdOpponent2); Ability(ABILITY_MOLD_BREAKER); }
} WHEN {
TURN { ; }
} SCENE {
if (spdPlayer1 == 5) {
ABILITY_POPUP(playerLeft, ABILITY_DRIZZLE);
ABILITY_POPUP(playerRight, ABILITY_INTIMIDATE);
ABILITY_POPUP(opponentLeft, ABILITY_DOWNLOAD);
ABILITY_POPUP(opponentRight, ABILITY_MOLD_BREAKER);
} else if (spdOpponent2 == 5) {
ABILITY_POPUP(opponentRight, ABILITY_MOLD_BREAKER);
ABILITY_POPUP(opponentLeft, ABILITY_DOWNLOAD);
ABILITY_POPUP(playerRight, ABILITY_INTIMIDATE);
ABILITY_POPUP(playerLeft, ABILITY_DRIZZLE);
} else {
ABILITY_POPUP(opponentLeft, ABILITY_DOWNLOAD);
ABILITY_POPUP(playerLeft, ABILITY_DRIZZLE);
ABILITY_POPUP(playerRight, ABILITY_INTIMIDATE);
ABILITY_POPUP(opponentRight, ABILITY_MOLD_BREAKER);
}
}
}
SINGLE_BATTLE_TEST("Switch-in abilities trigger in Speed Order after post-KO switch - Single Battle")
{
u32 spdPlayer, spdOpponent;
PARAMETRIZE { spdPlayer = 5; spdOpponent = 1; }
PARAMETRIZE { spdOpponent = 5; spdPlayer = 1; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(1); Speed(1); }
PLAYER(SPECIES_EKANS) { Speed(spdPlayer); Ability(ABILITY_INTIMIDATE); }
OPPONENT(SPECIES_WOBBUFFET) { HP(1); Speed(1); }
OPPONENT(SPECIES_PORYGON2) { Speed(spdOpponent); Ability(ABILITY_DOWNLOAD); }
} WHEN {
TURN { MOVE(player, MOVE_EXPLOSION); SEND_OUT(player, 1); SEND_OUT(opponent, 1); }
TURN { ; }
} SCENE {
MESSAGE("Wobbuffet used Explosion!");
if (spdPlayer > spdOpponent) {
ABILITY_POPUP(player, ABILITY_INTIMIDATE);
ABILITY_POPUP(opponent, ABILITY_DOWNLOAD);
} else {
ABILITY_POPUP(opponent, ABILITY_DOWNLOAD);
ABILITY_POPUP(player, ABILITY_INTIMIDATE);
}
}
}
DOUBLE_BATTLE_TEST("Switch-in abilities trigger in Speed Order after post-KO switch - Double Battle")
{
u32 spdPlayer1, spdPlayer2, spdOpponent1, spdOpponent2;
PARAMETRIZE { spdPlayer1 = 5; spdPlayer2 = 4; spdOpponent1 = 3; spdOpponent2 = 2; }
PARAMETRIZE { spdPlayer1 = 2; spdPlayer2 = 3; spdOpponent1 = 4; spdOpponent2 = 5; }
PARAMETRIZE { spdPlayer1 = 4; spdPlayer2 = 3; spdOpponent1 = 5; spdOpponent2 = 2; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(1); Speed(1); }
PLAYER(SPECIES_WOBBUFFET) { HP(1); Speed(1); }
PLAYER(SPECIES_TYRANITAR) { Speed(spdPlayer1); Ability(ABILITY_SAND_STREAM); }
PLAYER(SPECIES_GYARADOS) { Speed(spdPlayer2); Ability(ABILITY_INTIMIDATE); }
OPPONENT(SPECIES_WOBBUFFET) { HP(1); Speed(1); }
OPPONENT(SPECIES_WOBBUFFET) { HP(1); Speed(1); }
OPPONENT(SPECIES_WEEZING_GALARIAN) { Speed(spdOpponent1); Ability(ABILITY_MISTY_SURGE); }
OPPONENT(SPECIES_VULPIX_ALOLAN) { Speed(spdOpponent2); Ability(ABILITY_SNOW_WARNING); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_EXPLOSION); SEND_OUT(playerLeft, 2); SEND_OUT(opponentLeft, 2); SEND_OUT(playerRight, 3); SEND_OUT(opponentRight, 3); }
TURN { ; }
} SCENE {
MESSAGE("Wobbuffet used Explosion!");
if (spdPlayer1 == 5) {
ABILITY_POPUP(playerLeft, ABILITY_SAND_STREAM);
ABILITY_POPUP(playerRight, ABILITY_INTIMIDATE);
ABILITY_POPUP(opponentLeft, ABILITY_MISTY_SURGE);
ABILITY_POPUP(opponentRight, ABILITY_SNOW_WARNING);
} else if (spdOpponent2 == 5) {
ABILITY_POPUP(opponentRight, ABILITY_SNOW_WARNING);
ABILITY_POPUP(opponentLeft, ABILITY_MISTY_SURGE);
ABILITY_POPUP(playerRight, ABILITY_INTIMIDATE);
ABILITY_POPUP(playerLeft, ABILITY_SAND_STREAM);
} else {
ABILITY_POPUP(opponentLeft, ABILITY_MISTY_SURGE);
ABILITY_POPUP(playerLeft, ABILITY_SAND_STREAM);
ABILITY_POPUP(playerRight, ABILITY_INTIMIDATE);
ABILITY_POPUP(opponentRight, ABILITY_SNOW_WARNING);
}
}
}

View File

@@ -46,9 +46,9 @@ SINGLE_BATTLE_TEST("Sword of Ruin's message displays correctly after all battler
ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, opponent);
// Everyone faints.
MESSAGE("Go! Chien-Pao!");
MESSAGE("2 sent out Wobbuffet!");
ABILITY_POPUP(player, ABILITY_SWORD_OF_RUIN);
MESSAGE("Chien-Pao's Sword of Ruin weakened the Defense of all surrounding Pokémon!");
MESSAGE("2 sent out Wobbuffet!");
}
}

View File

@@ -46,9 +46,9 @@ SINGLE_BATTLE_TEST("Tablets of Ruin's message displays correctly after all battl
ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, opponent);
// Everyone faints.
MESSAGE("Go! Wo-Chien!");
MESSAGE("2 sent out Wobbuffet!");
ABILITY_POPUP(player, ABILITY_TABLETS_OF_RUIN);
MESSAGE("Wo-Chien's Tablets of Ruin weakened the Attack of all surrounding Pokémon!");
MESSAGE("2 sent out Wobbuffet!");
}
}

View File

@@ -0,0 +1,102 @@
#include "global.h"
#include "test/battle.h"
DOUBLE_BATTLE_TEST("Teraform Zero clears weather and terrain upon activation")
{
GIVEN {
PLAYER(SPECIES_TERAPAGOS_TERASTAL);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_KYOGRE) {Ability(ABILITY_DRIZZLE); }
OPPONENT(SPECIES_TAPU_KOKO) {Ability(ABILITY_ELECTRIC_SURGE); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); }
} SCENE {
ABILITY_POPUP(playerLeft, ABILITY_TERAFORM_ZERO);
MESSAGE("The rain stopped.");
MESSAGE("The electricity disappeared from the battlefield.");
}
}
DOUBLE_BATTLE_TEST("Teraform Zero can be supressed")
{
GIVEN {
PLAYER(SPECIES_TERAPAGOS_TERASTAL);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WEEZING) {Ability(ABILITY_NEUTRALIZING_GAS); }
OPPONENT(SPECIES_KYOGRE) {Ability(ABILITY_DRIZZLE); }
OPPONENT(SPECIES_TAPU_KOKO) {Ability(ABILITY_ELECTRIC_SURGE); }
} WHEN {
TURN { SWITCH(playerRight, 2); MOVE(playerLeft, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); }
} SCENE {
ABILITY_POPUP(playerRight, ABILITY_NEUTRALIZING_GAS);
NONE_OF {
MESSAGE("The rain stopped.");
MESSAGE("The electricity disappeared from the battlefield.");
}
}
}
SINGLE_BATTLE_TEST("Teraform Zero can be replaced")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_WORRY_SEED].effect == EFFECT_WORRY_SEED);
ASSUME(gMovesInfo[MOVE_REST].effect == EFFECT_REST);
PLAYER(SPECIES_TERAPAGOS);
OPPONENT(SPECIES_WHIMSICOTT) { Ability(ABILITY_PRANKSTER); }
} WHEN {
TURN { MOVE(opponent, MOVE_WORRY_SEED); MOVE(player, MOVE_REST, gimmick: GIMMICK_TERA); }
} SCENE {
MESSAGE("Foe Whimsicott used Worry Seed!");
MESSAGE("Terapagos acquired Insomnia!");
MESSAGE("Terapagos used Rest!");
ABILITY_POPUP(player, ABILITY_INSOMNIA);
MESSAGE("Terapagos stayed awake using its Insomnia!");
}
}
SINGLE_BATTLE_TEST("Teraform Zero cannot be swapped")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_SKILL_SWAP].effect == EFFECT_SKILL_SWAP);
PLAYER(SPECIES_TERAPAGOS);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); MOVE(opponent, MOVE_SKILL_SWAP); }
} SCENE {
MESSAGE("Foe Wobbuffet used Skill Swap!");
MESSAGE("But it failed!");
}
}
SINGLE_BATTLE_TEST("Teraform Zero cannot be copied")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_ROLE_PLAY].effect == EFFECT_ROLE_PLAY);
PLAYER(SPECIES_TERAPAGOS);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); MOVE(opponent, MOVE_ROLE_PLAY); }
} SCENE {
MESSAGE("Foe Wobbuffet used Role Play!");
MESSAGE("But it failed!");
}
}
DOUBLE_BATTLE_TEST("Teraform Zero shouldn't cause Neutralizing Gas to show it's popup when trying to activate")
{
KNOWN_FAILING; // #5010
GIVEN {
PLAYER(SPECIES_TERAPAGOS_TERASTAL);
PLAYER(SPECIES_ABSOL) {Ability(ABILITY_PRESSURE); }
PLAYER(SPECIES_WEEZING) {Ability(ABILITY_NEUTRALIZING_GAS); }
OPPONENT(SPECIES_KYOGRE) {Ability(ABILITY_DRIZZLE); }
OPPONENT(SPECIES_TAPU_KOKO) {Ability(ABILITY_ELECTRIC_SURGE); }
} WHEN {
TURN { SWITCH(playerRight, 2); MOVE(playerLeft, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); }
} SCENE {
MESSAGE("Terapagos is storing energy!");
MESSAGE("Terapagos terastalized into the Stellar type!");
NOT ABILITY_POPUP(playerRight, ABILITY_NEUTRALIZING_GAS);
MESSAGE("Terapagos used Celebreate!");
}
}

View File

@@ -79,3 +79,20 @@ SINGLE_BATTLE_TEST("Trace will copy an opponent's ability whenever it has the ch
MESSAGE("Ralts TRACED Foe Torchic's Blaze!");
}
}
DOUBLE_BATTLE_TEST("Trace respects the turn order")
{
GIVEN {
PLAYER(SPECIES_DEOXYS_SPEED) { Speed(40); Ability(ABILITY_PRESSURE); }
PLAYER(SPECIES_GARDEVOIR) { Speed(20); Ability(ABILITY_TRACE); }
OPPONENT(SPECIES_HIPPOWDON) { Speed(10); Ability(ABILITY_SAND_STREAM); }
OPPONENT(SPECIES_DEOXYS_SPEED) { Speed(30); Ability(ABILITY_PRESSURE); }
} WHEN {
TURN { }
} SCENE {
ABILITY_POPUP(playerLeft, ABILITY_PRESSURE);
ABILITY_POPUP(opponentRight, ABILITY_PRESSURE);
ABILITY_POPUP(playerRight, ABILITY_TRACE);
ABILITY_POPUP(opponentLeft, ABILITY_SAND_STREAM);
}
}

View File

@@ -46,9 +46,9 @@ SINGLE_BATTLE_TEST("Vessel of Ruin's message displays correctly after all battle
ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, opponent);
// Everyone faints.
MESSAGE("Go! Ting-Lu!");
MESSAGE("2 sent out Wobbuffet!");
ABILITY_POPUP(player, ABILITY_VESSEL_OF_RUIN);
MESSAGE("Ting-Lu's Vessel of Ruin weakened the Sp. Atk of all surrounding Pokémon!");
MESSAGE("2 sent out Wobbuffet!");
}
}

View File

@@ -0,0 +1,4 @@
#include "global.h"
#include "test/battle.h"
// Tests for White Smoke are handled in test/battle/ability/clear_body.c

View File

@@ -153,9 +153,9 @@ SINGLE_BATTLE_TEST("Zero to Hero's message displays correctly after all battlers
ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, opponent);
// Everyone faints.
SEND_IN_MESSAGE("Palafin");
MESSAGE("2 sent out Wobbuffet!");
ABILITY_POPUP(player, ABILITY_ZERO_TO_HERO);
MESSAGE("Palafin underwent a heroic transformation!");
MESSAGE("2 sent out Wobbuffet!");
}
}

View File

@@ -402,9 +402,9 @@ AI_DOUBLE_BATTLE_TEST("AI will not use Helping Hand if partner does not have any
{
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; }
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);
@@ -431,7 +431,7 @@ AI_DOUBLE_BATTLE_TEST("AI will not use a status move if partner already chose He
for (j = MOVE_NONE + 1; j < MOVES_COUNT; j++)
{
if (gMovesInfo[j].category == DAMAGE_CATEGORY_STATUS) {
PARAMETRIZE{ statusMove = j; }
PARAMETRIZE { statusMove = j; }
}
}

View File

@@ -20,8 +20,8 @@ AI_SINGLE_BATTLE_TEST("Choiced Pokémon switch out after using a status move onc
for (j = 0; j < ARRAY_COUNT(choiceItems); j++)
{
PARAMETRIZE{ ability = ABILITY_NONE; heldItem = choiceItems[j]; }
PARAMETRIZE{ ability = ABILITY_KLUTZ; heldItem = choiceItems[j]; }
PARAMETRIZE { ability = ABILITY_NONE; heldItem = choiceItems[j]; }
PARAMETRIZE { ability = ABILITY_KLUTZ; heldItem = choiceItems[j]; }
}
GIVEN {
@@ -55,8 +55,8 @@ AI_SINGLE_BATTLE_TEST("Choiced Pokémon won't use stat boosting moves")
for (j = 0; j < ARRAY_COUNT(choiceItems); j++)
{
PARAMETRIZE{ ability = ABILITY_NONE; heldItem = choiceItems[j]; }
PARAMETRIZE{ ability = ABILITY_KLUTZ; heldItem = choiceItems[j]; }
PARAMETRIZE { ability = ABILITY_NONE; heldItem = choiceItems[j]; }
PARAMETRIZE { ability = ABILITY_KLUTZ; heldItem = choiceItems[j]; }
}
GIVEN {
@@ -87,10 +87,10 @@ AI_SINGLE_BATTLE_TEST("Choiced Pokémon won't use status move if they are the on
for (j = 0; j < ARRAY_COUNT(choiceItems); j++)
{
PARAMETRIZE{ ability = ABILITY_NONE; heldItem = choiceItems[j]; isAlive = 0; }
PARAMETRIZE{ ability = ABILITY_KLUTZ; heldItem = choiceItems[j]; isAlive = 0; }
PARAMETRIZE{ ability = ABILITY_NONE; heldItem = choiceItems[j]; isAlive = 1; }
PARAMETRIZE{ ability = ABILITY_KLUTZ; heldItem = choiceItems[j]; isAlive = 1; }
PARAMETRIZE { ability = ABILITY_NONE; heldItem = choiceItems[j]; isAlive = 0; }
PARAMETRIZE { ability = ABILITY_KLUTZ; heldItem = choiceItems[j]; isAlive = 0; }
PARAMETRIZE { ability = ABILITY_NONE; heldItem = choiceItems[j]; isAlive = 1; }
PARAMETRIZE { ability = ABILITY_KLUTZ; heldItem = choiceItems[j]; isAlive = 1; }
}
GIVEN {
@@ -122,10 +122,10 @@ AI_SINGLE_BATTLE_TEST("Choiced Pokémon won't use status move if they don't have
for (j = 0; j < ARRAY_COUNT(choiceItems); j++)
{
PARAMETRIZE{ ability = ABILITY_NONE; heldItem = choiceItems[j]; species = SPECIES_SWAMPERT; move = MOVE_WATERFALL; }
PARAMETRIZE{ ability = ABILITY_KLUTZ; heldItem = choiceItems[j]; species = SPECIES_SWAMPERT; move = MOVE_WATERFALL; }
PARAMETRIZE{ ability = ABILITY_NONE; heldItem = choiceItems[j]; species = SPECIES_ELEKID; move = MOVE_THUNDER_WAVE; }
PARAMETRIZE{ ability = ABILITY_KLUTZ; heldItem = choiceItems[j]; species = SPECIES_ELEKID; move = MOVE_THUNDER_WAVE; }
PARAMETRIZE { ability = ABILITY_NONE; heldItem = choiceItems[j]; species = SPECIES_SWAMPERT; move = MOVE_WATERFALL; }
PARAMETRIZE { ability = ABILITY_KLUTZ; heldItem = choiceItems[j]; species = SPECIES_SWAMPERT; move = MOVE_WATERFALL; }
PARAMETRIZE { ability = ABILITY_NONE; heldItem = choiceItems[j]; species = SPECIES_ELEKID; move = MOVE_THUNDER_WAVE; }
PARAMETRIZE { ability = ABILITY_KLUTZ; heldItem = choiceItems[j]; species = SPECIES_ELEKID; move = MOVE_THUNDER_WAVE; }
}
GIVEN {
@@ -157,10 +157,10 @@ AI_SINGLE_BATTLE_TEST("Choiced Pokémon won't use status move if they are trappe
for (j = 0; j < ARRAY_COUNT(choiceItems); j++)
{
PARAMETRIZE{ aiAbility = ABILITY_NONE; heldItem = choiceItems[j]; species = SPECIES_RHYDON; playerAbility = ABILITY_LIGHTNING_ROD; }
PARAMETRIZE{ aiAbility = ABILITY_KLUTZ; heldItem = choiceItems[j]; species = SPECIES_RHYDON; playerAbility = ABILITY_LIGHTNING_ROD; }
PARAMETRIZE{ aiAbility = ABILITY_NONE; heldItem = choiceItems[j]; species = SPECIES_DUGTRIO; playerAbility = ABILITY_ARENA_TRAP; }
PARAMETRIZE{ aiAbility = ABILITY_KLUTZ; heldItem = choiceItems[j]; species = SPECIES_DUGTRIO; playerAbility = ABILITY_ARENA_TRAP; }
PARAMETRIZE { aiAbility = ABILITY_NONE; heldItem = choiceItems[j]; species = SPECIES_RHYDON; playerAbility = ABILITY_LIGHTNING_ROD; }
PARAMETRIZE { aiAbility = ABILITY_KLUTZ; heldItem = choiceItems[j]; species = SPECIES_RHYDON; playerAbility = ABILITY_LIGHTNING_ROD; }
PARAMETRIZE { aiAbility = ABILITY_NONE; heldItem = choiceItems[j]; species = SPECIES_DUGTRIO; playerAbility = ABILITY_ARENA_TRAP; }
PARAMETRIZE { aiAbility = ABILITY_KLUTZ; heldItem = choiceItems[j]; species = SPECIES_DUGTRIO; playerAbility = ABILITY_ARENA_TRAP; }
}
GIVEN {

View File

@@ -5,8 +5,8 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_RISKY: AI will blindly Mirror Coat against specia
{
u32 aiRiskyFlag = 0;
PARAMETRIZE{ aiRiskyFlag = 0; }
PARAMETRIZE{ aiRiskyFlag = AI_FLAG_RISKY; }
PARAMETRIZE { aiRiskyFlag = 0; }
PARAMETRIZE { aiRiskyFlag = AI_FLAG_RISKY; }
GIVEN {
ASSUME(gMovesInfo[MOVE_MIRROR_COAT].effect == EFFECT_MIRROR_COAT);
@@ -24,8 +24,8 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_RISKY: AI will blindly Counter against physical a
{
u32 aiRiskyFlag = 0;
PARAMETRIZE{ aiRiskyFlag = 0; }
PARAMETRIZE{ aiRiskyFlag = AI_FLAG_RISKY; }
PARAMETRIZE { aiRiskyFlag = 0; }
PARAMETRIZE { aiRiskyFlag = AI_FLAG_RISKY; }
GIVEN {
ASSUME(gMovesInfo[MOVE_COUNTER].effect == EFFECT_COUNTER);
@@ -43,8 +43,8 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_RISKY: AI will prioritize Revenge if slower")
{
u32 aiRiskyFlag = 0;
PARAMETRIZE{ aiRiskyFlag = 0; }
PARAMETRIZE{ aiRiskyFlag = AI_FLAG_RISKY; }
PARAMETRIZE { aiRiskyFlag = 0; }
PARAMETRIZE { aiRiskyFlag = AI_FLAG_RISKY; }
GIVEN {
ASSUME(gMovesInfo[MOVE_REVENGE].effect == EFFECT_REVENGE);
@@ -60,8 +60,8 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_RISKY: Mid-battle switches prioritize offensive o
{
u32 aiRiskyFlag = 0;
PARAMETRIZE{ aiRiskyFlag = 0; }
PARAMETRIZE{ aiRiskyFlag = AI_FLAG_RISKY; }
PARAMETRIZE { aiRiskyFlag = 0; }
PARAMETRIZE { aiRiskyFlag = AI_FLAG_RISKY; }
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_MON_CHOICES | aiRiskyFlag);
@@ -78,8 +78,8 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_RISKY: AI prefers high damage moves at the expens
{
u32 aiRiskyFlag = 0;
PARAMETRIZE{ aiRiskyFlag = 0; }
PARAMETRIZE{ aiRiskyFlag = AI_FLAG_RISKY; }
PARAMETRIZE { aiRiskyFlag = 0; }
PARAMETRIZE { aiRiskyFlag = AI_FLAG_RISKY; }
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiRiskyFlag);

View File

@@ -0,0 +1,129 @@
#include "global.h"
#include "test/battle.h"
AI_SINGLE_BATTLE_TEST("AI_FLAG_SEQUENCE_SWITCHING: AI will always switch after a KO in exactly party order")
{
u32 aiSequenceSwitchingFlag = 0;
PARAMETRIZE { aiSequenceSwitchingFlag = 0; }
PARAMETRIZE { aiSequenceSwitchingFlag = AI_FLAG_SEQUENCE_SWITCHING; }
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiSequenceSwitchingFlag);
PLAYER(SPECIES_SWELLOW) { Level (50); }
OPPONENT(SPECIES_MACHOP) { Level(5); }
OPPONENT(SPECIES_MACHOKE) { Level(5); }
OPPONENT(SPECIES_MACHAMP) { Level(5); }
OPPONENT(SPECIES_MANKEY) { Level(5); }
OPPONENT(SPECIES_PRIMEAPE) { Level(5); }
OPPONENT(SPECIES_MAGNEZONE) { Level(100); }
} WHEN {
TURN { MOVE(player, MOVE_WING_ATTACK); }
if (aiSequenceSwitchingFlag) {
TURN { MOVE(player, MOVE_WING_ATTACK); }
TURN { MOVE(player, MOVE_WING_ATTACK); }
TURN { MOVE(player, MOVE_WING_ATTACK); }
TURN { MOVE(player, MOVE_WING_ATTACK); }
}
} SCENE {
if (aiSequenceSwitchingFlag) {
MESSAGE("{PKMN} TRAINER LEAF sent out Machoke!");
MESSAGE("{PKMN} TRAINER LEAF sent out Machamp!");
MESSAGE("{PKMN} TRAINER LEAF sent out Mankey!");
MESSAGE("{PKMN} TRAINER LEAF sent out Primeape!");
MESSAGE("{PKMN} TRAINER LEAF sent out Magnezone!");
}
else {
MESSAGE("{PKMN} TRAINER LEAF sent out Magnezone!");
}
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SEQUENCE_SWITCHING: Roar and Dragon Tail still force switch to random party member")
{
u32 move;
PARAMETRIZE { move = MOVE_ROAR; }
PARAMETRIZE {move = MOVE_DRAGON_TAIL; }
PASSES_RANDOMLY(1, 2, RNG_FORCE_RANDOM_SWITCH);
GIVEN {
ASSUME(gMovesInfo[MOVE_ROAR].effect == EFFECT_ROAR);
ASSUME(gMovesInfo[MOVE_DRAGON_TAIL].effect == EFFECT_HIT_SWITCH_TARGET);
AI_FLAGS(AI_FLAG_SEQUENCE_SWITCHING);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_BULBASAUR);
OPPONENT(SPECIES_CHARMANDER);
OPPONENT(SPECIES_SQUIRTLE) { HP(0); }
} WHEN {
TURN { MOVE(player, move); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, move, player);
MESSAGE("Foe Bulbasaur was dragged out!");
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SEQUENCE_SWITCHING: AI will always switch into lowest party index after U-Turn, Parting Shot, and Baton Pass")
{
u32 j, aiSequenceSwitchingFlag = 0, move = MOVE_NONE;
static const u32 switchMoves[] = {
MOVE_U_TURN,
MOVE_PARTING_SHOT,
MOVE_BATON_PASS,
};
for (j = 0; j < ARRAY_COUNT(switchMoves); j++)
{
PARAMETRIZE { aiSequenceSwitchingFlag = 0; move = switchMoves[j]; }
PARAMETRIZE { aiSequenceSwitchingFlag = AI_FLAG_SEQUENCE_SWITCHING; move = switchMoves[j]; }
}
GIVEN {
ASSUME(gMovesInfo[MOVE_U_TURN].effect == EFFECT_HIT_ESCAPE);
ASSUME(gMovesInfo[MOVE_PARTING_SHOT].effect == EFFECT_PARTING_SHOT);
ASSUME(gMovesInfo[MOVE_BATON_PASS].effect == EFFECT_BATON_PASS);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiSequenceSwitchingFlag);
PLAYER(SPECIES_SWELLOW) { Level (50); }
OPPONENT(SPECIES_MACHOP) { Level(1); Moves(move); }
OPPONENT(SPECIES_MACHOP) { Level(2); Moves(move); }
OPPONENT(SPECIES_MACHOP) { Level(3); Moves(move); }
OPPONENT(SPECIES_MACHOP) { Level(4); Moves(move); }
OPPONENT(SPECIES_MACHOP) { Level(5); Moves(move); }
OPPONENT(SPECIES_MAGNEZONE) { Level(100); Moves(move); }
} WHEN {
if (aiSequenceSwitchingFlag) {
TURN { EXPECT_MOVE(opponent, move) ; EXPECT_SEND_OUT(opponent, 1); }
TURN { EXPECT_MOVE(opponent, move) ; EXPECT_SEND_OUT(opponent, 0); }
TURN { EXPECT_MOVE(opponent, move) ; EXPECT_SEND_OUT(opponent, 1); }
TURN { EXPECT_MOVE(opponent, move) ; EXPECT_SEND_OUT(opponent, 0); }
TURN { EXPECT_MOVE(opponent, move) ; EXPECT_SEND_OUT(opponent, 1); }
}
else {
TURN { EXPECT_MOVE(opponent, move) ; EXPECT_SEND_OUT(opponent, 5); }
}
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SEQUENCE_SWITCHING: AI will not switch mid-battle")
{
u32 aiSequenceSwitchingFlag = 0;
PARAMETRIZE { aiSequenceSwitchingFlag = 0; }
PARAMETRIZE { aiSequenceSwitchingFlag = AI_FLAG_SEQUENCE_SWITCHING; }
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiSequenceSwitchingFlag);
PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_LICK); }
OPPONENT(SPECIES_GASTLY) { Moves(MOVE_SHADOW_BALL); }
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_KARATE_CHOP); }
} WHEN {
if (aiSequenceSwitchingFlag == AI_FLAG_SEQUENCE_SWITCHING) {
TURN { MOVE(player, MOVE_LICK) ; EXPECT_MOVE(opponent, MOVE_SHADOW_BALL); }
}
else {
TURN { MOVE(player, MOVE_LICK) ; EXPECT_SWITCH(opponent, 1); }
}
}
}

View File

@@ -67,6 +67,19 @@ AI_DOUBLE_BATTLE_TEST("AI will not try to switch for the same pokemon for 2 spot
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: U-Turn will send out Ace Mon if it's the only one remaining")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_U_TURN].effect == EFFECT_HIT_ESCAPE);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_ACE_POKEMON);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_U_TURN); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { EXPECT_MOVE(opponent, MOVE_U_TURN); EXPECT_SEND_OUT(opponent, 1); }
}
}
// General AI_FLAG_SMART_MON_CHOICES behaviour
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")
{
@@ -127,9 +140,9 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI will not switch in a Pokemo
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
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 {
ASSUME(gMovesInfo[MOVE_PSYCHIC].category == DAMAGE_CATEGORY_SPECIAL);
@@ -159,8 +172,8 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI considers hazard damage whe
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
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);
@@ -181,10 +194,10 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Mid-battle switches prioritize
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; }
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);
@@ -257,8 +270,8 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will not switch out if Pokemo
{
u32 move1;
PARAMETRIZE{ move1 = MOVE_TACKLE; }
PARAMETRIZE{ move1 = MOVE_RAPID_SPIN; }
PARAMETRIZE { move1 = MOVE_TACKLE; }
PARAMETRIZE { move1 = MOVE_RAPID_SPIN; }
GIVEN {
ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL);

View File

@@ -153,3 +153,26 @@ SINGLE_BATTLE_TEST("Regular Mega Evolution and Fervent Wish Mega Evolution can h
EXPECT_EQ(opponent->species, SPECIES_GARDEVOIR_MEGA);
}
}
SINGLE_BATTLE_TEST("Mega Evolved Pokemon do not change abilities after fainting")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_CRUNCH].makesContact == TRUE);
ASSUME(gSpeciesInfo[SPECIES_GARCHOMP_MEGA].abilities[0] != ABILITY_ROUGH_SKIN);
ASSUME(gSpeciesInfo[SPECIES_GARCHOMP_MEGA].abilities[1] != ABILITY_ROUGH_SKIN);
ASSUME(gSpeciesInfo[SPECIES_GARCHOMP_MEGA].abilities[2] != ABILITY_ROUGH_SKIN);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_GARCHOMP) { Ability(ABILITY_ROUGH_SKIN); Item(ITEM_GARCHOMPITE); HP(1); }
} WHEN {
TURN { MOVE(player, MOVE_CRUNCH); MOVE(opponent, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CRUNCH, player);
MESSAGE("Foe Garchomp fainted!");
NONE_OF {
ABILITY_POPUP(opponent, ABILITY_ROUGH_SKIN);
MESSAGE("Wobbuffet was hurt by Foe Garchomp's Rough Skin!");
HP_BAR(player);
}
}
}

View File

@@ -24,7 +24,7 @@ SINGLE_BATTLE_TEST("Shaymin-Sky reverts to Shaymin-Land when frozen or frostbitt
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
if (move == MOVE_POWDER_SNOW) {
STATUS_ICON(player, freeze: TRUE);
FREEZE_OR_FROSTBURN_STATUS(player, TRUE);
NOT HP_BAR(player); // Regression caused by Mimikyu form change
MESSAGE("Shaymin transformed!");
} else {

View File

@@ -762,6 +762,89 @@ SINGLE_BATTLE_TEST("(TERA) Stellar type's one-time boost factors in dynamically-
}
}
SINGLE_BATTLE_TEST("(TERA) Terapagos retains the Stellar type boost at all times")
{
s16 damage[2];
u32 move;
PARAMETRIZE { move = MOVE_TACKLE; }
PARAMETRIZE { move = MOVE_MACH_PUNCH; }
GIVEN {
ASSUME(gMovesInfo[MOVE_TACKLE].type == TYPE_NORMAL);
ASSUME(gMovesInfo[MOVE_MACH_PUNCH].type != TYPE_NORMAL);
PLAYER(SPECIES_TERAPAGOS);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, move, gimmick: GIMMICK_TERA); }
TURN { MOVE(player, move); }
} SCENE {
HP_BAR(opponent, captureDamage: &damage[0]);
HP_BAR(opponent, captureDamage: &damage[1]);
} THEN {
EXPECT_EQ(damage[0], damage[1]);
}
}
SINGLE_BATTLE_TEST("(TERA) Terapagos retains its base defensive profile when Terastalizing")
{
GIVEN {
PLAYER(SPECIES_TERAPAGOS);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); MOVE(opponent, MOVE_BRICK_BREAK); }
} SCENE {
MESSAGE("It's super effective!");
}
}
SINGLE_BATTLE_TEST("(TERA) Illusion breaks if the pokemon Terastalizes")
{
KNOWN_FAILING; // #5015
u32 species;
PARAMETRIZE { species = SPECIES_TERAPAGOS; }
PARAMETRIZE { species = SPECIES_WOBBUFFET; }
GIVEN {
PLAYER(SPECIES_ZOROARK) { TeraType(TYPE_DARK); }
PLAYER(species);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); }
} SCENE {
MESSAGE("Zoroark's Illusion wore off!");
}
}
/*
// This test freezes the emulator
SINGLE_BATTLE_TEST("(TERA) Transformed pokemon can't Terastalize")
{
GIVEN {
PLAYER(SPECIES_DITTO);
OPPONENT(SPECIES_TERAPAGOS) { Moves(MOVE_CELEBRATE); }
} WHEN {
TURN { MOVE(player, MOVE_TRANSFORM); }
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); }
}
}
*/
SINGLE_BATTLE_TEST("(TERA) Pokemon with Tera forms change upon Terastallizing")
{
u32 species, targetSpecies;
PARAMETRIZE { species = SPECIES_OGERPON_TEAL_MASK; targetSpecies = SPECIES_OGERPON_TEAL_MASK_TERA; }
PARAMETRIZE { species = SPECIES_OGERPON_WELLSPRING_MASK; targetSpecies = SPECIES_OGERPON_WELLSPRING_MASK_TERA; }
PARAMETRIZE { species = SPECIES_OGERPON_HEARTHFLAME_MASK; targetSpecies = SPECIES_OGERPON_HEARTHFLAME_MASK_TERA; }
PARAMETRIZE { species = SPECIES_OGERPON_CORNERSTONE_MASK; targetSpecies = SPECIES_OGERPON_CORNERSTONE_MASK_TERA; }
PARAMETRIZE { species = SPECIES_TERAPAGOS_TERASTAL; targetSpecies = SPECIES_TERAPAGOS_STELLAR; }
GIVEN {
PLAYER(species);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); }
} THEN {
EXPECT_EQ(player->species, targetSpecies);
}
}
SINGLE_BATTLE_TEST("(TERA) All type indicators function correctly")
{
u32 type;
@@ -794,20 +877,34 @@ SINGLE_BATTLE_TEST("(TERA) All type indicators function correctly")
}
}
SINGLE_BATTLE_TEST("(TERA) Pokemon with Tera forms change upon Terastallizing")
SINGLE_BATTLE_TEST("(TERA) All type indicators function correctly - Opponent")
{
u32 species, targetSpecies;
PARAMETRIZE { species = SPECIES_OGERPON_TEAL_MASK; targetSpecies = SPECIES_OGERPON_TEAL_MASK_TERA; }
PARAMETRIZE { species = SPECIES_OGERPON_WELLSPRING_MASK; targetSpecies = SPECIES_OGERPON_WELLSPRING_MASK_TERA; }
PARAMETRIZE { species = SPECIES_OGERPON_HEARTHFLAME_MASK; targetSpecies = SPECIES_OGERPON_HEARTHFLAME_MASK_TERA; }
PARAMETRIZE { species = SPECIES_OGERPON_CORNERSTONE_MASK; targetSpecies = SPECIES_OGERPON_CORNERSTONE_MASK_TERA; }
PARAMETRIZE { species = SPECIES_TERAPAGOS_TERASTAL; targetSpecies = SPECIES_TERAPAGOS_STELLAR; }
u32 type;
PARAMETRIZE { type = TYPE_NONE; }
PARAMETRIZE { type = TYPE_NORMAL; }
PARAMETRIZE { type = TYPE_FIGHTING; }
PARAMETRIZE { type = TYPE_FLYING; }
PARAMETRIZE { type = TYPE_POISON; }
PARAMETRIZE { type = TYPE_GROUND; }
PARAMETRIZE { type = TYPE_ROCK; }
PARAMETRIZE { type = TYPE_BUG; }
PARAMETRIZE { type = TYPE_GHOST; }
PARAMETRIZE { type = TYPE_STEEL; }
PARAMETRIZE { type = TYPE_MYSTERY; }
PARAMETRIZE { type = TYPE_FIRE; }
PARAMETRIZE { type = TYPE_WATER; }
PARAMETRIZE { type = TYPE_GRASS; }
PARAMETRIZE { type = TYPE_ELECTRIC; }
PARAMETRIZE { type = TYPE_PSYCHIC; }
PARAMETRIZE { type = TYPE_ICE; }
PARAMETRIZE { type = TYPE_DRAGON; }
PARAMETRIZE { type = TYPE_DARK; }
PARAMETRIZE { type = TYPE_FAIRY; }
PARAMETRIZE { type = TYPE_STELLAR; }
GIVEN {
PLAYER(species);
OPPONENT(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { TeraType(type); }
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); }
} THEN {
EXPECT_EQ(player->species, targetSpecies);
TURN { MOVE(opponent, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); }
}
}

View File

@@ -72,7 +72,7 @@ SINGLE_BATTLE_TEST("Rawst and Lum Berries cure burn")
}
}
SINGLE_BATTLE_TEST("Aspear and Lum Berries cure freeze")
SINGLE_BATTLE_TEST("Aspear and Lum Berries cure freeze or frostbite")
{
u16 item;
@@ -88,9 +88,9 @@ SINGLE_BATTLE_TEST("Aspear and Lum Berries cure freeze")
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ICE_PUNCH, player);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, opponent);
STATUS_ICON(opponent, freeze: TRUE);
FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
STATUS_ICON(opponent, freeze: FALSE);
FREEZE_OR_FROSTBURN_STATUS(opponent, FALSE);
}
}
@@ -163,12 +163,12 @@ SINGLE_BATTLE_TEST("Berry hold effect cures status if a pokemon enters a battle"
u16 status;
u16 item;
PARAMETRIZE{ status = STATUS1_BURN; item = ITEM_RAWST_BERRY; }
PARAMETRIZE{ status = STATUS1_FREEZE; item = ITEM_ASPEAR_BERRY; }
PARAMETRIZE{ status = STATUS1_PARALYSIS; item = ITEM_CHERI_BERRY; }
PARAMETRIZE{ status = STATUS1_POISON; item = ITEM_PECHA_BERRY; }
PARAMETRIZE{ status = STATUS1_TOXIC_POISON; item = ITEM_PECHA_BERRY; }
PARAMETRIZE{ status = STATUS1_SLEEP; item = ITEM_CHESTO_BERRY; }
PARAMETRIZE { status = STATUS1_BURN; item = ITEM_RAWST_BERRY; }
PARAMETRIZE { status = STATUS1_FREEZE; item = ITEM_ASPEAR_BERRY; }
PARAMETRIZE { status = STATUS1_PARALYSIS; item = ITEM_CHERI_BERRY; }
PARAMETRIZE { status = STATUS1_POISON; item = ITEM_PECHA_BERRY; }
PARAMETRIZE { status = STATUS1_TOXIC_POISON; item = ITEM_PECHA_BERRY; }
PARAMETRIZE { status = STATUS1_SLEEP; item = ITEM_CHESTO_BERRY; }
GIVEN {
ASSUME(gItemsInfo[ITEM_RAWST_BERRY].holdEffect == HOLD_EFFECT_CURE_BRN);

View File

@@ -73,3 +73,18 @@ DOUBLE_BATTLE_TEST("Mirror Herb does not trigger for Ally's Soul Heart's stat ra
EXPECT_EQ(playerLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
}
}
SINGLE_BATTLE_TEST("Mirror Herb copies the boost gained by an ability")
{
GIVEN {
PLAYER(SPECIES_ZACIAN) { Ability(ABILITY_INTREPID_SWORD); }
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_MIRROR_HERB); }
} WHEN {
TURN { }
} SCENE {
ABILITY_POPUP(player, ABILITY_INTREPID_SWORD);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
}
}

View File

@@ -112,12 +112,12 @@ SINGLE_BATTLE_TEST("Ice Heal heals a battler from being frozen")
SINGLE_BATTLE_TEST("Full Heal heals a battler from any primary status")
{
u16 status;
PARAMETRIZE{ status = STATUS1_BURN; }
PARAMETRIZE{ status = STATUS1_FREEZE; }
PARAMETRIZE{ status = STATUS1_PARALYSIS; }
PARAMETRIZE{ status = STATUS1_POISON; }
PARAMETRIZE{ status = STATUS1_TOXIC_POISON; }
PARAMETRIZE{ status = STATUS1_SLEEP; }
PARAMETRIZE { status = STATUS1_BURN; }
PARAMETRIZE { status = STATUS1_FREEZE; }
PARAMETRIZE { status = STATUS1_PARALYSIS; }
PARAMETRIZE { status = STATUS1_POISON; }
PARAMETRIZE { status = STATUS1_TOXIC_POISON; }
PARAMETRIZE { status = STATUS1_SLEEP; }
GIVEN {
ASSUME(gItemsInfo[ITEM_FULL_HEAL].battleUsage == EFFECT_ITEM_CURE_STATUS);
PLAYER(SPECIES_WOBBUFFET) { Status1(status); }
@@ -134,12 +134,12 @@ SINGLE_BATTLE_TEST("Full Heal heals a battler from any primary status")
SINGLE_BATTLE_TEST("Heal Powder heals a battler from any primary status")
{
u16 status;
PARAMETRIZE{ status = STATUS1_BURN; }
PARAMETRIZE{ status = STATUS1_FREEZE; }
PARAMETRIZE{ status = STATUS1_PARALYSIS; }
PARAMETRIZE{ status = STATUS1_POISON; }
PARAMETRIZE{ status = STATUS1_TOXIC_POISON; }
PARAMETRIZE{ status = STATUS1_SLEEP; }
PARAMETRIZE { status = STATUS1_BURN; }
PARAMETRIZE { status = STATUS1_FREEZE; }
PARAMETRIZE { status = STATUS1_PARALYSIS; }
PARAMETRIZE { status = STATUS1_POISON; }
PARAMETRIZE { status = STATUS1_TOXIC_POISON; }
PARAMETRIZE { status = STATUS1_SLEEP; }
GIVEN {
ASSUME(gItemsInfo[ITEM_HEAL_POWDER].battleUsage == EFFECT_ITEM_CURE_STATUS);
PLAYER(SPECIES_WOBBUFFET) { Status1(status); }
@@ -156,12 +156,12 @@ SINGLE_BATTLE_TEST("Heal Powder heals a battler from any primary status")
SINGLE_BATTLE_TEST("Pewter Crunchies heals a battler from any primary status")
{
u16 status;
PARAMETRIZE{ status = STATUS1_BURN; }
PARAMETRIZE{ status = STATUS1_FREEZE; }
PARAMETRIZE{ status = STATUS1_PARALYSIS; }
PARAMETRIZE{ status = STATUS1_POISON; }
PARAMETRIZE{ status = STATUS1_TOXIC_POISON; }
PARAMETRIZE{ status = STATUS1_SLEEP; }
PARAMETRIZE { status = STATUS1_BURN; }
PARAMETRIZE { status = STATUS1_FREEZE; }
PARAMETRIZE { status = STATUS1_PARALYSIS; }
PARAMETRIZE { status = STATUS1_POISON; }
PARAMETRIZE { status = STATUS1_TOXIC_POISON; }
PARAMETRIZE { status = STATUS1_SLEEP; }
GIVEN {
ASSUME(gItemsInfo[ITEM_PEWTER_CRUNCHIES].battleUsage == EFFECT_ITEM_CURE_STATUS);
PLAYER(SPECIES_WOBBUFFET) { Status1(status); }
@@ -178,12 +178,12 @@ SINGLE_BATTLE_TEST("Pewter Crunchies heals a battler from any primary status")
SINGLE_BATTLE_TEST("Lava Cookies heals a battler from any primary status")
{
u16 status;
PARAMETRIZE{ status = STATUS1_BURN; }
PARAMETRIZE{ status = STATUS1_FREEZE; }
PARAMETRIZE{ status = STATUS1_PARALYSIS; }
PARAMETRIZE{ status = STATUS1_POISON; }
PARAMETRIZE{ status = STATUS1_TOXIC_POISON; }
PARAMETRIZE{ status = STATUS1_SLEEP; }
PARAMETRIZE { status = STATUS1_BURN; }
PARAMETRIZE { status = STATUS1_FREEZE; }
PARAMETRIZE { status = STATUS1_PARALYSIS; }
PARAMETRIZE { status = STATUS1_POISON; }
PARAMETRIZE { status = STATUS1_TOXIC_POISON; }
PARAMETRIZE { status = STATUS1_SLEEP; }
GIVEN {
ASSUME(gItemsInfo[ITEM_LAVA_COOKIE].battleUsage == EFFECT_ITEM_CURE_STATUS);
PLAYER(SPECIES_WOBBUFFET) { Status1(status); }
@@ -200,12 +200,12 @@ SINGLE_BATTLE_TEST("Lava Cookies heals a battler from any primary status")
SINGLE_BATTLE_TEST("Rage Candy Bar heals a battler from any primary status")
{
u16 status;
PARAMETRIZE{ status = STATUS1_BURN; }
PARAMETRIZE{ status = STATUS1_FREEZE; }
PARAMETRIZE{ status = STATUS1_PARALYSIS; }
PARAMETRIZE{ status = STATUS1_POISON; }
PARAMETRIZE{ status = STATUS1_TOXIC_POISON; }
PARAMETRIZE{ status = STATUS1_SLEEP; }
PARAMETRIZE { status = STATUS1_BURN; }
PARAMETRIZE { status = STATUS1_FREEZE; }
PARAMETRIZE { status = STATUS1_PARALYSIS; }
PARAMETRIZE { status = STATUS1_POISON; }
PARAMETRIZE { status = STATUS1_TOXIC_POISON; }
PARAMETRIZE { status = STATUS1_SLEEP; }
GIVEN {
ASSUME(gItemsInfo[ITEM_RAGE_CANDY_BAR].battleUsage == EFFECT_ITEM_CURE_STATUS);
PLAYER(SPECIES_WOBBUFFET) { Status1(status); }
@@ -222,12 +222,12 @@ SINGLE_BATTLE_TEST("Rage Candy Bar heals a battler from any primary status")
SINGLE_BATTLE_TEST("Old Gateu heals a battler from any primary status")
{
u16 status;
PARAMETRIZE{ status = STATUS1_BURN; }
PARAMETRIZE{ status = STATUS1_FREEZE; }
PARAMETRIZE{ status = STATUS1_PARALYSIS; }
PARAMETRIZE{ status = STATUS1_POISON; }
PARAMETRIZE{ status = STATUS1_TOXIC_POISON; }
PARAMETRIZE{ status = STATUS1_SLEEP; }
PARAMETRIZE { status = STATUS1_BURN; }
PARAMETRIZE { status = STATUS1_FREEZE; }
PARAMETRIZE { status = STATUS1_PARALYSIS; }
PARAMETRIZE { status = STATUS1_POISON; }
PARAMETRIZE { status = STATUS1_TOXIC_POISON; }
PARAMETRIZE { status = STATUS1_SLEEP; }
GIVEN {
ASSUME(gItemsInfo[ITEM_OLD_GATEAU].battleUsage == EFFECT_ITEM_CURE_STATUS);
PLAYER(SPECIES_WOBBUFFET) { Status1(status); }
@@ -244,12 +244,12 @@ SINGLE_BATTLE_TEST("Old Gateu heals a battler from any primary status")
SINGLE_BATTLE_TEST("Casteliacone heals a battler from any primary status")
{
u16 status;
PARAMETRIZE{ status = STATUS1_BURN; }
PARAMETRIZE{ status = STATUS1_FREEZE; }
PARAMETRIZE{ status = STATUS1_PARALYSIS; }
PARAMETRIZE{ status = STATUS1_POISON; }
PARAMETRIZE{ status = STATUS1_TOXIC_POISON; }
PARAMETRIZE{ status = STATUS1_SLEEP; }
PARAMETRIZE { status = STATUS1_BURN; }
PARAMETRIZE { status = STATUS1_FREEZE; }
PARAMETRIZE { status = STATUS1_PARALYSIS; }
PARAMETRIZE { status = STATUS1_POISON; }
PARAMETRIZE { status = STATUS1_TOXIC_POISON; }
PARAMETRIZE { status = STATUS1_SLEEP; }
GIVEN {
ASSUME(gItemsInfo[ITEM_CASTELIACONE].battleUsage == EFFECT_ITEM_CURE_STATUS);
PLAYER(SPECIES_WOBBUFFET) { Status1(status); }
@@ -266,12 +266,12 @@ SINGLE_BATTLE_TEST("Casteliacone heals a battler from any primary status")
SINGLE_BATTLE_TEST("Lumiose Galette heals a battler from any primary status")
{
u16 status;
PARAMETRIZE{ status = STATUS1_BURN; }
PARAMETRIZE{ status = STATUS1_FREEZE; }
PARAMETRIZE{ status = STATUS1_PARALYSIS; }
PARAMETRIZE{ status = STATUS1_POISON; }
PARAMETRIZE{ status = STATUS1_TOXIC_POISON; }
PARAMETRIZE{ status = STATUS1_SLEEP; }
PARAMETRIZE { status = STATUS1_BURN; }
PARAMETRIZE { status = STATUS1_FREEZE; }
PARAMETRIZE { status = STATUS1_PARALYSIS; }
PARAMETRIZE { status = STATUS1_POISON; }
PARAMETRIZE { status = STATUS1_TOXIC_POISON; }
PARAMETRIZE { status = STATUS1_SLEEP; }
GIVEN {
ASSUME(gItemsInfo[ITEM_LUMIOSE_GALETTE].battleUsage == EFFECT_ITEM_CURE_STATUS);
PLAYER(SPECIES_WOBBUFFET) { Status1(status); }
@@ -288,12 +288,12 @@ SINGLE_BATTLE_TEST("Lumiose Galette heals a battler from any primary status")
SINGLE_BATTLE_TEST("Shalour Sable heals a battler from any primary status")
{
u16 status;
PARAMETRIZE{ status = STATUS1_BURN; }
PARAMETRIZE{ status = STATUS1_FREEZE; }
PARAMETRIZE{ status = STATUS1_PARALYSIS; }
PARAMETRIZE{ status = STATUS1_POISON; }
PARAMETRIZE{ status = STATUS1_TOXIC_POISON; }
PARAMETRIZE{ status = STATUS1_SLEEP; }
PARAMETRIZE { status = STATUS1_BURN; }
PARAMETRIZE { status = STATUS1_FREEZE; }
PARAMETRIZE { status = STATUS1_PARALYSIS; }
PARAMETRIZE { status = STATUS1_POISON; }
PARAMETRIZE { status = STATUS1_TOXIC_POISON; }
PARAMETRIZE { status = STATUS1_SLEEP; }
GIVEN {
ASSUME(gItemsInfo[ITEM_SHALOUR_SABLE].battleUsage == EFFECT_ITEM_CURE_STATUS);
PLAYER(SPECIES_WOBBUFFET) { Status1(status); }
@@ -310,12 +310,12 @@ SINGLE_BATTLE_TEST("Shalour Sable heals a battler from any primary status")
SINGLE_BATTLE_TEST("Big Malasada heals a battler from any primary status")
{
u16 status;
PARAMETRIZE{ status = STATUS1_BURN; }
PARAMETRIZE{ status = STATUS1_FREEZE; }
PARAMETRIZE{ status = STATUS1_PARALYSIS; }
PARAMETRIZE{ status = STATUS1_POISON; }
PARAMETRIZE{ status = STATUS1_TOXIC_POISON; }
PARAMETRIZE{ status = STATUS1_SLEEP; }
PARAMETRIZE { status = STATUS1_BURN; }
PARAMETRIZE { status = STATUS1_FREEZE; }
PARAMETRIZE { status = STATUS1_PARALYSIS; }
PARAMETRIZE { status = STATUS1_POISON; }
PARAMETRIZE { status = STATUS1_TOXIC_POISON; }
PARAMETRIZE { status = STATUS1_SLEEP; }
GIVEN {
ASSUME(gItemsInfo[ITEM_BIG_MALASADA].battleUsage == EFFECT_ITEM_CURE_STATUS);
PLAYER(SPECIES_WOBBUFFET) { Status1(status); }

View File

@@ -9,13 +9,13 @@ ASSUMPTIONS
SINGLE_BATTLE_TEST("Full Restore restores a battler's HP and cures any primary status")
{
u16 status;
PARAMETRIZE{ status = STATUS1_BURN; }
PARAMETRIZE{ status = STATUS1_FREEZE; }
PARAMETRIZE{ status = STATUS1_PARALYSIS; }
PARAMETRIZE{ status = STATUS1_POISON; }
PARAMETRIZE{ status = STATUS1_TOXIC_POISON; }
PARAMETRIZE{ status = STATUS1_SLEEP; }
PARAMETRIZE{ status = STATUS1_NONE; }
PARAMETRIZE { status = STATUS1_BURN; }
PARAMETRIZE { status = STATUS1_FREEZE; }
PARAMETRIZE { status = STATUS1_PARALYSIS; }
PARAMETRIZE { status = STATUS1_POISON; }
PARAMETRIZE { status = STATUS1_TOXIC_POISON; }
PARAMETRIZE { status = STATUS1_SLEEP; }
PARAMETRIZE { status = STATUS1_NONE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(1); MaxHP(300); Status1(status); }
OPPONENT(SPECIES_WOBBUFFET);
@@ -35,13 +35,13 @@ SINGLE_BATTLE_TEST("Full Restore restores a battler's HP and cures any primary s
SINGLE_BATTLE_TEST("Full Restore restores a party members HP and cures any primary status")
{
u16 status;
PARAMETRIZE{ status = STATUS1_BURN; }
PARAMETRIZE{ status = STATUS1_FREEZE; }
PARAMETRIZE{ status = STATUS1_PARALYSIS; }
PARAMETRIZE{ status = STATUS1_POISON; }
PARAMETRIZE{ status = STATUS1_TOXIC_POISON; }
PARAMETRIZE{ status = STATUS1_SLEEP; }
PARAMETRIZE{ status = STATUS1_NONE; }
PARAMETRIZE { status = STATUS1_BURN; }
PARAMETRIZE { status = STATUS1_FREEZE; }
PARAMETRIZE { status = STATUS1_PARALYSIS; }
PARAMETRIZE { status = STATUS1_POISON; }
PARAMETRIZE { status = STATUS1_TOXIC_POISON; }
PARAMETRIZE { status = STATUS1_SLEEP; }
PARAMETRIZE { status = STATUS1_NONE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(1); MaxHP(300); Status1(status); }
PLAYER(SPECIES_WYNAUT) { HP(1); MaxHP(300); Status1(status); }
@@ -64,12 +64,12 @@ SINGLE_BATTLE_TEST("Full Restore restores a party members HP and cures any prima
SINGLE_BATTLE_TEST("Full Restore heals a battler from any primary status")
{
u16 status;
PARAMETRIZE{ status = STATUS1_BURN; }
PARAMETRIZE{ status = STATUS1_FREEZE; }
PARAMETRIZE{ status = STATUS1_PARALYSIS; }
PARAMETRIZE{ status = STATUS1_POISON; }
PARAMETRIZE{ status = STATUS1_TOXIC_POISON; }
PARAMETRIZE{ status = STATUS1_SLEEP; }
PARAMETRIZE { status = STATUS1_BURN; }
PARAMETRIZE { status = STATUS1_FREEZE; }
PARAMETRIZE { status = STATUS1_PARALYSIS; }
PARAMETRIZE { status = STATUS1_POISON; }
PARAMETRIZE { status = STATUS1_TOXIC_POISON; }
PARAMETRIZE { status = STATUS1_SLEEP; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Status1(status); }
OPPONENT(SPECIES_WYNAUT);
@@ -86,12 +86,12 @@ SINGLE_BATTLE_TEST("Full Restore heals a battler from any primary status")
SINGLE_BATTLE_TEST("Full Restore heals a party member from any primary status")
{
u16 status;
PARAMETRIZE{ status = STATUS1_BURN; }
PARAMETRIZE{ status = STATUS1_FREEZE; }
PARAMETRIZE{ status = STATUS1_PARALYSIS; }
PARAMETRIZE{ status = STATUS1_POISON; }
PARAMETRIZE{ status = STATUS1_TOXIC_POISON; }
PARAMETRIZE{ status = STATUS1_SLEEP; }
PARAMETRIZE { status = STATUS1_BURN; }
PARAMETRIZE { status = STATUS1_FREEZE; }
PARAMETRIZE { status = STATUS1_PARALYSIS; }
PARAMETRIZE { status = STATUS1_POISON; }
PARAMETRIZE { status = STATUS1_TOXIC_POISON; }
PARAMETRIZE { status = STATUS1_SLEEP; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT) { Status1(status); }

View File

@@ -1,5 +1,48 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Acrobatics doubles in power if the user has no held item");
TO_DO_BATTLE_TEST("Acrobatics still doubles in power when Flying Gem is consumed");
ASSUMPTIONS
{
ASSUME(gMovesInfo[MOVE_ACROBATICS].effect == EFFECT_ACROBATICS);
ASSUME(gMovesInfo[MOVE_ACROBATICS].type == TYPE_FLYING);
}
SINGLE_BATTLE_TEST("Acrobatics doubles in power if the user has no held item", s16 damage)
{
u16 heldItem;
PARAMETRIZE { heldItem = ITEM_POTION; }
PARAMETRIZE { heldItem = ITEM_NONE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Item(heldItem); }
} WHEN {
TURN { MOVE(opponent, MOVE_ACROBATICS); }
} SCENE {
HP_BAR(player, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(2), results[1].damage);
}
}
SINGLE_BATTLE_TEST("Acrobatics still doubles in power when Flying Gem is consumed", s16 damage)
{
u16 heldItem;
PARAMETRIZE { heldItem = ITEM_NONE; }
PARAMETRIZE { heldItem = ITEM_FLYING_GEM; }
GIVEN {
ASSUME(I_GEM_BOOST_POWER >= GEN_6);
ASSUME(gItemsInfo[ITEM_FLYING_GEM].holdEffect == HOLD_EFFECT_GEMS);
ASSUME(gItemsInfo[ITEM_FLYING_GEM].secondaryId == TYPE_FLYING);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Item(heldItem); }
} WHEN {
TURN { MOVE(opponent, MOVE_ACROBATICS); }
} SCENE {
HP_BAR(player, captureDamage: &results[i].damage);
} FINALLY {
if (I_GEM_BOOST_POWER >= GEN_6)
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.3), (results[1].damage));
else
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), (results[1].damage));
}
}

View File

@@ -1,4 +1,18 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Hone Claws increases Attack and Accuracy by one stage each");
SINGLE_BATTLE_TEST("Hone Claws increases Attack and Accuracy by one stage each")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_HONE_CLAWS].effect == EFFECT_ATTACK_ACCURACY_UP);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_HONE_CLAWS); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_HONE_CLAWS, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Wobbuffet's Attack rose!");
MESSAGE("Wobbuffet's accuracy rose!");
}
}

View File

@@ -10,8 +10,8 @@ ASSUMPTIONS
SINGLE_BATTLE_TEST("Aura Wheel raises Speed; fails if the user is not Morpeko")
{
u16 species;
PARAMETRIZE{ species = SPECIES_WOBBUFFET; }
PARAMETRIZE{ species = SPECIES_MORPEKO; }
PARAMETRIZE { species = SPECIES_WOBBUFFET; }
PARAMETRIZE { species = SPECIES_MORPEKO; }
GIVEN {
PLAYER(species);
OPPONENT(SPECIES_WOBBUFFET);

View File

@@ -253,8 +253,8 @@ SINGLE_BATTLE_TEST("Embargo disables the effect of the Plate items on the move J
{
u32 heldItem;
PARAMETRIZE{ heldItem = ITEM_NONE; }
PARAMETRIZE{ heldItem = ITEM_PIXIE_PLATE; }
PARAMETRIZE { heldItem = ITEM_NONE; }
PARAMETRIZE { heldItem = ITEM_PIXIE_PLATE; }
GIVEN {
PLAYER(SPECIES_ARCEUS) { Item(heldItem); };
OPPONENT(SPECIES_DRAGONITE);
@@ -274,8 +274,8 @@ SINGLE_BATTLE_TEST("Embargo disables the effect of the Drive items on the move T
{
u32 heldItem;
PARAMETRIZE{ heldItem = ITEM_NONE; }
PARAMETRIZE{ heldItem = ITEM_SHOCK_DRIVE; }
PARAMETRIZE { heldItem = ITEM_NONE; }
PARAMETRIZE { heldItem = ITEM_SHOCK_DRIVE; }
GIVEN {
PLAYER(SPECIES_GENESECT) { Item(heldItem); };
OPPONENT(SPECIES_GYARADOS);
@@ -295,8 +295,8 @@ SINGLE_BATTLE_TEST("Embargo disables the effect of the Memory items on the move
{
u32 heldItem;
PARAMETRIZE{ heldItem = ITEM_NONE; }
PARAMETRIZE{ heldItem = ITEM_FIRE_MEMORY; }
PARAMETRIZE { heldItem = ITEM_NONE; }
PARAMETRIZE { heldItem = ITEM_FIRE_MEMORY; }
GIVEN {
PLAYER(SPECIES_SILVALLY) { Item(heldItem); };
OPPONENT(SPECIES_VENUSAUR);

View File

@@ -0,0 +1,24 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(gMovesInfo[MOVE_FLAME_BURST].additionalEffects->moveEffect == MOVE_EFFECT_FLAME_BURST);
}
// Flame Burst AoE is supposed to hit through Substitute
DOUBLE_BATTLE_TEST("Flame Burst Substitute")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_SUBSTITUTE].effect == EFFECT_SUBSTITUTE);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponentLeft, MOVE_SUBSTITUTE); MOVE(playerRight, MOVE_FLAME_BURST, target: opponentRight); }
} SCENE {
MESSAGE("The bursting flames hit Foe Wynaut!");
NOT MESSAGE("The SUBSTITUTE took damage for Foe Wynaut!");
}
}

View File

@@ -74,3 +74,28 @@ DOUBLE_BATTLE_TEST("Focus Punch activation is based on Speed")
MESSAGE("Foe Wobbuffet lost its focus and couldn't move!");
}
}
AI_SINGLE_BATTLE_TEST("AI won't use Focus Punch if it predicts a damaging move")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_MAGNEZONE) { Moves(MOVE_THUNDER_WAVE, MOVE_FLASH_CANNON, MOVE_DISCHARGE, MOVE_TRI_ATTACK); }
OPPONENT(SPECIES_BRELOOM) { Moves(MOVE_FOCUS_PUNCH, MOVE_SEED_BOMB); }
} WHEN {
TURN { MOVE(player, MOVE_DISCHARGE); EXPECT_MOVE(opponent, MOVE_FOCUS_PUNCH); }
TURN { MOVE(player, MOVE_DISCHARGE); EXPECT_MOVE(opponent, MOVE_SEED_BOMB); }
}
}
AI_SINGLE_BATTLE_TEST("AI will Incapacitate -> Substitute -> Focus Punch if able")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_MAGNEZONE) { Moves(MOVE_THUNDER_WAVE, MOVE_FLASH_CANNON, MOVE_DISCHARGE, MOVE_TRI_ATTACK); }
OPPONENT(SPECIES_BRELOOM) { Moves(MOVE_SPORE, MOVE_FOCUS_PUNCH, MOVE_SUBSTITUTE, MOVE_SEED_BOMB); }
} WHEN {
TURN { MOVE(player, MOVE_DISCHARGE); EXPECT_MOVE(opponent, MOVE_SPORE); }
TURN { MOVE(player, MOVE_DISCHARGE); EXPECT_MOVE(opponent, MOVE_SUBSTITUTE); }
TURN { MOVE(player, MOVE_DISCHARGE); EXPECT_MOVE(opponent, MOVE_FOCUS_PUNCH); }
}
}

View File

@@ -0,0 +1,38 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(gMovesInfo[MOVE_FURY_CUTTER].effect == EFFECT_FURY_CUTTER);
}
SINGLE_BATTLE_TEST("Fury Cutter power doubles with each use, up to 160 power")
{
s16 damage[6];
int turn;
int maxTurns;
if (B_UPDATED_MOVE_DATA >= GEN_6)
maxTurns = 4;
else if (B_UPDATED_MOVE_DATA == GEN_5)
maxTurns = 5;
else
maxTurns = 6;
GIVEN {
PLAYER(SPECIES_CROBAT);
OPPONENT(SPECIES_LINOONE) { HP(900); }
} WHEN {
for (turn = 0; turn < maxTurns; turn++)
TURN { MOVE(player, MOVE_FURY_CUTTER); }
} SCENE {
for (turn = 0; turn < maxTurns; turn++) {
ANIMATION(ANIM_TYPE_MOVE, MOVE_FURY_CUTTER, player);
HP_BAR(opponent, captureDamage: &damage[turn]);
}
} THEN {
for (turn = 1; turn < maxTurns - 1; turn++)
EXPECT_MUL_EQ(damage[turn - 1], UQ_4_12(2.0), damage[turn]);
EXPECT_EQ(damage[maxTurns - 2], damage[maxTurns - 1]);
}
}

View File

@@ -126,6 +126,7 @@ SINGLE_BATTLE_TEST("Held items are consumed immediately after a mon switched in
ABILITY_POPUP(player, ABILITY_ELECTRIC_SURGE);
ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, player);
HP_BAR(opponent);
ABILITY_POPUP(player, ABILITY_INTIMIDATE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("2 sent out Wynaut!");
@@ -137,7 +138,6 @@ SINGLE_BATTLE_TEST("Held items are consumed immediately after a mon switched in
SINGLE_BATTLE_TEST("Held items are consumed immediately after a mon switched in by U-turn and Intimidate activates after it: opposing side")
{
KNOWN_FAILING;
GIVEN {
PLAYER(SPECIES_TAPU_KOKO) { Ability(ABILITY_ELECTRIC_SURGE); };
PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); }
@@ -149,7 +149,7 @@ SINGLE_BATTLE_TEST("Held items are consumed immediately after a mon switched in
ABILITY_POPUP(player, ABILITY_ELECTRIC_SURGE);
ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, player);
HP_BAR(opponent);
NOT ABILITY_POPUP(player, ABILITY_INTIMIDATE);
ABILITY_POPUP(player, ABILITY_INTIMIDATE);
MESSAGE("2 sent out Wynaut!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);

View File

@@ -41,7 +41,7 @@ WILD_BATTLE_TEST("Ion Deluge works the same way as always when used by a mon wit
NONE_OF {
ABILITY_POPUP(opponent, ability);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Wild Zebstrika's Sp.Atk rose!");
MESSAGE("Wild Zebstrika's Sp. Atk rose!");
MESSAGE("Wild Zebstrika's Speed rose!");
}
MESSAGE("A deluge of ions showers the battlefield!");

View File

@@ -23,7 +23,7 @@ SINGLE_BATTLE_TEST("Photon Geyser can be mirror coated if it is a special move")
}
}
SINGLE_BATTLE_TEST("Photon Geyser can be countered if it is a physcal move")
SINGLE_BATTLE_TEST("Photon Geyser can be countered if it is a physical move")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Attack(110); SpAttack(100); }

View File

@@ -47,20 +47,19 @@ SINGLE_BATTLE_TEST("Plasma Fists turns normal moves into electric for the remain
}
}
SINGLE_BATTLE_TEST("Plasma Fists type-changing effect is applied after Pixilate")
SINGLE_BATTLE_TEST("Plasma Fists type-changing effect does not override Pixilate")
{
GIVEN {
PLAYER(SPECIES_KRABBY) { Speed(300); };
OPPONENT(SPECIES_ALTARIA) { Speed(1); Item(ITEM_ALTARIANITE); }
OPPONENT(SPECIES_SYLVEON) { Speed(1); Ability(ABILITY_PIXILATE); }
} WHEN {
TURN { MOVE(player, MOVE_PLASMA_FISTS); MOVE(opponent, MOVE_EMBER, gimmick: GIMMICK_MEGA); }
TURN { MOVE(player, MOVE_PLASMA_FISTS); MOVE(opponent, MOVE_TACKLE); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponent);
MESSAGE("Krabby used Plasma Fists!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLASMA_FISTS, player);
MESSAGE("A deluge of ions showers the battlefield!");
MESSAGE("Foe Altaria used Ember!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, opponent);
MESSAGE("Foe Sylveon used Tackle!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
NOT MESSAGE("It's super effective!");
}
}

View File

@@ -111,24 +111,24 @@ SINGLE_BATTLE_TEST("Roost suppresses the user's Flying-typing this turn, then re
SINGLE_BATTLE_TEST("Roost, if used by a Flying/Flying type, treats the user as a Normal-type (or Typeless in Gen. 4) until the end of the turn")
{
u32 damagingMove;
PARAMETRIZE{ damagingMove = MOVE_POUND; }
PARAMETRIZE{ damagingMove = MOVE_KARATE_CHOP; }
PARAMETRIZE{ damagingMove = MOVE_GUST; }
PARAMETRIZE{ damagingMove = MOVE_POISON_STING; }
PARAMETRIZE{ damagingMove = MOVE_EARTHQUAKE; }
PARAMETRIZE{ damagingMove = MOVE_ROCK_THROW; }
PARAMETRIZE{ damagingMove = MOVE_LEECH_LIFE; }
PARAMETRIZE{ damagingMove = MOVE_LICK; }
PARAMETRIZE{ damagingMove = MOVE_STEEL_WING; }
PARAMETRIZE{ damagingMove = MOVE_EMBER; }
PARAMETRIZE{ damagingMove = MOVE_WATER_GUN; }
PARAMETRIZE{ damagingMove = MOVE_VINE_WHIP; }
PARAMETRIZE{ damagingMove = MOVE_THUNDER_SHOCK; }
PARAMETRIZE{ damagingMove = MOVE_CONFUSION; }
PARAMETRIZE{ damagingMove = MOVE_ICE_BEAM; }
PARAMETRIZE{ damagingMove = MOVE_DRAGON_BREATH; }
PARAMETRIZE{ damagingMove = MOVE_BITE; }
PARAMETRIZE{ damagingMove = MOVE_DISARMING_VOICE; }
PARAMETRIZE { damagingMove = MOVE_POUND; }
PARAMETRIZE { damagingMove = MOVE_KARATE_CHOP; }
PARAMETRIZE { damagingMove = MOVE_GUST; }
PARAMETRIZE { damagingMove = MOVE_POISON_STING; }
PARAMETRIZE { damagingMove = MOVE_EARTHQUAKE; }
PARAMETRIZE { damagingMove = MOVE_ROCK_THROW; }
PARAMETRIZE { damagingMove = MOVE_LEECH_LIFE; }
PARAMETRIZE { damagingMove = MOVE_LICK; }
PARAMETRIZE { damagingMove = MOVE_STEEL_WING; }
PARAMETRIZE { damagingMove = MOVE_EMBER; }
PARAMETRIZE { damagingMove = MOVE_WATER_GUN; }
PARAMETRIZE { damagingMove = MOVE_VINE_WHIP; }
PARAMETRIZE { damagingMove = MOVE_THUNDER_SHOCK; }
PARAMETRIZE { damagingMove = MOVE_CONFUSION; }
PARAMETRIZE { damagingMove = MOVE_ICE_BEAM; }
PARAMETRIZE { damagingMove = MOVE_DRAGON_BREATH; }
PARAMETRIZE { damagingMove = MOVE_BITE; }
PARAMETRIZE { damagingMove = MOVE_DISARMING_VOICE; }
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_TORNADUS].types[0] == TYPE_FLYING);
@@ -179,24 +179,24 @@ SINGLE_BATTLE_TEST("Roost, if used by a Flying/Flying type, treats the user as a
SINGLE_BATTLE_TEST("Roost, if used by a Mystery/Flying type, treats the user as a Mystery/Mystery type until the end of the turn")
{
u32 damagingMove;
PARAMETRIZE{ damagingMove = MOVE_POUND; }
PARAMETRIZE{ damagingMove = MOVE_KARATE_CHOP; }
PARAMETRIZE{ damagingMove = MOVE_GUST; }
PARAMETRIZE{ damagingMove = MOVE_POISON_STING; }
PARAMETRIZE{ damagingMove = MOVE_EARTHQUAKE; }
PARAMETRIZE{ damagingMove = MOVE_ROCK_THROW; }
PARAMETRIZE{ damagingMove = MOVE_LEECH_LIFE; }
PARAMETRIZE{ damagingMove = MOVE_LICK; }
PARAMETRIZE{ damagingMove = MOVE_STEEL_WING; }
PARAMETRIZE{ damagingMove = MOVE_EMBER; }
PARAMETRIZE{ damagingMove = MOVE_WATER_GUN; }
PARAMETRIZE{ damagingMove = MOVE_VINE_WHIP; }
PARAMETRIZE{ damagingMove = MOVE_THUNDER_SHOCK; }
PARAMETRIZE{ damagingMove = MOVE_CONFUSION; }
PARAMETRIZE{ damagingMove = MOVE_ICE_BEAM; }
PARAMETRIZE{ damagingMove = MOVE_DRAGON_BREATH; }
PARAMETRIZE{ damagingMove = MOVE_BITE; }
PARAMETRIZE{ damagingMove = MOVE_DISARMING_VOICE; }
PARAMETRIZE { damagingMove = MOVE_POUND; }
PARAMETRIZE { damagingMove = MOVE_KARATE_CHOP; }
PARAMETRIZE { damagingMove = MOVE_GUST; }
PARAMETRIZE { damagingMove = MOVE_POISON_STING; }
PARAMETRIZE { damagingMove = MOVE_EARTHQUAKE; }
PARAMETRIZE { damagingMove = MOVE_ROCK_THROW; }
PARAMETRIZE { damagingMove = MOVE_LEECH_LIFE; }
PARAMETRIZE { damagingMove = MOVE_LICK; }
PARAMETRIZE { damagingMove = MOVE_STEEL_WING; }
PARAMETRIZE { damagingMove = MOVE_EMBER; }
PARAMETRIZE { damagingMove = MOVE_WATER_GUN; }
PARAMETRIZE { damagingMove = MOVE_VINE_WHIP; }
PARAMETRIZE { damagingMove = MOVE_THUNDER_SHOCK; }
PARAMETRIZE { damagingMove = MOVE_CONFUSION; }
PARAMETRIZE { damagingMove = MOVE_ICE_BEAM; }
PARAMETRIZE { damagingMove = MOVE_DRAGON_BREATH; }
PARAMETRIZE { damagingMove = MOVE_BITE; }
PARAMETRIZE { damagingMove = MOVE_DISARMING_VOICE; }
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_MOLTRES].types[0] == TYPE_FIRE);

View File

@@ -31,7 +31,7 @@ SINGLE_BATTLE_TEST("Shell Side Arm can be mirror coated if it is special")
}
}
SINGLE_BATTLE_TEST("Shell Side Arm does not change catogory mid-turn")
SINGLE_BATTLE_TEST("Shell Side Arm does not change category mid-turn")
{
s16 damage[3];
@@ -56,7 +56,7 @@ SINGLE_BATTLE_TEST("Shell Side Arm does not change catogory mid-turn")
}
}
DOUBLE_BATTLE_TEST("Shell Side Arm is choosing it's type for each battler on the field")
DOUBLE_BATTLE_TEST("Shell Side Arm chooses its category for each battler on the field")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Speed(10); Moves(MOVE_SHELL_SIDE_ARM); }

View File

@@ -170,7 +170,6 @@ AI_DOUBLE_BATTLE_TEST("Spicy Extract user will use it if partner holds Clear Amu
PARAMETRIZE { move = MOVE_TACKLE; }
PARAMETRIZE { move = MOVE_SWIFT;}
AI_LOG;
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET) { Speed(10); }

View File

@@ -56,7 +56,7 @@ DOUBLE_BATTLE_TEST("Sticky Web lowers Speed by 1 in a double battle after Explos
OPPONENT(SPECIES_WOBBUFFET) {HP(1); Speed(1);}
OPPONENT(SPECIES_WOBBUFFET) {HP(1); Speed(1);}
OPPONENT(SPECIES_WYNAUT) {Speed(10);}
OPPONENT(SPECIES_WYNAUT) {Speed(10);}
OPPONENT(SPECIES_ALAKAZAM) {Speed(100);}
} WHEN {
TURN { MOVE(playerRight, MOVE_STICKY_WEB); MOVE(playerLeft, MOVE_EXPLOSION); SEND_OUT(playerLeft, 2); SEND_OUT(opponentLeft, 2); SEND_OUT(opponentRight, 3); }
TURN {}
@@ -65,13 +65,13 @@ DOUBLE_BATTLE_TEST("Sticky Web lowers Speed by 1 in a double battle after Explos
MESSAGE("A sticky web spreads out on the ground around the opposing team!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, playerLeft);
MESSAGE("2 sent out Wynaut!");
MESSAGE("2 sent out Alakazam!");
MESSAGE("Foe Alakazam was caught in a Sticky Web!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
MESSAGE("Foe Alakazam's Speed fell!");
MESSAGE("Foe Wynaut was caught in a Sticky Web!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
MESSAGE("Foe Wynaut's Speed fell!");
MESSAGE("2 sent out Wynaut!");
MESSAGE("Foe Wynaut was caught in a Sticky Web!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
MESSAGE("Foe Wynaut's Speed fell!");
}
}

View File

@@ -10,8 +10,8 @@ SINGLE_BATTLE_TEST("Strength Sap lowers Attack by 1 and restores HP based on tar
{
u32 atkStat = 0;
PARAMETRIZE{ atkStat = 100; }
PARAMETRIZE{ atkStat = 50; }
PARAMETRIZE { atkStat = 100; }
PARAMETRIZE { atkStat = 50; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(200); }
@@ -35,8 +35,8 @@ SINGLE_BATTLE_TEST("Strength Sap works exactly the same when attacker is behind
{
u32 atkStat = 0;
PARAMETRIZE{ atkStat = 100; }
PARAMETRIZE{ atkStat = 50; }
PARAMETRIZE { atkStat = 100; }
PARAMETRIZE { atkStat = 50; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(200); }
@@ -65,7 +65,7 @@ SINGLE_BATTLE_TEST("Strength Sap lowers Attack by 1 and restores HP based on tar
for (j = 0; j <= MAX_STAT_STAGE; j++) {
if (j == DEFAULT_STAT_STAGE - 1) { continue; } // Ignore -6, because Strength Sap won't work otherwise
PARAMETRIZE{ statStage = j; }
PARAMETRIZE { statStage = j; }
}
GIVEN {

View File

@@ -59,3 +59,18 @@ SINGLE_BATTLE_TEST("Tera Starstorm becomes a physical move if the user is Terapa
EXPECT_MUL_EQ(results[0].damage, UQ_4_12(2.5), results[1].damage);
}
}
SINGLE_BATTLE_TEST("Tera Starstorm remains Normal-type if used by Pokemon other than Terapagos")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_TERA_STARSTORM].type == TYPE_NORMAL);
ASSUME(gSpeciesInfo[SPECIES_MISDREAVUS].types[0] == TYPE_GHOST);
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); }
OPPONENT(SPECIES_MISDREAVUS);
} WHEN {
TURN { MOVE(player, MOVE_TERA_STARSTORM, gimmick: GIMMICK_TERA); }
} SCENE {
MESSAGE("Wobbuffet used Tera Starstorm!");
MESSAGE("It doesn't affect Foe Misdreavus…");
}
}

View File

@@ -74,13 +74,14 @@ SINGLE_BATTLE_TEST("Tidy Up removes Substitute")
}
}
AI_SINGLE_BATTLE_TEST("AI prefers to keep it's substitute over removing hazards if target is slower")
AI_SINGLE_BATTLE_TEST("AI prefers to keep its substitute over removing hazards if target is slower")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET) { Speed(50); Status1(STATUS1_PARALYSIS); Moves(MOVE_SLEEP_POWDER, MOVE_STEALTH_ROCK, MOVE_CELEBRATE); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(100); Moves(MOVE_BITE, MOVE_TACKLE, MOVE_SUBSTITUTE, MOVE_TIDY_UP); }
} WHEN {
TURN { MOVE(player, MOVE_STEALTH_ROCK); EXPECT_MOVE(opponent, MOVE_TIDY_UP); }
TURN { MOVE(player, MOVE_STEALTH_ROCK); EXPECT_MOVE(opponent, MOVE_SUBSTITUTE); }
TURN { EXPECT_MOVE(opponent, MOVE_BITE); }
}
@@ -93,6 +94,7 @@ AI_SINGLE_BATTLE_TEST("AI will try to remove hazards if slower then target even
PLAYER(SPECIES_WOBBUFFET) { Speed(100); Status1(STATUS1_BURN); Moves(MOVE_SLEEP_POWDER, MOVE_STEALTH_ROCK, MOVE_CELEBRATE); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(50); Moves(MOVE_BITE, MOVE_TACKLE, MOVE_SUBSTITUTE, MOVE_TIDY_UP); }
} WHEN {
TURN { MOVE(player, MOVE_STEALTH_ROCK); EXPECT_MOVE(opponent, MOVE_TIDY_UP); }
TURN { MOVE(player, MOVE_STEALTH_ROCK); EXPECT_MOVE(opponent, MOVE_SUBSTITUTE); }
TURN { EXPECT_MOVE(opponent, MOVE_TIDY_UP); }
}

View File

@@ -48,3 +48,21 @@ SINGLE_BATTLE_TEST("Toxic cannot miss if used by a Poison-type")
}
}
}
AI_SINGLE_BATTLE_TEST("AI avoids toxic when it can not poison target")
{
u32 species, ability;
PARAMETRIZE { species = SPECIES_SNORLAX; ability = ABILITY_IMMUNITY; }
PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; }
PARAMETRIZE { species = SPECIES_NACLI; ability = ABILITY_PURIFYING_SALT; }
PARAMETRIZE { species = SPECIES_BULBASAUR; ability = ABILITY_OVERGROW; }
GIVEN {
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_CELEBRATE, MOVE_TOXIC); }
} WHEN {
TURN { SCORE_EQ(opponent, MOVE_CELEBRATE, MOVE_TOXIC); } // Both get -10
}
}

View File

@@ -243,8 +243,8 @@ SINGLE_BATTLE_TEST("Solar Beam and Solar Blade can be used instantly in Sunlight
SINGLE_BATTLE_TEST("Solar Beam's power is halved in Rain", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_RAIN_DANCE; }
PARAMETRIZE { move = MOVE_CELEBRATE; }
PARAMETRIZE { move = MOVE_RAIN_DANCE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
@@ -261,8 +261,8 @@ SINGLE_BATTLE_TEST("Solar Beam's power is halved in Rain", s16 damage)
SINGLE_BATTLE_TEST("Solar Blade's power is halved in Rain", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_RAIN_DANCE; }
PARAMETRIZE { move = MOVE_CELEBRATE; }
PARAMETRIZE { move = MOVE_RAIN_DANCE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
@@ -279,8 +279,8 @@ SINGLE_BATTLE_TEST("Solar Blade's power is halved in Rain", s16 damage)
SINGLE_BATTLE_TEST("Solar Beam's power is halved in a Sandstorm", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_SANDSTORM; }
PARAMETRIZE { move = MOVE_CELEBRATE; }
PARAMETRIZE { move = MOVE_SANDSTORM; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); };
@@ -297,8 +297,8 @@ SINGLE_BATTLE_TEST("Solar Beam's power is halved in a Sandstorm", s16 damage)
SINGLE_BATTLE_TEST("Solar Blade's power is halved in a Sandstorm", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_SANDSTORM; }
PARAMETRIZE { move = MOVE_CELEBRATE; }
PARAMETRIZE { move = MOVE_SANDSTORM; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); };
@@ -315,8 +315,8 @@ SINGLE_BATTLE_TEST("Solar Blade's power is halved in a Sandstorm", s16 damage)
SINGLE_BATTLE_TEST("Solar Beam's power is halved in Hail", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_HAIL; }
PARAMETRIZE { move = MOVE_CELEBRATE; }
PARAMETRIZE { move = MOVE_HAIL; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); };
@@ -333,8 +333,8 @@ SINGLE_BATTLE_TEST("Solar Beam's power is halved in Hail", s16 damage)
SINGLE_BATTLE_TEST("Solar Blade's power is halved in Hail", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_HAIL; }
PARAMETRIZE { move = MOVE_CELEBRATE; }
PARAMETRIZE { move = MOVE_HAIL; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); };
@@ -351,8 +351,8 @@ SINGLE_BATTLE_TEST("Solar Blade's power is halved in Hail", s16 damage)
SINGLE_BATTLE_TEST("Solar Beam's power is halved in Snow", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_SNOWSCAPE; }
PARAMETRIZE { move = MOVE_CELEBRATE; }
PARAMETRIZE { move = MOVE_SNOWSCAPE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
@@ -369,8 +369,8 @@ SINGLE_BATTLE_TEST("Solar Beam's power is halved in Snow", s16 damage)
SINGLE_BATTLE_TEST("Solar Blade's power is halved in Snow", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_SNOWSCAPE; }
PARAMETRIZE { move = MOVE_CELEBRATE; }
PARAMETRIZE { move = MOVE_SNOWSCAPE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);

View File

@@ -116,3 +116,19 @@ SINGLE_BATTLE_TEST("Upper Hand is boosted by Sheer Force")
HP_BAR(player);
}
}
AI_SINGLE_BATTLE_TEST("AI won't use Upper Hand unless it has seen a priority move")
{
u16 move;
PARAMETRIZE { move = MOVE_TACKLE; }
PARAMETRIZE { move = MOVE_QUICK_ATTACK; }
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
ASSUME(gMovesInfo[MOVE_QUICK_ATTACK].priority == 1);
PLAYER(SPECIES_WOBBUFFET) {Moves(move); }
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_UPPER_HAND, MOVE_KARATE_CHOP); }
} WHEN {
TURN { MOVE(player, move); EXPECT_MOVE(opponent, MOVE_KARATE_CHOP); }
TURN { MOVE(player, move); EXPECT_MOVE(opponent, move == MOVE_QUICK_ATTACK ? MOVE_UPPER_HAND : MOVE_KARATE_CHOP); }
}
}

View File

@@ -9,8 +9,8 @@ ASSUMPTIONS
SINGLE_BATTLE_TEST("Weather Ball doubles its power and turns to a Fire-type move in Sunlight", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_SUNNY_DAY; }
PARAMETRIZE { move = MOVE_CELEBRATE; }
PARAMETRIZE { move = MOVE_SUNNY_DAY; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_MEGANIUM);
@@ -27,8 +27,8 @@ SINGLE_BATTLE_TEST("Weather Ball doubles its power and turns to a Fire-type move
SINGLE_BATTLE_TEST("Weather Ball doubles its power and turns to a Water-type move in Rain", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_RAIN_DANCE; }
PARAMETRIZE { move = MOVE_CELEBRATE; }
PARAMETRIZE { move = MOVE_RAIN_DANCE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_ARCANINE);
@@ -45,8 +45,8 @@ SINGLE_BATTLE_TEST("Weather Ball doubles its power and turns to a Water-type mov
SINGLE_BATTLE_TEST("Weather Ball doubles its power and turns to a Rock-type move in a Sandstorm", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_SANDSTORM; }
PARAMETRIZE { move = MOVE_CELEBRATE; }
PARAMETRIZE { move = MOVE_SANDSTORM; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_MAGMAR) { Item(ITEM_SAFETY_GOGGLES); };
@@ -63,9 +63,9 @@ SINGLE_BATTLE_TEST("Weather Ball doubles its power and turns to a Rock-type move
SINGLE_BATTLE_TEST("Weather Ball doubles its power and turns to an Ice-type move in Hail and Snow", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE{ move = MOVE_HAIL; }
PARAMETRIZE{ move = MOVE_SNOWSCAPE; }
PARAMETRIZE { move = MOVE_CELEBRATE; }
PARAMETRIZE { move = MOVE_HAIL; }
PARAMETRIZE { move = MOVE_SNOWSCAPE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_DRAGONAIR) { Item(ITEM_SAFETY_GOGGLES); };

View File

@@ -67,7 +67,8 @@ SINGLE_BATTLE_TEST("Dire Claw cannot poison/paralyze/cause to fall asleep pokemo
u8 statusAnim;
u16 species, ability;
u32 rng;
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = MOVE_EFFECT_PARALYSIS; species = SPECIES_RAICHU; ability = ABILITY_LIGHTNING_ROD; }
if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5)
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = MOVE_EFFECT_PARALYSIS; species = SPECIES_RAICHU; ability = ABILITY_LIGHTNING_ROD; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = MOVE_EFFECT_PARALYSIS; species = SPECIES_JOLTEON; ability = ABILITY_VOLT_ABSORB; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = MOVE_EFFECT_PARALYSIS; species = SPECIES_ELECTIVIRE; ability = ABILITY_MOTOR_DRIVE; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PSN; rng = MOVE_EFFECT_POISON; species = SPECIES_ZANGOOSE; ability = ABILITY_IMMUNITY; }

View File

@@ -7,7 +7,11 @@ ASSUMPTIONS
ASSUME(gMovesInfo[MOVE_BLIZZARD].accuracy == 70);
}
#if B_USE_FROSTBITE == TRUE
SINGLE_BATTLE_TEST("Powder Snow inflicts frostbite")
#else
SINGLE_BATTLE_TEST("Powder Snow inflicts freeze")
#endif
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
@@ -18,11 +22,15 @@ SINGLE_BATTLE_TEST("Powder Snow inflicts freeze")
ANIMATION(ANIM_TYPE_MOVE, MOVE_POWDER_SNOW, player);
HP_BAR(opponent);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, opponent);
STATUS_ICON(opponent, freeze: TRUE);
FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE);
}
}
#if B_USE_FROSTBITE == TRUE
SINGLE_BATTLE_TEST("Powder Snow cannot frostbite an Ice-type Pokémon")
#else
SINGLE_BATTLE_TEST("Powder Snow cannot freeze an Ice-type Pokémon")
#endif
{
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_SNORUNT].types[0] == TYPE_ICE);
@@ -35,7 +43,7 @@ SINGLE_BATTLE_TEST("Powder Snow cannot freeze an Ice-type Pokémon")
HP_BAR(opponent);
NONE_OF {
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, opponent);
STATUS_ICON(opponent, freeze: TRUE);
FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE);
}
}
}
@@ -68,10 +76,18 @@ SINGLE_BATTLE_TEST("Blizzard bypasses accuracy checks in Hail and Snow")
}
#if B_STATUS_TYPE_IMMUNITY > GEN_1
#if B_USE_FROSTBITE == TRUE
SINGLE_BATTLE_TEST("Freezing Glare should frostbite Psychic-types")
#else
SINGLE_BATTLE_TEST("Freezing Glare should freeze Psychic-types")
#endif
#else
#if B_USE_FROSTBITE == TRUE
SINGLE_BATTLE_TEST("Freezing Glare shouldn't freeze Psychic-types")
#else
SINGLE_BATTLE_TEST("Freezing Glare shouldn't freeze Psychic-types")
#endif
#endif
{
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_ARTICUNO_GALARIAN].types[0] == TYPE_PSYCHIC);
@@ -86,11 +102,11 @@ SINGLE_BATTLE_TEST("Freezing Glare shouldn't freeze Psychic-types")
HP_BAR(opponent);
#if B_STATUS_TYPE_IMMUNITY > GEN_1
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, opponent);
STATUS_ICON(opponent, freeze: TRUE);
FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE);
#else
NONE_OF {
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, opponent);
STATUS_ICON(opponent, freeze: TRUE);
FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE);
}
#endif
}

View File

@@ -6,7 +6,11 @@ ASSUMPTIONS
ASSUME(MoveHasAdditionalEffect(MOVE_TRI_ATTACK, MOVE_EFFECT_TRI_ATTACK) == TRUE);
}
#if B_USE_FROSTBITE == TRUE
SINGLE_BATTLE_TEST("Tri Attack can inflict paralysis, burn or frostbite")
#else
SINGLE_BATTLE_TEST("Tri Attack can inflict paralysis, burn or freeze")
#endif
{
u8 statusAnim;
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; }
@@ -26,14 +30,18 @@ SINGLE_BATTLE_TEST("Tri Attack can inflict paralysis, burn or freeze")
if (statusAnim == B_ANIM_STATUS_BRN) {
STATUS_ICON(opponent, burn: TRUE);
} else if (statusAnim == B_ANIM_STATUS_FRZ) {
STATUS_ICON(opponent, freeze: TRUE);
FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE);
} else if (statusAnim == B_ANIM_STATUS_PRZ) {
STATUS_ICON(opponent, paralysis: TRUE);
}
}
}
#if B_USE_FROSTBITE == TRUE
SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/frostbite electric/fire/ice types respectively")
#else
SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze electric/fire/ice types respectively")
#endif
{
u8 statusAnim;
u16 species;
@@ -42,7 +50,7 @@ SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze electric/fire/ice typ
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = MOVE_EFFECT_PARALYSIS; species = SPECIES_RAICHU; }
#endif // B_PARALYZE_ELECTRIC
PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = MOVE_EFFECT_BURN; species = SPECIES_ARCANINE; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = MOVE_EFFECT_FREEZE; species = SPECIES_GLALIE; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = MOVE_EFFECT_FREEZE_OR_FROSTBITE; species = SPECIES_GLALIE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(species);
@@ -57,7 +65,7 @@ SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze electric/fire/ice typ
if (statusAnim == B_ANIM_STATUS_BRN) {
STATUS_ICON(opponent, burn: TRUE);
} else if (statusAnim == B_ANIM_STATUS_FRZ) {
STATUS_ICON(opponent, freeze: TRUE);
FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE);
} else if (statusAnim == B_ANIM_STATUS_PRZ) {
STATUS_ICON(opponent, paralysis: TRUE);
}
@@ -65,7 +73,11 @@ SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze electric/fire/ice typ
}
}
#if B_USE_FROSTBITE == TRUE
SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/frostbite pokemon with abilities preventing respective statuses")
#else
SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze pokemon with abilities preventing respective statuses")
#endif
{
u8 statusAnim;
u16 species, ability;
@@ -75,8 +87,8 @@ SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze pokemon with abilitie
PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = MOVE_EFFECT_BURN; species = SPECIES_DEWPIDER; ability = ABILITY_WATER_BUBBLE; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = MOVE_EFFECT_BURN; species = SPECIES_SEAKING; ability = ABILITY_WATER_VEIL; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = MOVE_EFFECT_BURN; species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = MOVE_EFFECT_FREEZE; species = SPECIES_CAMERUPT; ability = ABILITY_MAGMA_ARMOR; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = MOVE_EFFECT_FREEZE; species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = MOVE_EFFECT_FREEZE_OR_FROSTBITE; species = SPECIES_CAMERUPT; ability = ABILITY_MAGMA_ARMOR; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = MOVE_EFFECT_FREEZE_OR_FROSTBITE; species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
@@ -92,7 +104,7 @@ SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze pokemon with abilitie
if (statusAnim == B_ANIM_STATUS_BRN) {
STATUS_ICON(opponent, burn: TRUE);
} else if (statusAnim == B_ANIM_STATUS_FRZ) {
STATUS_ICON(opponent, freeze: TRUE);
FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE);
} else if (statusAnim == B_ANIM_STATUS_PRZ) {
STATUS_ICON(opponent, paralysis: TRUE);
}
@@ -100,13 +112,17 @@ SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze pokemon with abilitie
}
}
#if B_USE_FROSTBITE == TRUE
SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/frostbite a mon which is already statused")
#else
SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze a mon which is already statused")
#endif
{
u8 statusAnim;
u32 rng;
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = MOVE_EFFECT_PARALYSIS; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = MOVE_EFFECT_BURN; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = MOVE_EFFECT_FREEZE; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = MOVE_EFFECT_FREEZE_OR_FROSTBITE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); }
@@ -121,7 +137,7 @@ SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze a mon which is alread
if (statusAnim == B_ANIM_STATUS_BRN) {
STATUS_ICON(opponent, burn: TRUE);
} else if (statusAnim == B_ANIM_STATUS_FRZ) {
STATUS_ICON(opponent, freeze: TRUE);
FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE);
} else if (statusAnim == B_ANIM_STATUS_PRZ) {
STATUS_ICON(opponent, paralysis: TRUE);
}

View File

@@ -5,7 +5,7 @@ ASSUMPTIONS
{
ASSUME(MoveHasAdditionalEffect(MOVE_THUNDER_FANG, MOVE_EFFECT_PARALYSIS) == TRUE);
ASSUME(MoveHasAdditionalEffect(MOVE_THUNDER_FANG, MOVE_EFFECT_FLINCH) == TRUE);
ASSUME(MoveHasAdditionalEffect(MOVE_ICE_FANG, MOVE_EFFECT_FREEZE) == TRUE);
ASSUME(MoveHasAdditionalEffect(MOVE_ICE_FANG, MOVE_EFFECT_FREEZE_OR_FROSTBITE) == TRUE);
ASSUME(MoveHasAdditionalEffect(MOVE_ICE_FANG, MOVE_EFFECT_FLINCH) == TRUE);
ASSUME(MoveHasAdditionalEffect(MOVE_FIRE_FANG, MOVE_EFFECT_BURN) == TRUE);
ASSUME(MoveHasAdditionalEffect(MOVE_FIRE_FANG, MOVE_EFFECT_FLINCH) == TRUE);
@@ -33,7 +33,7 @@ SINGLE_BATTLE_TEST("Thunder, Ice and Fire Fang inflict status 10% of the time")
STATUS_ICON(opponent, paralysis: TRUE);
} if (move == MOVE_ICE_FANG) {
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, opponent);
STATUS_ICON(opponent, freeze: TRUE);
FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE);
} if (move == MOVE_FIRE_FANG) {
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponent);
STATUS_ICON(opponent, burn: TRUE);

View File

@@ -32,4 +32,42 @@ SINGLE_BATTLE_TEST("Hurricane bypasses accuracy checks in Rain")
NONE_OF { MESSAGE("Wobbuffet's attack missed!"); }
}
}
TO_DO_BATTLE_TEST("Hurricane Veil can hit airborne targets") // Fly, Bounce, Sky Drop
SINGLE_BATTLE_TEST("Hurricane can hit airborne targets (Fly, Bounce)")
{
u16 move;
PARAMETRIZE { move = MOVE_FLY; }
PARAMETRIZE { move = MOVE_BOUNCE; }
GIVEN {
ASSUME(gMovesInfo[MOVE_FLY].effect == EFFECT_SEMI_INVULNERABLE);
ASSUME(UNCOMPRESS_BITS(HIHALF(gMovesInfo[MOVE_FLY].argument)) == STATUS3_ON_AIR);
ASSUME(gMovesInfo[MOVE_BOUNCE].effect == EFFECT_SEMI_INVULNERABLE);
ASSUME(UNCOMPRESS_BITS(HIHALF(gMovesInfo[MOVE_BOUNCE].argument)) == STATUS3_ON_AIR);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Moves(move); }
} WHEN {
TURN { MOVE(opponent, move); MOVE(player, MOVE_HURRICANE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_HURRICANE, player);
NONE_OF { MESSAGE("Wobbuffet's attack missed!"); }
}
}
DOUBLE_BATTLE_TEST("Hurricane can hit airborne targets (Sky Drop)")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_SKY_DROP].effect == EFFECT_SKY_DROP);
ASSUME(UNCOMPRESS_BITS(HIHALF(gMovesInfo[MOVE_SKY_DROP].argument)) == STATUS3_ON_AIR);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(playerLeft, MOVE_SKY_DROP, target: opponentLeft); MOVE(playerRight, MOVE_HURRICANE, target: playerLeft); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_DROP, playerLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_HURRICANE, playerRight);
NONE_OF { MESSAGE("Wobbuffet's attack missed!"); }
}
}

View File

@@ -0,0 +1,74 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(gMovesInfo[MOVE_SUNSTEEL_STRIKE].ignoresTargetAbility);
ASSUME(gMovesInfo[MOVE_MOONGEIST_BEAM].ignoresTargetAbility);
ASSUME(gMovesInfo[MOVE_PHOTON_GEYSER].ignoresTargetAbility);
}
SINGLE_BATTLE_TEST("ignoresTargetAbility moves do not ignore the attacker's own ability", s16 damage)
{
u32 ability, move;
PARAMETRIZE { move = MOVE_SUNSTEEL_STRIKE; ability = ABILITY_MAGIC_GUARD; }
PARAMETRIZE { move = MOVE_SUNSTEEL_STRIKE; ability = ABILITY_UNAWARE; }
PARAMETRIZE { move = MOVE_MOONGEIST_BEAM; ability = ABILITY_MAGIC_GUARD; }
PARAMETRIZE { move = MOVE_MOONGEIST_BEAM; ability = ABILITY_UNAWARE; }
PARAMETRIZE { move = MOVE_PHOTON_GEYSER; ability = ABILITY_MAGIC_GUARD; }
PARAMETRIZE { move = MOVE_PHOTON_GEYSER; ability = ABILITY_UNAWARE; }
ASSUME(gAbilitiesInfo[ABILITY_UNAWARE].breakable);
ASSUME(gMovesInfo[MOVE_IRON_DEFENSE].effect == EFFECT_DEFENSE_UP_2);
ASSUME(gMovesInfo[MOVE_AMNESIA].effect == EFFECT_SPECIAL_DEFENSE_UP_2);
GIVEN {
PLAYER(SPECIES_CLEFABLE) { Speed(1); Ability(ability); }
OPPONENT(SPECIES_ARON) { Speed(2); }
} WHEN {
if (gMovesInfo[move].category == DAMAGE_CATEGORY_PHYSICAL)
TURN { MOVE(opponent, MOVE_IRON_DEFENSE); MOVE(player, move); }
else
TURN { MOVE(opponent, MOVE_AMNESIA); MOVE(player, move); }
} SCENE {
if (gMovesInfo[move].category == DAMAGE_CATEGORY_PHYSICAL)
ANIMATION(ANIM_TYPE_MOVE, MOVE_IRON_DEFENSE, opponent);
else
ANIMATION(ANIM_TYPE_MOVE, MOVE_AMNESIA, opponent);
ANIMATION(ANIM_TYPE_MOVE, move, player);
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, UQ_4_12(2.0), results[1].damage);
EXPECT_MUL_EQ(results[2].damage, UQ_4_12(2.0), results[3].damage);
EXPECT_MUL_EQ(results[4].damage, UQ_4_12(2.0), results[5].damage);
}
}
SINGLE_BATTLE_TEST("ignoresTargetAbility moves do ignore target's abilities", s16 damage)
{
u32 ability, move;
PARAMETRIZE { move = MOVE_SUNSTEEL_STRIKE; ability = ABILITY_INNER_FOCUS; }
PARAMETRIZE { move = MOVE_SUNSTEEL_STRIKE; ability = ABILITY_MULTISCALE; }
PARAMETRIZE { move = MOVE_MOONGEIST_BEAM; ability = ABILITY_INNER_FOCUS; }
PARAMETRIZE { move = MOVE_MOONGEIST_BEAM; ability = ABILITY_MULTISCALE; }
PARAMETRIZE { move = MOVE_PHOTON_GEYSER; ability = ABILITY_INNER_FOCUS; }
PARAMETRIZE { move = MOVE_PHOTON_GEYSER; ability = ABILITY_MULTISCALE; }
ASSUME(gAbilitiesInfo[ABILITY_MULTISCALE].breakable);
GIVEN {
PLAYER(SPECIES_AZUMARILL);
OPPONENT(SPECIES_DRAGONITE) { Ability(ability); }
} WHEN {
TURN { MOVE(player, move); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, move, player);
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_EQ(results[0].damage, results[1].damage);
EXPECT_EQ(results[2].damage, results[3].damage);
EXPECT_EQ(results[4].damage, results[5].damage);
}
}

View File

@@ -35,3 +35,23 @@ SINGLE_BATTLE_TEST("Burn reduces Attack by 50%", s16 damage)
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage);
}
}
AI_SINGLE_BATTLE_TEST("AI avoids Will-o-Wisp when it can not burn target")
{
u32 species, ability;
PARAMETRIZE { species = SPECIES_BUIZEL; ability = ABILITY_WATER_VEIL; }
PARAMETRIZE { species = SPECIES_DEWPIDER; ability = ABILITY_WATER_BUBBLE; }
PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; }
PARAMETRIZE { species = SPECIES_ARCTIBAX; ability = ABILITY_THERMAL_EXCHANGE; }
PARAMETRIZE { species = SPECIES_NACLI; ability = ABILITY_PURIFYING_SALT; }
PARAMETRIZE { species = SPECIES_CHARMANDER; ability = ABILITY_BLAZE; }
GIVEN {
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_CELEBRATE, MOVE_WILL_O_WISP); }
} WHEN {
TURN { SCORE_EQ(opponent, MOVE_CELEBRATE, MOVE_WILL_O_WISP); } // Both get -10
}
}

View File

@@ -42,3 +42,21 @@ SINGLE_BATTLE_TEST("Paralysis has a 25% chance of skipping the turn")
MESSAGE("Wobbuffet is paralyzed! It can't move!");
}
}
AI_SINGLE_BATTLE_TEST("AI avoids Thunder Wave when it can not paralyse target")
{
u32 species, ability;
PARAMETRIZE { species = SPECIES_HITMONLEE; ability = ABILITY_LIMBER; }
PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; }
PARAMETRIZE { species = SPECIES_NACLI; ability = ABILITY_PURIFYING_SALT; }
PARAMETRIZE { species = SPECIES_PIKACHU; ability = ABILITY_STATIC; }
GIVEN {
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_CELEBRATE, MOVE_THUNDER_WAVE); }
} WHEN {
TURN { SCORE_EQ(opponent, MOVE_CELEBRATE, MOVE_THUNDER_WAVE); } // Both get -10
}
}

View File

@@ -21,3 +21,21 @@ SINGLE_BATTLE_TEST("Sleep prevents the battler from using a move")
MESSAGE("Wobbuffet used Celebrate!");
}
}
AI_SINGLE_BATTLE_TEST("AI avoids hypnosis when it can not put target to sleep")
{
u32 species, ability;
PARAMETRIZE { species = SPECIES_HOOTHOOT; ability = ABILITY_INSOMNIA; }
PARAMETRIZE { species = SPECIES_MANKEY; ability = ABILITY_VITAL_SPIRIT; }
PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; }
PARAMETRIZE { species = SPECIES_NACLI; ability = ABILITY_PURIFYING_SALT; }
GIVEN {
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_CELEBRATE, MOVE_HYPNOSIS); }
} WHEN {
TURN { SCORE_EQ(opponent, MOVE_CELEBRATE, MOVE_HYPNOSIS); } // Both get -10
}
}

View File

@@ -0,0 +1,28 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Confusion adds a 50/33% chance to hit self with 40 power")
{
s16 damage[2];
ASSUME(gMovesInfo[MOVE_TACKLE].power == 40);
PASSES_RANDOMLY(B_CONFUSION_SELF_DMG_CHANCE >= GEN_7 ? 33 : 50, 100, RNG_CONFUSION);
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Speed(1); };
OPPONENT(SPECIES_WOBBUFFET) { Speed(2); };
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE, WITH_RNG(RNG_DAMAGE_MODIFIER, 0)); MOVE(player, MOVE_CONFUSE_RAY); }
TURN;
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
HP_BAR(player, captureDamage: &damage[0]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CONFUSE_RAY, player);
MESSAGE("Foe Wobbuffet became confused!");
MESSAGE("Foe Wobbuffet is confused!");
MESSAGE("It hurt itself in its confusion!");
HP_BAR(opponent, captureDamage: &damage[1]);
} THEN {
EXPECT_EQ(damage[0], damage[1]);
}
}

View File

@@ -85,3 +85,27 @@ SINGLE_BATTLE_TEST("Grassy Terrain lasts for 5 turns")
MESSAGE("The grass disappeared from the battlefield.");
}
}
SINGLE_BATTLE_TEST("Grassy Terrain heals the pokemon on the field for the duration of the terrain, including last turn")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { HP(1); };
} WHEN {
TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_GRASSY_TERRAIN); }
TURN {}
TURN {}
TURN {}
TURN {}
} SCENE {
MESSAGE("Foe Wobbuffet used Celebrate!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_GRASSY_TERRAIN, player);
MESSAGE("Grass grew to cover the battlefield!");
MESSAGE("Foe Wobbuffet is healed by the grassy terrain!");
MESSAGE("Foe Wobbuffet is healed by the grassy terrain!");
MESSAGE("Foe Wobbuffet is healed by the grassy terrain!");
MESSAGE("Foe Wobbuffet is healed by the grassy terrain!");
MESSAGE("Foe Wobbuffet is healed by the grassy terrain!");
MESSAGE("The grass disappeared from the battlefield.");
}
}

View File

@@ -0,0 +1,27 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS {
int i;
for (i = 0; i < NUM_ABILITY_SLOTS; i++) {
ASSUME(gSpeciesInfo[SPECIES_KADABRA].abilities[i] != ABILITY_QUARK_DRIVE);
ASSUME(gSpeciesInfo[SPECIES_ALAKAZAM].abilities[i] != ABILITY_ELECTRIC_SURGE);
}
}
SINGLE_BATTLE_TEST("Forced abilities activate on switch-in")
{
GIVEN {
PLAYER(SPECIES_ALAKAZAM);
PLAYER(SPECIES_KADABRA) { Ability(ABILITY_QUARK_DRIVE); SpAttack(400);}
OPPONENT(SPECIES_ARON);
OPPONENT(SPECIES_ALAKAZAM) { Ability(ABILITY_ELECTRIC_SURGE); };
} WHEN {
TURN { SWITCH(player, 1); SWITCH(opponent, 1);}
} SCENE {
ABILITY_POPUP(opponent, ABILITY_ELECTRIC_SURGE);
ABILITY_POPUP(player, ABILITY_QUARK_DRIVE);
MESSAGE("The Electric Terrain activated Kadabra's Quark Drive!");
MESSAGE("Kadabra's Sp. Atk was heightened!");
}
}

View File

@@ -113,3 +113,50 @@ TEST("ModifyPersonalityForNature can set any nature")
ModifyPersonalityForNature(&personality, nature);
EXPECT_EQ(GetNatureFromPersonality(personality), nature);
}
static const struct TrainerMon sTestParty2[] =
{
{
.species = SPECIES_WYNAUT,
.lvl = 5,
},
{
.species = SPECIES_WYNAUT,
.lvl = 5,
},
{
.species = SPECIES_WYNAUT,
.lvl = 5,
},
{
.species = SPECIES_WYNAUT,
.lvl = 5,
},
{
.species = SPECIES_WYNAUT,
.lvl = 5,
},
{
.species = SPECIES_WYNAUT,
.lvl = 5,
},
};
static const struct Trainer sTestTrainer2 =
{
.trainerName = _("Test2"),
.trainerClass = TRAINER_CLASS_BLACK_BELT,
.party = TRAINER_PARTY(sTestParty2),
};
TEST("Trainer Class Balls apply to the entire party")
{
u32 j;
struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon));
CreateNPCTrainerPartyFromTrainer(testParty, &sTestTrainer2, TRUE, BATTLE_TYPE_TRAINER);
for(j = 0; j < 6; j++)
{
EXPECT(GetMonData(&testParty[j], MON_DATA_POKEBALL, 0) == gTrainerClasses[sTestTrainer2.trainerClass].ball);
}
Free(testParty);
}

View File

@@ -52,7 +52,7 @@
.isShiny = TRUE,
#line 18
.dynamaxLevel = 5,
.useGimmick = GIMMICK_DYNAMAX,
.shouldUseDynamax = TRUE,
.moves = {
#line 19
MOVE_AIR_SLASH,

View File

@@ -53,3 +53,20 @@ SINGLE_BATTLE_TEST("Hail fails if Desolate Land or Primordial Sea are active")
}
}
}
DOUBLE_BATTLE_TEST("Hail deals damage based on turn order")
{
GIVEN {
PLAYER(SPECIES_GLALIE);
PLAYER(SPECIES_WYNAUT) { Speed(1); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(2); }
OPPONENT(SPECIES_WYNAUT) { Speed(3); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_HAIL); }
} SCENE {
NOT HP_BAR(playerLeft);
HP_BAR(opponentRight);
HP_BAR(opponentLeft);
HP_BAR(playerRight);
}
}

View File

@@ -20,8 +20,8 @@ SINGLE_BATTLE_TEST("Sandstorm deals 1/16 damage per turn")
SINGLE_BATTLE_TEST("Sandstorm multiplies the special defense of Rock-types by 1.5x", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_SANDSTORM; }
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE { move = MOVE_SANDSTORM; }
PARAMETRIZE { move = MOVE_CELEBRATE; }
GIVEN {
ASSUME(gMovesInfo[MOVE_SWIFT].category == DAMAGE_CATEGORY_SPECIAL);
PLAYER(SPECIES_WOBBUFFET) ;
@@ -65,3 +65,20 @@ SINGLE_BATTLE_TEST("Sandstorm damage does not hurt Ground, Rock, and Steel-type
}
}
}
DOUBLE_BATTLE_TEST("Sandstorm deals damage based on turn order")
{
GIVEN {
PLAYER(SPECIES_PHANPY);
PLAYER(SPECIES_WYNAUT) { Speed(1); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(2); }
OPPONENT(SPECIES_WYNAUT) { Speed(3); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_SANDSTORM); }
} SCENE {
NOT HP_BAR(playerLeft);
HP_BAR(opponentRight);
HP_BAR(opponentLeft);
HP_BAR(playerRight);
}
}

View File

@@ -13,8 +13,8 @@ ASSUMPTIONS
SINGLE_BATTLE_TEST("Snow multiplies the defense of Ice-types by 1.5x", s16 damage)
{
u16 move;
PARAMETRIZE{ move = MOVE_SNOWSCAPE; }
PARAMETRIZE{ move = MOVE_CELEBRATE; }
PARAMETRIZE { move = MOVE_SNOWSCAPE; }
PARAMETRIZE { move = MOVE_CELEBRATE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_GLALIE);

View File

@@ -1,4 +1,5 @@
#include "global.h"
#include "string_util.h"
#include "test/test.h"
#include "constants/form_change_types.h"
@@ -138,3 +139,18 @@ TEST("No species has two evolutions that use the evolution tracker")
EXPECT(evolutionTrackerEvolutions < 2);
}
extern const u8 gFallbackPokedexText[];
TEST("Every species has a description")
{
u32 i;
u32 species = SPECIES_NONE;
for (i = 1; i < NUM_SPECIES; i++)
{
if (IsSpeciesEnabled(i))
PARAMETRIZE { species = i; }
}
EXPECT_NE(StringCompare(GetSpeciesPokedexDescription(species), gFallbackPokedexText), 0);
}

View File

@@ -120,7 +120,6 @@ top:
MoveSaveBlocks_ResetHeap();
ClearSav1();
ClearSav2();
// ClearSav3();
gIntrTable[7] = Intr_Timer2;
@@ -181,6 +180,7 @@ top:
}
Test_MgbaPrintf(":N%s", gTestRunnerState.test->name);
Test_MgbaPrintf(":L%s:%d", gTestRunnerState.test->filename);
gTestRunnerState.result = TEST_RESULT_PASS;
gTestRunnerState.expectedResult = TEST_RESULT_PASS;
gTestRunnerState.expectLeaks = FALSE;
@@ -218,6 +218,7 @@ top:
// NOTE: Assumes that the compiler interns __FILE__.
if (gTestRunnerState.skipFilename == gTestRunnerState.test->filename) // Assumption fails for tests in this file.
{
Test_MgbaPrintf(":L%s:%d", gTestRunnerState.test->filename, gTestRunnerState.failedAssumptionsBlockLine);
gTestRunnerState.result = TEST_RESULT_ASSUMPTION_FAIL;
return;
}
@@ -482,9 +483,10 @@ static void Intr_Timer2(void)
}
}
void Test_ExitWithResult(enum TestResult result, const char *fmt, ...)
void Test_ExitWithResult(enum TestResult result, u32 stopLine, const char *fmt, ...)
{
gTestRunnerState.result = result;
gTestRunnerState.failedAssumptionsBlockLine = stopLine;
ReinitCallbacks();
if (gTestRunnerState.state == STATE_REPORT_RESULT
&& gTestRunnerState.result != gTestRunnerState.expectedResult)
@@ -692,3 +694,24 @@ void DACSHandle(void)
ReinitCallbacks();
DACS_LR = ((uintptr_t)JumpToAgbMainLoop & ~1) + 4;
}
static const struct Test *GetTest(void)
{
const struct Test *test = gTestRunnerState.test;
return test;
}
u32 SourceLine(u32 sourceLineOffset)
{
const struct Test *test = GetTest();
return test->sourceLine + sourceLineOffset;
}
u32 SourceLineOffset(u32 sourceLine)
{
const struct Test *test = GetTest();
if (sourceLine - test->sourceLine > 0xFF)
return 0;
else
return sourceLine - test->sourceLine;
}

View File

@@ -27,10 +27,10 @@
#undef TestRunner_Battle_GetForcedAbility
#endif
#define INVALID(fmt, ...) Test_ExitWithResult(TEST_RESULT_INVALID, "%s:%d: " fmt, gTestRunnerState.test->filename, sourceLine, ##__VA_ARGS__)
#define INVALID_IF(c, fmt, ...) do { if (c) Test_ExitWithResult(TEST_RESULT_INVALID, "%s:%d: " fmt, gTestRunnerState.test->filename, sourceLine, ##__VA_ARGS__); } while (0)
#define INVALID(fmt, ...) Test_ExitWithResult(TEST_RESULT_INVALID, sourceLine, ":L%s:%d: " fmt, gTestRunnerState.test->filename, sourceLine, ##__VA_ARGS__)
#define INVALID_IF(c, fmt, ...) do { if (c) Test_ExitWithResult(TEST_RESULT_INVALID, sourceLine, ":L%s:%d: " fmt, gTestRunnerState.test->filename, sourceLine, ##__VA_ARGS__); } while (0)
#define ASSUMPTION_FAIL_IF(c, fmt, ...) do { if (c) Test_ExitWithResult(TEST_RESULT_ASSUMPTION_FAIL, "%s:%d: " fmt, gTestRunnerState.test->filename, sourceLine, ##__VA_ARGS__); } while (0)
#define ASSUMPTION_FAIL_IF(c, fmt, ...) do { if (c) Test_ExitWithResult(TEST_RESULT_ASSUMPTION_FAIL, sourceLine, ":L%s:%d: " fmt, gTestRunnerState.test->filename, sourceLine, ##__VA_ARGS__); } while (0)
#define STATE gBattleTestRunnerState
#define DATA gBattleTestRunnerState->data
@@ -147,21 +147,6 @@ static bool32 IsAITest(void)
return FALSE;
}
static u32 SourceLine(u32 sourceLineOffset)
{
const struct BattleTest *test = GetBattleTest();
return test->sourceLine + sourceLineOffset;
}
static u32 SourceLineOffset(u32 sourceLine)
{
const struct BattleTest *test = GetBattleTest();
if (sourceLine - test->sourceLine > 0xFF)
return 0;
else
return sourceLine - test->sourceLine;
}
static u32 BattleTest_EstimateCost(void *data)
{
u32 cost;
@@ -186,9 +171,9 @@ static void BattleTest_SetUp(void *data)
InvokeTestFunction(test);
STATE->parameters = STATE->parametersCount;
if (STATE->parametersCount == 0 && test->resultsSize > 0)
Test_ExitWithResult(TEST_RESULT_INVALID, "results without PARAMETRIZE");
Test_ExitWithResult(TEST_RESULT_INVALID, SourceLine(0), ":Lresults without PARAMETRIZE");
if (sizeof(*STATE) + test->resultsSize * STATE->parameters > sizeof(sBackupMapData))
Test_ExitWithResult(TEST_RESULT_ERROR, "OOM: STATE (%d) + STATE->results (%d) too big for sBackupMapData (%d)", sizeof(*STATE), test->resultsSize * STATE->parameters, sizeof(sBackupMapData));
Test_ExitWithResult(TEST_RESULT_ERROR, SourceLine(0), ":LOOM: STATE (%d) + STATE->results (%d) too big for sBackupMapData (%d)", sizeof(*STATE), test->resultsSize * STATE->parameters, sizeof(sBackupMapData));
STATE->results = (void *)((char *)sBackupMapData + sizeof(struct BattleTestRunnerState));
memset(STATE->results, 0, test->resultsSize * STATE->parameters);
switch (test->type)
@@ -262,7 +247,7 @@ static void SetImplicitSpeeds(void)
}
}
if (!madeProgress)
Test_ExitWithResult(TEST_RESULT_INVALID, "TURNs have contradictory speeds");
Test_ExitWithResult(TEST_RESULT_INVALID, SourceLine(0), ":LTURNs have contradictory speeds");
}
}
@@ -336,9 +321,9 @@ static void BattleTest_Run(void *data)
requiredOpponentPartySize = DATA.currentMonIndexes[i] + 1;
}
if (DATA.playerPartySize < requiredPlayerPartySize)
Test_ExitWithResult(TEST_RESULT_INVALID, "%d PLAYER Pokemon required", requiredPlayerPartySize);
Test_ExitWithResult(TEST_RESULT_INVALID, SourceLine(0), ":L%d PLAYER Pokemon required", requiredPlayerPartySize);
if (DATA.opponentPartySize < requiredOpponentPartySize)
Test_ExitWithResult(TEST_RESULT_INVALID, "%d OPPONENT Pokemon required", requiredOpponentPartySize);
Test_ExitWithResult(TEST_RESULT_INVALID, SourceLine(0), ":L%d OPPONENT Pokemon required", requiredOpponentPartySize);
for (i = 0; i < STATE->battlersCount; i++)
PushBattlerAction(0, i, RECORDED_BYTE, 0xFF);
@@ -348,7 +333,7 @@ static void BattleTest_Run(void *data)
if (DATA.explicitSpeeds[B_SIDE_PLAYER] != (1 << DATA.playerPartySize) - 1
&& DATA.explicitSpeeds[B_SIDE_OPPONENT] != (1 << DATA.opponentPartySize) - 1)
{
Test_ExitWithResult(TEST_RESULT_INVALID, "Speed required for all PLAYERs and OPPONENTs");
Test_ExitWithResult(TEST_RESULT_INVALID, SourceLine(0), ":LSpeed required for all PLAYERs and OPPONENTs");
}
}
else
@@ -394,7 +379,7 @@ u32 RandomUniform(enum RandomTag tag, u32 lo, u32 hi)
}
else if (STATE->trials != n)
{
Test_ExitWithResult(TEST_RESULT_ERROR, "RandomUniform called with inconsistent trials %d and %d", STATE->trials, n);
Test_ExitWithResult(TEST_RESULT_ERROR, SourceLine(0), ":LRandomUniform called with inconsistent trials %d and %d", STATE->trials, n);
}
STATE->trialRatio = Q_4_12(1) / STATE->trials;
return STATE->runTrial + lo;
@@ -415,7 +400,7 @@ u32 RandomUniformExcept(enum RandomTag tag, u32 lo, u32 hi, bool32 (*reject)(u32
if (turn && turn->rng.tag == tag)
{
if (reject(turn->rng.value))
Test_ExitWithResult(TEST_RESULT_INVALID, "WITH_RNG specified a rejected value (%d)", turn->rng.value);
Test_ExitWithResult(TEST_RESULT_INVALID, SourceLine(0), ":LWITH_RNG specified a rejected value (%d)", turn->rng.value);
return turn->rng.value;
}
}
@@ -436,7 +421,7 @@ u32 RandomUniformExcept(enum RandomTag tag, u32 lo, u32 hi, bool32 (*reject)(u32
while (reject(STATE->runTrial + lo + STATE->rngTrialOffset))
{
if (STATE->runTrial + lo + STATE->rngTrialOffset > hi)
Test_ExitWithResult(TEST_RESULT_INVALID, "RandomUniformExcept called with inconsistent reject");
Test_ExitWithResult(TEST_RESULT_INVALID, SourceLine(0), ":LRandomUniformExcept called with inconsistent reject");
STATE->rngTrialOffset++;
}
@@ -447,7 +432,7 @@ u32 RandomUniformExcept(enum RandomTag tag, u32 lo, u32 hi, bool32 (*reject)(u32
while (reject(default_))
{
if (default_ == lo)
Test_ExitWithResult(TEST_RESULT_INVALID, "RandomUniformExcept rejected all values");
Test_ExitWithResult(TEST_RESULT_INVALID, SourceLine(0), ":LRandomUniformExcept rejected all values");
default_--;
}
return default_;
@@ -458,7 +443,7 @@ u32 RandomWeightedArray(enum RandomTag tag, u32 sum, u32 n, const u8 *weights)
const struct BattlerTurn *turn = NULL;
if (sum == 0)
Test_ExitWithResult(TEST_RESULT_ERROR, "RandomWeightedArray called with zero sum");
Test_ExitWithResult(TEST_RESULT_ERROR, SourceLine(0), ":LRandomWeightedArray called with zero sum");
if (gCurrentTurnActionNumber < gBattlersCount)
{
@@ -477,7 +462,7 @@ u32 RandomWeightedArray(enum RandomTag tag, u32 sum, u32 n, const u8 *weights)
}
else if (STATE->trials != n)
{
Test_ExitWithResult(TEST_RESULT_ERROR, "RandomWeighted called with inconsistent trials %d and %d", STATE->trials, n);
Test_ExitWithResult(TEST_RESULT_ERROR, SourceLine(0), ":LRandomWeighted called with inconsistent trials %d and %d", STATE->trials, n);
}
// TODO: Detect inconsistent sum.
STATE->trialRatio = Q_4_12(weights[STATE->runTrial]) / sum;
@@ -511,7 +496,7 @@ u32 RandomWeightedArray(enum RandomTag tag, u32 sum, u32 n, const u8 *weights)
while (weights[n-1] == 0)
{
if (n == 1)
Test_ExitWithResult(TEST_RESULT_ERROR, "RandomWeightedArray called with all zero weights");
Test_ExitWithResult(TEST_RESULT_ERROR, SourceLine(0), ":LRandomWeightedArray called with all zero weights");
n--;
}
return n-1;
@@ -537,7 +522,7 @@ const void *RandomElementArray(enum RandomTag tag, const void *array, size_t siz
return (const u8 *)array + size * index;
}
// TODO: Incorporate the line number.
Test_ExitWithResult(TEST_RESULT_ERROR, "%s: RandomElement illegal value requested: %d", gTestRunnerState.test->filename, turn->rng.value);
Test_ExitWithResult(TEST_RESULT_ERROR, SourceLine(0), ":L%s: RandomElement illegal value requested: %d", gTestRunnerState.test->filename, turn->rng.value);
}
}
@@ -550,7 +535,7 @@ const void *RandomElementArray(enum RandomTag tag, const void *array, size_t siz
}
else if (STATE->trials != count)
{
Test_ExitWithResult(TEST_RESULT_ERROR, "RandomElement called with inconsistent trials %d and %d", STATE->trials, count);
Test_ExitWithResult(TEST_RESULT_ERROR, SourceLine(0), ":LRandomElement called with inconsistent trials %d and %d", STATE->trials, count);
}
STATE->trialRatio = Q_4_12(1) / count;
return (const u8 *)array + size * STATE->runTrial;
@@ -601,7 +586,7 @@ void TestRunner_Battle_RecordAbilityPopUp(u32 battlerId, u32 ability)
{
const char *filename = gTestRunnerState.test->filename;
u32 line = SourceLine(DATA.queuedEvents[match].sourceLineOffset);
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Matched ABILITY_POPUP", filename, line);
Test_ExitWithResult(TEST_RESULT_FAIL, line, ":L%s:%d: Matched ABILITY_POPUP", filename, line);
}
queuedEvent += event->groupSize;
@@ -664,7 +649,7 @@ void TestRunner_Battle_RecordAnimation(u32 animType, u32 animId)
{
const char *filename = gTestRunnerState.test->filename;
u32 line = SourceLine(DATA.queuedEvents[match].sourceLineOffset);
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Matched ANIMATION", filename, line);
Test_ExitWithResult(TEST_RESULT_FAIL, line, ":L%s:%d: Matched ANIMATION", filename, line);
}
queuedEvent += event->groupSize;
@@ -754,7 +739,7 @@ void TestRunner_Battle_RecordHP(u32 battlerId, u32 oldHP, u32 newHP)
{
const char *filename = gTestRunnerState.test->filename;
u32 line = SourceLine(DATA.queuedEvents[match].sourceLineOffset);
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Matched HP_BAR", filename, line);
Test_ExitWithResult(TEST_RESULT_FAIL, line, ":L%s:%d: Matched HP_BAR", filename, line);
}
queuedEvent += event->groupSize;
@@ -809,10 +794,10 @@ void TestRunner_Battle_CheckChosenMove(u32 battlerId, u32 moveId, u32 target)
bool32 movePasses = FALSE;
if (expectedAction->type != B_ACTION_USE_MOVE)
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Expected %s, got MOVE", filename, expectedAction->sourceLine, sBattleActionNames[expectedAction->type]);
Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: Expected %s, got MOVE", filename, expectedAction->sourceLine, sBattleActionNames[expectedAction->type]);
if (expectedAction->explicitTarget && expectedAction->target != target)
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Expected target %s, got %s", filename, expectedAction->sourceLine, BattlerIdentifier(expectedAction->target), BattlerIdentifier(target));
Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: Expected target %s, got %s", filename, expectedAction->sourceLine, BattlerIdentifier(expectedAction->target), BattlerIdentifier(target));
for (i = 0; i < MAX_MON_MOVES; i++)
{
@@ -846,16 +831,16 @@ void TestRunner_Battle_CheckChosenMove(u32 battlerId, u32 moveId, u32 target)
u32 moveSlot = GetMoveSlot(gBattleMons[battlerId].moves, moveId);
PrintAiMoveLog(battlerId, moveSlot, moveId, gBattleStruct->aiFinalScore[battlerId][expectedAction->target][moveSlot]);
if (countExpected > 1)
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Unmatched EXPECT_MOVES %S, got %S", filename, expectedAction->sourceLine, GetMoveName(expectedMoveId), GetMoveName(moveId));
Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: Unmatched EXPECT_MOVES %S, got %S", filename, expectedAction->sourceLine, GetMoveName(expectedMoveId), GetMoveName(moveId));
else
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Unmatched EXPECT_MOVE %S, got %S", filename, expectedAction->sourceLine, GetMoveName(expectedMoveId), GetMoveName(moveId));
Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: Unmatched EXPECT_MOVE %S, got %S", filename, expectedAction->sourceLine, GetMoveName(expectedMoveId), GetMoveName(moveId));
}
if (expectedAction->notMove && !movePasses)
{
if (countExpected > 1)
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Unmatched NOT_EXPECT_MOVES %S", filename, expectedAction->sourceLine, GetMoveName(expectedMoveId));
Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: Unmatched NOT_EXPECT_MOVES %S", filename, expectedAction->sourceLine, GetMoveName(expectedMoveId));
else
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Unmatched NOT_EXPECT_MOVE %S", filename, expectedAction->sourceLine, GetMoveName(expectedMoveId));
Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: Unmatched NOT_EXPECT_MOVE %S", filename, expectedAction->sourceLine, GetMoveName(expectedMoveId));
}
}
// Turn passed, clear logs from the turn
@@ -875,17 +860,17 @@ void TestRunner_Battle_CheckSwitch(u32 battlerId, u32 partyIndex)
if (!expectedAction->pass)
{
if (expectedAction->type != B_ACTION_SWITCH)
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Expected %s, got SWITCH/SEND_OUT", filename, expectedAction->sourceLine, sBattleActionNames[expectedAction->type]);
Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: Expected %s, got SWITCH/SEND_OUT", filename, expectedAction->sourceLine, sBattleActionNames[expectedAction->type]);
if (expectedAction->target != partyIndex)
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Expected partyIndex %d, got %d", filename, expectedAction->sourceLine, expectedAction->target, partyIndex);
Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: Expected partyIndex %d, got %d", filename, expectedAction->sourceLine, expectedAction->target, partyIndex);
}
DATA.aiActionsPlayed[battlerId]++;
}
void TestRunner_Battle_InvalidNoHPMon(u32 battlerId, u32 partyIndex)
{
Test_ExitWithResult(TEST_RESULT_INVALID, "%s: INVALID: %s trying to send out a mon(id: %d) with 0 HP.",
Test_ExitWithResult(TEST_RESULT_INVALID, SourceLine(0), ":L%s: INVALID: %s trying to send out a mon(id: %d) with 0 HP.",
gTestRunnerState.test->filename, BattlerIdentifier(battlerId), gBattlerPartyIndexes[battlerId]);
}
@@ -935,7 +920,7 @@ static void CheckIfMaxScoreEqualExpectMove(u32 battlerId, s32 target, struct Exp
&& (aiAction->moveSlots & gBitTable[i])
&& !(aiAction->moveSlots & gBitTable[bestScoreId]))
{
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT_MOVE %S has the same best score(%d) as not expected MOVE %S", filename,
Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: EXPECT_MOVE %S has the same best score(%d) as not expected MOVE %S", filename,
aiAction->sourceLine, GetMoveName(moves[i]), scores[i], GetMoveName(moves[bestScoreId]));
}
// We DO NOT expect move 'i', but it has the same best score as another move.
@@ -944,7 +929,7 @@ static void CheckIfMaxScoreEqualExpectMove(u32 battlerId, s32 target, struct Exp
&& (aiAction->moveSlots & gBitTable[i])
&& !(aiAction->moveSlots & gBitTable[bestScoreId]))
{
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: NOT_EXPECT_MOVE %S has the same best score(%d) as MOVE %S", filename,
Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: NOT_EXPECT_MOVE %S has the same best score(%d) as MOVE %S", filename,
aiAction->sourceLine, GetMoveName(moves[i]), scores[i], GetMoveName(moves[bestScoreId]));
}
}
@@ -987,7 +972,7 @@ static void PrintAiMoveLog(u32 battlerId, u32 moveSlot, u32 moveId, s32 totalSco
}
if (scoreFromLogs != totalScore)
{
Test_ExitWithResult(TEST_RESULT_ERROR, "Warning! Score from logs(%d) is different than actual score(%d). Make sure all of the score adjustments use the ADJUST_SCORE macro\n", scoreFromLogs, totalScore);
Test_ExitWithResult(TEST_RESULT_ERROR, SourceLine(0), ":LWarning! Score from logs(%d) is different than actual score(%d). Make sure all of the score adjustments use the ADJUST_SCORE macro\n", scoreFromLogs, totalScore);
}
Test_MgbaPrintf("Total: %d\n", totalScore);
}
@@ -1025,7 +1010,7 @@ void TestRunner_Battle_CheckAiMoveScores(u32 battlerId)
PrintAiMoveLog(battlerId, scoreCtx->moveSlot1, moveId1, scores[scoreCtx->moveSlot1]);
if (!CheckComparision(scores[scoreCtx->moveSlot1], scoreCtx->value, scoreCtx->cmp))
{
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Unmatched SCORE_%s_VAL %S %d, got %d",
Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: Unmatched SCORE_%s_VAL %S %d, got %d",
filename, scoreCtx->sourceLine, sCmpToStringTable[scoreCtx->cmp], GetMoveName(moveId1), scoreCtx->value, scores[scoreCtx->moveSlot1]);
}
}
@@ -1036,7 +1021,7 @@ void TestRunner_Battle_CheckAiMoveScores(u32 battlerId)
PrintAiMoveLog(battlerId, scoreCtx->moveSlot2, moveId2, scores[scoreCtx->moveSlot2]);
if (!CheckComparision(scores[scoreCtx->moveSlot1], scores[scoreCtx->moveSlot2], scoreCtx->cmp))
{
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Unmatched SCORE_%s, got %S: %d, %S: %d",
Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: Unmatched SCORE_%s, got %S: %d, %S: %d",
filename, scoreCtx->sourceLine, sCmpToStringTable[scoreCtx->cmp], GetMoveName(moveId1), scores[scoreCtx->moveSlot1], GetMoveName(moveId2), scores[scoreCtx->moveSlot2]);
}
}
@@ -1136,7 +1121,7 @@ void TestRunner_Battle_RecordExp(u32 battlerId, u32 oldExp, u32 newExp)
{
const char *filename = gTestRunnerState.test->filename;
u32 line = SourceLine(DATA.queuedEvents[match].sourceLineOffset);
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Matched EXPERIENCE_BAR", filename, line);
Test_ExitWithResult(TEST_RESULT_FAIL, line, ":L%s:%d: Matched EXPERIENCE_BAR", filename, line);
}
queuedEvent += event->groupSize;
@@ -1225,7 +1210,7 @@ void TestRunner_Battle_RecordMessage(const u8 *string)
{
const char *filename = gTestRunnerState.test->filename;
u32 line = SourceLine(DATA.queuedEvents[match].sourceLineOffset);
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Matched MESSAGE", filename, line);
Test_ExitWithResult(TEST_RESULT_FAIL, line, ":L%s:%d: Matched MESSAGE", filename, line);
}
queuedEvent += event->groupSize;
@@ -1290,7 +1275,7 @@ void TestRunner_Battle_RecordStatus1(u32 battlerId, u32 status1)
{
const char *filename = gTestRunnerState.test->filename;
u32 line = SourceLine(DATA.queuedEvents[match].sourceLineOffset);
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Matched STATUS_ICON", filename, line);
Test_ExitWithResult(TEST_RESULT_FAIL, line, ":L%s:%d: Matched STATUS_ICON", filename, line);
}
queuedEvent += event->groupSize;
@@ -1325,7 +1310,7 @@ void TestRunner_Battle_AfterLastTurn(void)
if (DATA.turns - 1 != DATA.lastActionTurn)
{
const char *filename = gTestRunnerState.test->filename;
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: %d TURNs specified, but %d ran", filename, SourceLine(0), DATA.turns, DATA.lastActionTurn + 1);
Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: %d TURNs specified, but %d ran", filename, SourceLine(0), DATA.turns, DATA.lastActionTurn + 1);
}
while (DATA.queuedEvent < DATA.queuedEventsCount
@@ -1338,7 +1323,7 @@ void TestRunner_Battle_AfterLastTurn(void)
const char *filename = gTestRunnerState.test->filename;
u32 line = SourceLine(DATA.queuedEvents[DATA.queuedEvent].sourceLineOffset);
const char *macro = sEventTypeMacros[DATA.queuedEvents[DATA.queuedEvent].type];
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: Unmatched %s", filename, line, macro);
Test_ExitWithResult(TEST_RESULT_FAIL, line, ":L%s:%d: Unmatched %s", filename, line, macro);
}
STATE->runThen = TRUE;
@@ -1420,7 +1405,7 @@ static void CB2_BattleTest_NextTrial(void)
if (abs(STATE->observedRatio - STATE->expectedRatio) <= Q_4_12(0.02))
gTestRunnerState.result = TEST_RESULT_PASS;
else
Test_ExitWithResult(TEST_RESULT_FAIL, "Expected %q passes/successes, observed %q", STATE->expectedRatio, STATE->observedRatio);
Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: Expected %q passes/successes, observed %q", gTestRunnerState.test->filename, SourceLine(0), STATE->expectedRatio, STATE->observedRatio);
}
}
@@ -1639,6 +1624,7 @@ void Ability_(u32 sourceLine, u32 ability)
u32 species;
const struct SpeciesInfo *info;
INVALID_IF(!DATA.currentMon, "Ability outside of PLAYER/OPPONENT");
INVALID_IF(ability >= ABILITIES_COUNT, "Illegal ability id: %d", ability);
species = GetMonData(DATA.currentMon, MON_DATA_SPECIES);
info = &gSpeciesInfo[species];
for (i = 0; i < NUM_ABILITY_SLOTS; i++)
@@ -1875,7 +1861,7 @@ static void PushBattlerAction(u32 sourceLine, s32 battlerId, u32 actionType, u32
{
u32 recordIndex = DATA.recordIndexes[battlerId]++;
if (recordIndex >= BATTLER_RECORD_SIZE)
Test_ExitWithResult(TEST_RESULT_INVALID, "Too many actions");
Test_ExitWithResult(TEST_RESULT_INVALID, SourceLine(0), ":LToo many actions");
DATA.battleRecordTypes[battlerId][recordIndex] = actionType;
DATA.battleRecordSourceLineOffsets[battlerId][recordIndex] = SourceLineOffset(sourceLine);
DATA.recordedBattle.battleRecord[battlerId][recordIndex] = byte;
@@ -1897,10 +1883,10 @@ void TestRunner_Battle_CheckBattleRecordActionType(u32 battlerId, u32 recordInde
&& DATA.recordedBattle.battleRecord[battlerId][i-1] == B_ACTION_USE_MOVE)
{
u32 line = SourceLine(DATA.battleRecordSourceLineOffsets[battlerId][i-1]);
Test_ExitWithResult(TEST_RESULT_INVALID, "%s:%d: Illegal MOVE", filename, line);
Test_ExitWithResult(TEST_RESULT_INVALID, line, ":L%s:%d: Illegal MOVE", filename, line);
}
}
Test_ExitWithResult(TEST_RESULT_INVALID, "%s:%d: Illegal MOVE", filename, SourceLine(0));
Test_ExitWithResult(TEST_RESULT_INVALID, SourceLine(0), ":L%s:%d: Illegal MOVE", filename, SourceLine(0));
}
if (DATA.battleRecordTypes[battlerId][recordIndex] != RECORDED_BYTE)
@@ -1928,13 +1914,13 @@ void TestRunner_Battle_CheckBattleRecordActionType(u32 battlerId, u32 recordInde
switch (actionType)
{
case RECORDED_ACTION_TYPE:
Test_ExitWithResult(TEST_RESULT_INVALID, "%s:%d: Expected MOVE/SWITCH, got %s", filename, line, actualMacro);
Test_ExitWithResult(TEST_RESULT_INVALID, line, ":L%s:%d: Expected MOVE/SWITCH, got %s", filename, line, actualMacro);
case RECORDED_PARTY_INDEX:
Test_ExitWithResult(TEST_RESULT_INVALID, "%s:%d: Expected SEND_OUT, got %s", filename, line, actualMacro);
Test_ExitWithResult(TEST_RESULT_INVALID, line, ":L%s:%d: Expected SEND_OUT, got %s", filename, line, actualMacro);
}
}
Test_ExitWithResult(TEST_RESULT_ERROR, "%s:%d: Illegal battle record", filename, line);
Test_ExitWithResult(TEST_RESULT_ERROR, line, ":L%s:%d: Illegal battle record", filename, line);
}
}
else
@@ -1942,7 +1928,7 @@ void TestRunner_Battle_CheckBattleRecordActionType(u32 battlerId, u32 recordInde
if (DATA.lastActionTurn == gBattleResults.battleTurnCounter)
{
const char *filename = gTestRunnerState.test->filename;
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: TURN %d incomplete", filename, SourceLine(0), gBattleResults.battleTurnCounter + 1);
Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: TURN %d incomplete", filename, SourceLine(0), gBattleResults.battleTurnCounter + 1);
}
}
}
@@ -1951,7 +1937,7 @@ void OpenTurn(u32 sourceLine)
{
INVALID_IF(DATA.turnState != TURN_CLOSED, "Nested TURN");
if (DATA.turns == MAX_TURNS)
Test_ExitWithResult(TEST_RESULT_ERROR, "%s:%d: TURN exceeds MAX_TURNS", gTestRunnerState.test->filename, sourceLine);
Test_ExitWithResult(TEST_RESULT_ERROR, sourceLine, ":L%s:%d: TURN exceeds MAX_TURNS", gTestRunnerState.test->filename, sourceLine);
DATA.turnState = TURN_OPEN;
DATA.actionBattlers = 0x00;
DATA.moveBattlers = 0x00;
@@ -2483,7 +2469,7 @@ void QueueAbility(u32 sourceLine, struct BattlePokemon *battler, struct AbilityE
s32 battlerId = battler - gBattleMons;
INVALID_IF(!STATE->runScene, "ABILITY_POPUP outside of SCENE");
if (DATA.queuedEventsCount == MAX_QUEUED_EVENTS)
Test_ExitWithResult(TEST_RESULT_ERROR, "%s:%d: ABILITY exceeds MAX_QUEUED_EVENTS", gTestRunnerState.test->filename, sourceLine);
Test_ExitWithResult(TEST_RESULT_ERROR, sourceLine, ":L%s:%d: ABILITY exceeds MAX_QUEUED_EVENTS", gTestRunnerState.test->filename, sourceLine);
DATA.queuedEvents[DATA.queuedEventsCount++] = (struct QueuedEvent) {
.type = QUEUED_ABILITY_POPUP_EVENT,
.sourceLineOffset = SourceLineOffset(sourceLine),
@@ -2503,7 +2489,7 @@ void QueueAnimation(u32 sourceLine, u32 type, u32 id, struct AnimationEventConte
INVALID_IF(!STATE->runScene, "ANIMATION outside of SCENE");
if (DATA.queuedEventsCount == MAX_QUEUED_EVENTS)
Test_ExitWithResult(TEST_RESULT_ERROR, "%s:%d: ANIMATION exceeds MAX_QUEUED_EVENTS", gTestRunnerState.test->filename, sourceLine);
Test_ExitWithResult(TEST_RESULT_ERROR, sourceLine, ":L%s:%d: ANIMATION exceeds MAX_QUEUED_EVENTS", gTestRunnerState.test->filename, sourceLine);
attackerId = ctx.attacker ? ctx.attacker - gBattleMons : 0xF;
if (type == ANIM_TYPE_MOVE)
@@ -2538,7 +2524,7 @@ void QueueHP(u32 sourceLine, struct BattlePokemon *battler, struct HPEventContex
INVALID_IF(!STATE->runScene, "HP_BAR outside of SCENE");
if (DATA.queuedEventsCount == MAX_QUEUED_EVENTS)
Test_ExitWithResult(TEST_RESULT_ERROR, "%s:%d: HP_BAR exceeds MAX_QUEUED_EVENTS", gTestRunnerState.test->filename, sourceLine);
Test_ExitWithResult(TEST_RESULT_ERROR, sourceLine, ":L%s:%d: HP_BAR exceeds MAX_QUEUED_EVENTS", gTestRunnerState.test->filename, sourceLine);
if (ctx.explicitHP)
{
@@ -2591,7 +2577,7 @@ void QueueExp(u32 sourceLine, struct BattlePokemon *battler, struct ExpEventCont
INVALID_IF(!STATE->runScene, "EXPERIENCE_BAR outside of SCENE");
if (DATA.queuedEventsCount == MAX_QUEUED_EVENTS)
Test_ExitWithResult(TEST_RESULT_ERROR, "%s:%d: EXPERIENCE_BAR exceeds MAX_QUEUED_EVENTS", gTestRunnerState.test->filename, sourceLine);
Test_ExitWithResult(TEST_RESULT_ERROR, sourceLine, ":L%s:%d: EXPERIENCE_BAR exceeds MAX_QUEUED_EVENTS", gTestRunnerState.test->filename, sourceLine);
if (ctx.explicitExp)
{
@@ -2628,7 +2614,7 @@ void QueueMessage(u32 sourceLine, const u8 *pattern)
{
INVALID_IF(!STATE->runScene, "MESSAGE outside of SCENE");
if (DATA.queuedEventsCount == MAX_QUEUED_EVENTS)
Test_ExitWithResult(TEST_RESULT_ERROR, "%s:%d: MESSAGE exceeds MAX_QUEUED_EVENTS", gTestRunnerState.test->filename, sourceLine);
Test_ExitWithResult(TEST_RESULT_ERROR, sourceLine, ":L%s:%d: MESSAGE exceeds MAX_QUEUED_EVENTS", gTestRunnerState.test->filename, sourceLine);
DATA.queuedEvents[DATA.queuedEventsCount++] = (struct QueuedEvent) {
.type = QUEUED_MESSAGE_EVENT,
.sourceLineOffset = SourceLineOffset(sourceLine),
@@ -2647,7 +2633,7 @@ void QueueStatus(u32 sourceLine, struct BattlePokemon *battler, struct StatusEve
INVALID_IF(!STATE->runScene, "STATUS_ICON outside of SCENE");
if (DATA.queuedEventsCount == MAX_QUEUED_EVENTS)
Test_ExitWithResult(TEST_RESULT_ERROR, "%s:%d: STATUS_ICON exceeds MAX_QUEUED_EVENTS", gTestRunnerState.test->filename, sourceLine);
Test_ExitWithResult(TEST_RESULT_ERROR, sourceLine, ":L%s:%d: STATUS_ICON exceeds MAX_QUEUED_EVENTS", gTestRunnerState.test->filename, sourceLine);
if (ctx.none)
mask = 0;
@@ -2711,7 +2697,7 @@ struct AILogLine *GetLogLine(u32 battlerId, u32 moveIndex)
}
}
Test_ExitWithResult(TEST_RESULT_ERROR, "Too many AI log lines");
Test_ExitWithResult(TEST_RESULT_ERROR, SourceLine(0), ":LToo many AI log lines");
return NULL;
}

View File

@@ -275,7 +275,7 @@ TEST("Item names fit on Shop Screen")
TEST("Species names fit on Battle Screen HP box")
{
u32 i, genderWidthPx;
const u32 fontId = FONT_SMALL_NARROWER, widthPx = 54;
const u32 fontId = FONT_SMALL_NARROWER, widthPx = 55;
u32 species = SPECIES_NONE;
genderWidthPx = GetStringWidth(fontId, COMPOUND_STRING("♂"), 0);
for (i = 1; i < NUM_SPECIES; i++)
@@ -501,6 +501,25 @@ TEST("Species names fit on PokeNav Ribbon List Screen")
EXPECT_LE(GetStringWidth(fontId, gSpeciesInfo[species].speciesName, 0), widthPx);
}
TEST("Species names fit on Battle Screen HP box for vanilla mons with the default font")
{
u32 i, genderWidthPx;
const u32 fontId = FONT_SMALL, widthPx = 55;
u32 species = SPECIES_NONE;
genderWidthPx = GetStringWidth(fontId, COMPOUND_STRING("♂"), 0);
for (i = 1; i < SPECIES_TURTWIG; i++)
{
if (IsSpeciesEnabled(i))
{
PARAMETRIZE_LABEL("%S", gSpeciesInfo[i].speciesName) { species = i; }
}
}
if (gSpeciesInfo[i].genderRatio != MON_GENDERLESS)
EXPECT_LE(GetStringWidth(fontId, gSpeciesInfo[species].speciesName, 0) - genderWidthPx, widthPx);
else
EXPECT_LE(GetStringWidth(fontId, gSpeciesInfo[species].speciesName, 0), widthPx);
}
TEST("Ability names fit on Pokemon Summary Screen")
{
u32 i;

View File

@@ -8,6 +8,7 @@
*
* COMMANDS
* N: Sets the test name to the remainder of the line.
* L: Sets the filename to the remainder of the line.
* R: Sets the result to the remainder of the line, and flushes any
* output buffered since the previous R.
* P/K/F/A: Sets the result to the remaining of the line, flushes any
@@ -46,6 +47,7 @@ struct Runner
int outfd;
char rom_path[FILENAME_MAX];
char test_name[256];
char filename_line[256];
size_t input_buffer_size;
size_t input_buffer_capacity;
char *input_buffer;
@@ -59,8 +61,12 @@ struct Runner
int assumptionFails;
int fails;
int results;
char failedTestNames[MAX_SUMMARY_TESTS_TO_LIST][MAX_TEST_LIST_BUFFER_LENGTH];
char knownFailingPassedTestNames[MAX_SUMMARY_TESTS_TO_LIST][MAX_TEST_LIST_BUFFER_LENGTH];
char failed_TestNames[MAX_SUMMARY_TESTS_TO_LIST][MAX_TEST_LIST_BUFFER_LENGTH];
char failed_TestFilenameLine[MAX_SUMMARY_TESTS_TO_LIST][MAX_TEST_LIST_BUFFER_LENGTH];
char knownFailingPassed_TestNames[MAX_SUMMARY_TESTS_TO_LIST][MAX_TEST_LIST_BUFFER_LENGTH];
char knownFailingPassed_FilenameLine[MAX_SUMMARY_TESTS_TO_LIST][MAX_TEST_LIST_BUFFER_LENGTH];
char assumeFailed_TestNames[MAX_SUMMARY_TESTS_TO_LIST * MAX_PROCESSES][MAX_TEST_LIST_BUFFER_LENGTH];
char assumeFailed_FilenameLine[MAX_SUMMARY_TESTS_TO_LIST * MAX_PROCESSES][MAX_TEST_LIST_BUFFER_LENGTH];
};
static unsigned nrunners = 0;
@@ -102,6 +108,16 @@ static void handle_read(int i, struct Runner *runner)
strncpy(runner->test_name, soc, eol - soc - 1);
runner->test_name[eol - soc - 1] = '\0';
break;
case 'L':
soc += 2;
if (sizeof(runner->filename_line) <= eol - soc - 1)
{
fprintf(stderr, "filename_line too long\n");
exit(2);
}
strncpy(runner->filename_line, soc, eol - soc - 1);
runner->filename_line[eol - soc - 1] = '\0';
break;
case 'P':
runner->passes++;
@@ -111,18 +127,29 @@ static void handle_read(int i, struct Runner *runner)
goto add_to_results;
case 'U':
if (runner->knownFailsPassing < MAX_SUMMARY_TESTS_TO_LIST)
strcpy(runner->knownFailingPassedTestNames[runner->knownFailsPassing], runner->test_name);
{
strcpy(runner->knownFailingPassed_TestNames[runner->knownFailsPassing], runner->test_name);
strcpy(runner->knownFailingPassed_FilenameLine[runner->knownFailsPassing], runner->filename_line);
}
runner->knownFailsPassing++;
goto add_to_results;
case 'T':
runner->todos++;
goto add_to_results;
case 'A':
if (runner->assumptionFails < MAX_SUMMARY_TESTS_TO_LIST)
{
strcpy(runner->assumeFailed_TestNames[runner->assumptionFails], runner->test_name);
strcpy(runner->assumeFailed_FilenameLine[runner->assumptionFails], runner->filename_line);
}
runner->assumptionFails++;
goto add_to_results;
case 'F':
if (runner->fails < MAX_SUMMARY_TESTS_TO_LIST)
strcpy(runner->failedTestNames[runner->fails], runner->test_name);
{
strcpy(runner->failed_TestNames[runner->fails], runner->test_name);
strcpy(runner->failed_TestFilenameLine[runner->fails], runner->filename_line);
}
runner->fails++;
add_to_results:
runner->results++;
@@ -532,8 +559,14 @@ int main(int argc, char *argv[])
int fails = 0;
int results = 0;
char failedTestNames[MAX_SUMMARY_TESTS_TO_LIST * MAX_PROCESSES][MAX_TEST_LIST_BUFFER_LENGTH];
char knownFailingPassedTestNames[MAX_SUMMARY_TESTS_TO_LIST * MAX_PROCESSES][MAX_TEST_LIST_BUFFER_LENGTH];
char failed_TestNames[MAX_SUMMARY_TESTS_TO_LIST * MAX_PROCESSES][MAX_TEST_LIST_BUFFER_LENGTH];
char failed_TestFilenameLine[MAX_SUMMARY_TESTS_TO_LIST * MAX_PROCESSES][MAX_TEST_LIST_BUFFER_LENGTH];
char knownFailingPassed_TestNames[MAX_SUMMARY_TESTS_TO_LIST * MAX_PROCESSES][MAX_TEST_LIST_BUFFER_LENGTH];
char knownFailingPassed_FilenameLine[MAX_SUMMARY_TESTS_TO_LIST * MAX_PROCESSES][MAX_TEST_LIST_BUFFER_LENGTH];
char assumeFailed_TestNames[MAX_SUMMARY_TESTS_TO_LIST * MAX_PROCESSES][MAX_TEST_LIST_BUFFER_LENGTH];
char assumeFailed_FilenameLine[MAX_SUMMARY_TESTS_TO_LIST * MAX_PROCESSES][MAX_TEST_LIST_BUFFER_LENGTH];
for (int i = 0; i < nrunners; i++)
{
@@ -552,33 +585,43 @@ int main(int argc, char *argv[])
for (int j = 0; j < runners[i].knownFailsPassing; j++)
{
if (j < MAX_SUMMARY_TESTS_TO_LIST)
strcpy(knownFailingPassedTestNames[fails], runners[i].knownFailingPassedTestNames[j]);
{
strcpy(knownFailingPassed_TestNames[knownFailsPassing], runners[i].knownFailingPassed_TestNames[j]);
strcpy(knownFailingPassed_FilenameLine[knownFailsPassing], runners[i].knownFailingPassed_FilenameLine[j]);
}
knownFailsPassing++;
}
todos += runners[i].todos;
assumptionFails += runners[i].assumptionFails;
for (int j = 0; j < runners[i].assumptionFails; j++)
{
if (j < MAX_SUMMARY_TESTS_TO_LIST)
{
strcpy(assumeFailed_TestNames[assumptionFails], runners[i].assumeFailed_TestNames[j]);
strcpy(assumeFailed_FilenameLine[assumptionFails], runners[i].assumeFailed_FilenameLine[j]);
}
assumptionFails++;
}
for (int j = 0; j < runners[i].fails; j++)
{
if (j < MAX_SUMMARY_TESTS_TO_LIST)
strcpy(failedTestNames[fails], runners[i].failedTestNames[j]);
{
strcpy(failed_TestNames[fails], runners[i].failed_TestNames[j]);
strcpy(failed_TestFilenameLine[fails], runners[i].failed_TestFilenameLine[j]);
}
fails++;
}
results += runners[i].results;
}
qsort(failedTestNames, min(fails, MAX_SUMMARY_TESTS_TO_LIST), sizeof(char) * MAX_TEST_LIST_BUFFER_LENGTH, compare_strings);
qsort(knownFailingPassedTestNames, min(fails, MAX_SUMMARY_TESTS_TO_LIST), sizeof(char) * MAX_TEST_LIST_BUFFER_LENGTH, compare_strings);
if (results == 0)
{
fprintf(stdout, "\nNo tests found.\n");
}
else
{
fprintf(stdout, "\n");
if (fails > 0)
{
fprintf(stdout, "- Tests \e[31mFAILED\e[0m : %d Add TESTS='X' to run tests with the defined prefix.\n", fails);
fprintf(stdout, "\n \e[31mFAILED\e[0m tests:\n");
for (int i = 0; i < fails; i++)
{
if (i >= MAX_SUMMARY_TESTS_TO_LIST)
@@ -586,31 +629,51 @@ int main(int argc, char *argv[])
fprintf(stdout, " - \e[31mand %d more...\e[0m\n", fails - MAX_SUMMARY_TESTS_TO_LIST);
break;
}
fprintf(stdout, " - \e[31m%s\e[0m.\n", failedTestNames[i]);
fprintf(stdout, " - \e[31m%s\e[0m - %s.\n", failed_TestFilenameLine[i], failed_TestNames[i]);
}
}
if (assumptionFails > 0)
{
fprintf(stdout, "\n Tests with \e[33mASSUMPTIONS_FAILED\e[0m:\n");
for (int i = 0; i < assumptionFails; i++)
{
if (i >= MAX_SUMMARY_TESTS_TO_LIST)
{
fprintf(stdout, " - \e[33mand %d more...\e[0m\n", assumptionFails - MAX_SUMMARY_TESTS_TO_LIST);
break;
}
fprintf(stdout, " - \e[33m%s\e[0m - %s.\n", assumeFailed_FilenameLine[i], assumeFailed_TestNames[i]);
}
}
if (knownFailsPassing > 0)
{
fprintf(stdout, "- \e[31mKNOWN_FAILING_PASSED\e[0m: %d \e[31mPlease remove KNOWN_FAILING if these tests intentionally PASS\e[0m\n", knownFailsPassing);
fprintf(stdout, "\n \e[33mKNOWN_FAILING\e[0m tests \e[32mPASSING\e[0m:\n");
for (int i = 0; i < knownFailsPassing; i++)
{
if (i >= MAX_SUMMARY_TESTS_TO_LIST)
{
fprintf(stdout, " - \e[31mand %d more...\e[0m\n", knownFailsPassing - MAX_SUMMARY_TESTS_TO_LIST);
fprintf(stdout, " - \e[32mand %d more...\e[0m\n", knownFailsPassing - MAX_SUMMARY_TESTS_TO_LIST);
break;
}
fprintf(stdout, " - \e[31m%s\e[0m.\n", knownFailingPassedTestNames[i]);
fprintf(stdout, " - \e[32m%s\e[0m - %s.\n", knownFailingPassed_FilenameLine[i], knownFailingPassed_TestNames[i]);
}
}
fprintf(stdout, "- Tests \e[32mPASSED\e[0m: %d\n", passes);
if (knownFails > 0)
fprintf(stdout, "- Tests \e[33mKNOWN_FAILING\e[0m: %d\n", knownFails);
if (todos > 0)
fprintf(stdout, "- Tests \e[33mTO_DO\e[0m: %d\n", todos);
if (assumptionFails > 0)
fprintf(stdout, "- \e[33mASSUMPTIONS_FAILED\e[0m: %d\n", assumptionFails);
fprintf(stdout, "- Tests \e[34mTOTAL\e[0m: %d\n", results);
fprintf(stdout, "\n");
if (fails > 0)
fprintf(stdout, "- Tests \e[31mFAILED\e[0m : %d Add TESTS='X' to run tests with the defined prefix.\n", fails);
if (knownFails > 0)
fprintf(stdout, "- Tests \e[33mKNOWN_FAILING\e[0m: %d\n", knownFails);
if (assumptionFails > 0)
fprintf(stdout, "- \e[33mASSUMPTIONS_FAILED\e[0m: %d\n", assumptionFails);
if (todos > 0)
fprintf(stdout, "- Tests \e[33mTO_DO\e[0m: %d\n", todos);
if (knownFailsPassing > 0)
fprintf(stdout, "- \e[32mKNOWN_FAILING_PASSING\e[0m: %d \e[33mPlease remove KNOWN_FAILING if these tests intentionally PASS\e[0m\n", knownFailsPassing);
fprintf(stdout, "- Tests \e[32mPASSED\e[0m: %d\n", passes);
fprintf(stdout, "- Tests \e[34mTOTAL\e[0m: %d\n", results);
}
fprintf(stdout, "\n");