From a04046dbb6f4ee2deab66f9e90f0eb55f6d0094f Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Mon, 6 Mar 2023 20:35:08 +0100 Subject: [PATCH] Add tests for Defog and fix Defog battle strings (#2737) * start working on defog tests * defog tests + improve terrain end battlescripts * newline --- data/battle_scripts_1.s | 52 ++-- include/battle_scripts.h | 10 +- include/config/battle.h | 3 +- include/constants/battle_string_ids.h | 17 +- src/battle_message.c | 15 +- src/battle_script_commands.c | 70 +++-- src/battle_util.c | 53 ++-- test/move_effect_defog.c | 375 ++++++++++++++++++++++++++ 8 files changed, 500 insertions(+), 95 deletions(-) create mode 100644 test/move_effect_defog.c diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 9ea346cc6..0a3a4f264 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -1265,7 +1265,7 @@ BattleScript_EffectRemoveTerrain: resultmessage waitmessage B_WAIT_TIME_LONG removeterrain - jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, 4, BattleScript_MoveEnd + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_TERRAINENDS_COUNT, BattleScript_MoveEnd printfromtable gTerrainEndingStringIds waitmessage B_WAIT_TIME_LONG playanimation BS_ATTACKER, B_ANIM_RESTORE_BG @@ -6899,29 +6899,17 @@ BattleScript_MagicRoomEnds:: printstring STRINGID_MAGICROOMENDS waitmessage B_WAIT_TIME_LONG end2 - -BattleScript_ElectricTerrainEnds:: - printstring STRINGID_ELECTRICTERRAINENDS - waitmessage B_WAIT_TIME_LONG - playanimation BS_ATTACKER, B_ANIM_RESTORE_BG - end2 - -BattleScript_MistyTerrainEnds:: - printstring STRINGID_MISTYTERRAINENDS - waitmessage B_WAIT_TIME_LONG - playanimation BS_ATTACKER, B_ANIM_RESTORE_BG - end2 - -BattleScript_GrassyTerrainEnds:: - printstring STRINGID_GRASSYTERRAINENDS - waitmessage B_WAIT_TIME_LONG - playanimation BS_ATTACKER, B_ANIM_RESTORE_BG - end2 - -BattleScript_PsychicTerrainEnds:: - printstring STRINGID_PSYCHICTERRAINENDS + +BattleScript_GrassyTerrainEnds: + setbyte cMULTISTRING_CHOOSER, B_MSG_TERRAINENDS_GRASS +BattleScript_TerrainEnds_Ret:: + printfromtable gTerrainEndingStringIds waitmessage B_WAIT_TIME_LONG playanimation BS_ATTACKER, B_ANIM_RESTORE_BG + return + +BattleScript_TerrainEnds:: + call BattleScript_TerrainEnds_Ret end2 BattleScript_MudSportEnds:: @@ -7465,6 +7453,26 @@ BattleScript_StealthRockFree:: printstring STRINGID_PKMNBLEWAWAYSTEALTHROCK waitmessage B_WAIT_TIME_LONG return + +BattleScript_SpikesDefog:: + printstring STRINGID_SPIKESDISAPPEAREDFROMTEAM + waitmessage B_WAIT_TIME_LONG + return + +BattleScript_ToxicSpikesDefog:: + printstring STRINGID_TOXICSPIKESDISAPPEAREDFROMTEAM + waitmessage B_WAIT_TIME_LONG + return + +BattleScript_StickyWebDefog:: + printstring STRINGID_STICKYWEBDISAPPEAREDFROMTEAM + waitmessage B_WAIT_TIME_LONG + return + +BattleScript_StealthRockDefog:: + printstring STRINGID_STEALTHROCKDISAPPEAREDFROMTEAM + waitmessage B_WAIT_TIME_LONG + return BattleScript_MonTookFutureAttack:: printstring STRINGID_PKMNTOOKATTACK diff --git a/include/battle_scripts.h b/include/battle_scripts.h index f2821af05..6ffaa3278 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -246,10 +246,8 @@ extern const u8 BattleScript_TailwindEnds[]; extern const u8 BattleScript_TrickRoomEnds[]; extern const u8 BattleScript_WonderRoomEnds[]; extern const u8 BattleScript_MagicRoomEnds[]; -extern const u8 BattleScript_ElectricTerrainEnds[]; -extern const u8 BattleScript_MistyTerrainEnds[]; -extern const u8 BattleScript_GrassyTerrainEnds[]; -extern const u8 BattleScript_PsychicTerrainEnds[]; +extern const u8 BattleScript_TerrainEnds[]; +extern const u8 BattleScript_TerrainEnds_Ret[]; extern const u8 BattleScript_MudSportEnds[]; extern const u8 BattleScript_WaterSportEnds[]; extern const u8 BattleScript_SturdiedMsg[]; @@ -287,6 +285,10 @@ extern const u8 BattleScript_SelectingNotAllowedMoveHealBlockInPalace[]; extern const u8 BattleScript_ToxicSpikesFree[]; extern const u8 BattleScript_StickyWebFree[]; extern const u8 BattleScript_StealthRockFree[]; +extern const u8 BattleScript_SpikesDefog[]; +extern const u8 BattleScript_ToxicSpikesDefog[]; +extern const u8 BattleScript_StickyWebDefog[]; +extern const u8 BattleScript_StealthRockDefog[]; extern const u8 BattleScript_MegaEvolution[]; extern const u8 BattleScript_WishMegaEvolution[]; extern const u8 BattleScript_MoveEffectRecoilWithStatus[]; diff --git a/include/config/battle.h b/include/config/battle.h index 8fdf596cc..4a3d6382f 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -96,7 +96,8 @@ #define B_DARK_VOID_FAIL GEN_LATEST // In Gen7+, only Darkrai can use Dark Void. #define B_BURN_HIT_THAW GEN_LATEST // In Gen6+, damaging moves with a chance of burn will thaw the target, regardless if they're fire-type moves or not. #define B_HEALING_WISH_SWITCH GEN_LATEST // In Gen5+, the mon receiving Healing Wish is sent out at the end of the turn. - // Additionally, in gen8+ the Healing Wish's effect will be stored until the user switches into a statused or hurt mon + // Additionally, in gen8+ the Healing Wish's effect will be stored until the user switches into a statused or hurt mon. +#define B_DEFOG_CLEARS_TERRAIN GEN_LATEST // In Gen8+, Defog also clears active Terrain. #define B_STOCKPILE_RAISES_DEFS GEN_LATEST // In Gen4+, Stockpile also raises Defense and Sp.Defense stats. Once Spit Up / Swallow is used, these stat changes are lost. // Ability settings diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 1bd82023e..43e4ea574 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -641,10 +641,14 @@ #define STRINGID_PKMNSABILITYPREVENTSABILITY 639 #define STRINGID_PREPARESHELLTRAP 640 #define STRINGID_SHELLTRAPDIDNTWORK 641 -#define STRINGID_COULDNTFULLYPROTECT 642 -#define STRINGID_STOCKPILEDEFFECTWOREOFF 643 +#define STRINGID_SPIKESDISAPPEAREDFROMTEAM 642 +#define STRINGID_TOXICSPIKESDISAPPEAREDFROMTEAM 643 +#define STRINGID_STICKYWEBDISAPPEAREDFROMTEAM 644 +#define STRINGID_STEALTHROCKDISAPPEAREDFROMTEAM 645 +#define STRINGID_COULDNTFULLYPROTECT 646 +#define STRINGID_STOCKPILEDEFFECTWOREOFF 647 -#define BATTLESTRINGS_COUNT 644 +#define BATTLESTRINGS_COUNT 648 // This is the string id that gBattleStringsTable starts with. // String ids before this (e.g. STRINGID_INTROMSG) are not in the table, @@ -901,6 +905,13 @@ #define B_MSG_TERRAINPREVENTS_ELECTRIC 1 #define B_MSG_TERRAINPREVENTS_PSYCHIC 2 +// gTerrainEndingStringIds +#define B_MSG_TERRAINENDS_MISTY 0 +#define B_MSG_TERRAINENDS_ELECTRIC 1 +#define B_MSG_TERRAINENDS_PSYCHIC 2 +#define B_MSG_TERRAINENDS_GRASS 3 +#define B_MSG_TERRAINENDS_COUNT 4 + // gWrappedStringIds #define B_MSG_WRAPPED_BIND 0 #define B_MSG_WRAPPED_WRAP 1 diff --git a/src/battle_message.c b/src/battle_message.c index abf9bf705..cf57247bf 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -631,6 +631,10 @@ static const u8 sText_NotDoneYet[] = _("This move effect is not done yet!\p"); static const u8 sText_PkmnBlewAwayToxicSpikes[] = _("{B_ATK_NAME_WITH_PREFIX} blew away\nToxic Spikes!"); static const u8 sText_PkmnBlewAwayStickyWeb[] = _("{B_ATK_NAME_WITH_PREFIX} blew away\nSticky Web!"); static const u8 sText_PkmnBlewAwayStealthRock[] = _("{B_ATK_NAME_WITH_PREFIX} blew away\nStealth Rock!"); +static const u8 sText_SpikesDisappearedFromTeam[] = _("The spikes disappeared from\nthe ground around {B_ATK_TEAM2} team!"); +static const u8 sText_ToxicSpikesDisappearedFromTeam[] = _("The poison spikes disappeared from\nthe ground around {B_ATK_TEAM2} team!"); +static const u8 sText_StealthRockDisappearedFromTeam[] = _("The pointed stones disappeared\nfrom around {B_ATK_TEAM2} team!"); +static const u8 sText_StickyWebDisappearedFromTeam[] = _("The sticky web has disappeared from\nthe ground around {B_ATK_TEAM2} team!"); static const u8 sText_StickyWebUsed[] = _("A sticky web spreads out on the\nground around {B_DEF_TEAM2} team!"); static const u8 sText_QuashSuccess[] = _("The opposing {B_DEF_NAME_WITH_PREFIX}'s\nmove was postponed!"); static const u8 sText_IonDelugeOn[] = _("A deluge of ions showers\nthe battlefield!"); @@ -1369,6 +1373,10 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = [STRINGID_PKMNBLEWAWAYTOXICSPIKES - BATTLESTRINGS_TABLE_START] = sText_PkmnBlewAwayToxicSpikes, [STRINGID_PKMNBLEWAWAYSTICKYWEB - BATTLESTRINGS_TABLE_START] = sText_PkmnBlewAwayStickyWeb, [STRINGID_PKMNBLEWAWAYSTEALTHROCK - BATTLESTRINGS_TABLE_START] = sText_PkmnBlewAwayStealthRock, + [STRINGID_SPIKESDISAPPEAREDFROMTEAM - BATTLESTRINGS_TABLE_START] = sText_SpikesDisappearedFromTeam, + [STRINGID_TOXICSPIKESDISAPPEAREDFROMTEAM - BATTLESTRINGS_TABLE_START] = sText_ToxicSpikesDisappearedFromTeam, + [STRINGID_STEALTHROCKDISAPPEAREDFROMTEAM - BATTLESTRINGS_TABLE_START] = sText_StealthRockDisappearedFromTeam, + [STRINGID_STICKYWEBDISAPPEAREDFROMTEAM - BATTLESTRINGS_TABLE_START] = sText_StickyWebDisappearedFromTeam, [STRINGID_IONDELUGEON - BATTLESTRINGS_TABLE_START] = sText_IonDelugeOn, [STRINGID_TOPSYTURVYSWITCHEDSTATS - BATTLESTRINGS_TABLE_START] = sText_TopsyTurvySwitchedStats, [STRINGID_TERRAINBECOMESMISTY - BATTLESTRINGS_TABLE_START] = sText_TerrainBecomesMisty, @@ -1440,9 +1448,12 @@ const u16 gTerrainStringIds[] = STRINGID_TERRAINBECOMESMISTY, STRINGID_TERRAINBECOMESGRASSY, STRINGID_TERRAINBECOMESELECTRIC, STRINGID_TERRAINBECOMESPSYCHIC, STRINGID_TERRAINREMOVED, }; -const u16 gTerrainEndingStringIds[] = +const u16 gTerrainEndingStringIds[B_MSG_TERRAINENDS_COUNT] = { - STRINGID_MISTYTERRAINENDS, STRINGID_GRASSYTERRAINENDS, STRINGID_ELECTRICTERRAINENDS, STRINGID_PSYCHICTERRAINENDS + [B_MSG_TERRAINENDS_MISTY] = STRINGID_MISTYTERRAINENDS, + [B_MSG_TERRAINENDS_ELECTRIC] = STRINGID_ELECTRICTERRAINENDS, + [B_MSG_TERRAINENDS_PSYCHIC] = STRINGID_PSYCHICTERRAINENDS, + [B_MSG_TERRAINENDS_GRASS] = STRINGID_GRASSYTERRAINENDS, }; const u16 gTerrainPreventsStringIds[] = diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 1ea353f3c..a5b7656bf 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -8412,6 +8412,30 @@ bool32 CanUseLastResort(u8 battlerId) return (knownMovesCount >= 2 && usedMovesCount >= knownMovesCount - 1); } +static void RemoveAllTerrains(void) +{ + gFieldTimers.terrainTimer = 0; + switch (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY) + { + case STATUS_FIELD_MISTY_TERRAIN: + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAINENDS_MISTY; + break; + case STATUS_FIELD_GRASSY_TERRAIN: + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAINENDS_GRASS; + break; + case STATUS_FIELD_ELECTRIC_TERRAIN: + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAINENDS_ELECTRIC; + break; + case STATUS_FIELD_PSYCHIC_TERRAIN: + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAINENDS_PSYCHIC; + break; + default: + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAINENDS_COUNT; // failsafe + break; + } + gFieldStatuses &= ~STATUS_FIELD_TERRAIN_ANY; // remove the terrain +} + #define DEFOG_CLEAR(status, structField, battlescript, move)\ { \ if (*sideStatuses & status) \ @@ -8429,7 +8453,7 @@ bool32 CanUseLastResort(u8 battlerId) } \ } -static bool32 ClearDefogHazards(u8 battlerAtk, bool32 clear) +static bool32 TryDefogClear(u8 battlerAtk, bool32 clear) { s32 i; for (i = 0; i < 2; i++) @@ -8437,7 +8461,7 @@ static bool32 ClearDefogHazards(u8 battlerAtk, bool32 clear) struct SideTimer *sideTimer = &gSideTimers[i]; u32 *sideStatuses = &gSideStatuses[i]; - gBattlerAttacker = i; + gBattlerAttacker = i; // For correct battle string. Ally's / Foe's if (GetBattlerSide(battlerAtk) != i) { DEFOG_CLEAR(SIDE_STATUS_REFLECT, reflectTimer, BattleScript_SideStatusWoreOffReturn, MOVE_REFLECT); @@ -8446,10 +8470,19 @@ static bool32 ClearDefogHazards(u8 battlerAtk, bool32 clear) DEFOG_CLEAR(SIDE_STATUS_AURORA_VEIL, auroraVeilTimer, BattleScript_SideStatusWoreOffReturn, MOVE_AURORA_VEIL); DEFOG_CLEAR(SIDE_STATUS_SAFEGUARD, safeguardTimer, BattleScript_SideStatusWoreOffReturn, MOVE_SAFEGUARD); } - DEFOG_CLEAR(SIDE_STATUS_SPIKES, spikesAmount, BattleScript_SpikesFree, 0); - DEFOG_CLEAR(SIDE_STATUS_STEALTH_ROCK, stealthRockAmount, BattleScript_StealthRockFree, 0); - DEFOG_CLEAR(SIDE_STATUS_TOXIC_SPIKES, toxicSpikesAmount, BattleScript_ToxicSpikesFree, 0); - DEFOG_CLEAR(SIDE_STATUS_STICKY_WEB, stickyWebAmount, BattleScript_StickyWebFree, 0); + DEFOG_CLEAR(SIDE_STATUS_SPIKES, spikesAmount, BattleScript_SpikesDefog, 0); + DEFOG_CLEAR(SIDE_STATUS_STEALTH_ROCK, stealthRockAmount, BattleScript_StealthRockDefog, 0); + DEFOG_CLEAR(SIDE_STATUS_TOXIC_SPIKES, toxicSpikesAmount, BattleScript_ToxicSpikesDefog, 0); + DEFOG_CLEAR(SIDE_STATUS_STICKY_WEB, stickyWebAmount, BattleScript_StickyWebDefog, 0); + #if B_DEFOG_CLEARS_TERRAIN >= GEN_8 + if (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY) + { + RemoveAllTerrains(); + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_TerrainEnds_Ret; + return TRUE; + } + #endif // B_DEFOG_CLEARS_TERRAIN } return FALSE; @@ -9992,14 +10025,14 @@ static void Cmd_various(void) VARIOUS_ARGS(bool8 clear, const u8 *failInstr); if (cmd->clear) // Clear { - if (ClearDefogHazards(gEffectBattler, TRUE)) + if (TryDefogClear(gEffectBattler, TRUE)) return; else gBattlescriptCurrInstr = cmd->nextInstr; } else { - if (ClearDefogHazards(gActiveBattler, FALSE)) + if (TryDefogClear(gActiveBattler, FALSE)) gBattlescriptCurrInstr = cmd->nextInstr; else gBattlescriptCurrInstr = cmd->failInstr; @@ -10424,26 +10457,7 @@ static void Cmd_various(void) case VARIOUS_REMOVE_TERRAIN: { VARIOUS_ARGS(); - gFieldTimers.terrainTimer = 0; - switch (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY) - { - case STATUS_FIELD_MISTY_TERRAIN: - gBattleCommunication[MULTISTRING_CHOOSER] = 0; - break; - case STATUS_FIELD_GRASSY_TERRAIN: - gBattleCommunication[MULTISTRING_CHOOSER] = 1; - break; - case STATUS_FIELD_ELECTRIC_TERRAIN: - gBattleCommunication[MULTISTRING_CHOOSER] = 2; - break; - case STATUS_FIELD_PSYCHIC_TERRAIN: - gBattleCommunication[MULTISTRING_CHOOSER] = 3; - break; - default: - gBattleCommunication[MULTISTRING_CHOOSER] = 4; // failsafe - break; - } - gFieldStatuses &= ~STATUS_FIELD_TERRAIN_ANY; // remove the terrain + RemoveAllTerrains(); break; } case VARIOUS_JUMP_IF_UNDER_200: diff --git a/src/battle_util.c b/src/battle_util.c index d7f3a767b..e82f6c87f 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -2126,6 +2126,20 @@ enum ENDTURN_FIELD_COUNT, }; +static bool32 TryEndTerrain(u32 terrainFlag, u32 stringTableId) +{ + if (gFieldStatuses & terrainFlag + && (!(gFieldStatuses & STATUS_FIELD_TERRAIN_PERMANENT) && --gFieldTimers.terrainTimer == 0)) + { + gFieldStatuses &= ~terrainFlag; + TryToRevertMimicry(); + gBattleCommunication[MULTISTRING_CHOOSER] = stringTableId; + BattleScriptExecute(BattleScript_TerrainEnds); + return TRUE; + } + return FALSE; +} + u8 DoFieldEndTurnEffects(void) { u8 effect = 0; @@ -2477,50 +2491,19 @@ u8 DoFieldEndTurnEffects(void) gBattleStruct->turnCountersTracker++; break; case ENDTURN_ELECTRIC_TERRAIN: - if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN - && (!(gFieldStatuses & STATUS_FIELD_TERRAIN_PERMANENT) && --gFieldTimers.terrainTimer == 0)) - { - gFieldStatuses &= ~(STATUS_FIELD_ELECTRIC_TERRAIN | STATUS_FIELD_TERRAIN_PERMANENT); - TryToRevertMimicry(); - BattleScriptExecute(BattleScript_ElectricTerrainEnds); - effect++; - } + effect = TryEndTerrain(STATUS_FIELD_ELECTRIC_TERRAIN, B_MSG_TERRAINENDS_ELECTRIC); gBattleStruct->turnCountersTracker++; break; case ENDTURN_MISTY_TERRAIN: - if (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN - && (!(gFieldStatuses & STATUS_FIELD_TERRAIN_PERMANENT) && --gFieldTimers.terrainTimer == 0)) - { - gFieldStatuses &= ~STATUS_FIELD_MISTY_TERRAIN; - TryToRevertMimicry(); - BattleScriptExecute(BattleScript_MistyTerrainEnds); - effect++; - } + effect = TryEndTerrain(STATUS_FIELD_MISTY_TERRAIN, B_MSG_TERRAINENDS_MISTY); gBattleStruct->turnCountersTracker++; break; case ENDTURN_GRASSY_TERRAIN: - if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN) - { - if (!(gFieldStatuses & STATUS_FIELD_TERRAIN_PERMANENT) - && (gFieldTimers.terrainTimer == 0 || --gFieldTimers.terrainTimer == 0)) - { - gFieldStatuses &= ~STATUS_FIELD_GRASSY_TERRAIN; - TryToRevertMimicry(); - } - BattleScriptExecute(BattleScript_GrassyTerrainHeals); - effect++; - } + effect = TryEndTerrain(STATUS_FIELD_GRASSY_TERRAIN, B_MSG_TERRAINENDS_GRASS); gBattleStruct->turnCountersTracker++; break; case ENDTURN_PSYCHIC_TERRAIN: - if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN - && (!(gFieldStatuses & STATUS_FIELD_TERRAIN_PERMANENT) && --gFieldTimers.terrainTimer == 0)) - { - gFieldStatuses &= ~STATUS_FIELD_PSYCHIC_TERRAIN; - TryToRevertMimicry(); - BattleScriptExecute(BattleScript_PsychicTerrainEnds); - effect++; - } + effect = TryEndTerrain(STATUS_FIELD_PSYCHIC_TERRAIN, B_MSG_TERRAINENDS_PSYCHIC); gBattleStruct->turnCountersTracker++; break; case ENDTURN_WATER_SPORT: diff --git a/test/move_effect_defog.c b/test/move_effect_defog.c new file mode 100644 index 000000000..d877899bb --- /dev/null +++ b/test/move_effect_defog.c @@ -0,0 +1,375 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_DEFOG].effect == EFFECT_DEFOG); + ASSUME(gBattleMoves[MOVE_REFLECT].effect == EFFECT_REFLECT); + ASSUME(gBattleMoves[MOVE_LIGHT_SCREEN].effect == EFFECT_LIGHT_SCREEN); + ASSUME(gBattleMoves[MOVE_MIST].effect == EFFECT_MIST); + ASSUME(gBattleMoves[MOVE_SAFEGUARD].effect == EFFECT_SAFEGUARD); + ASSUME(gBattleMoves[MOVE_AURORA_VEIL].effect == EFFECT_AURORA_VEIL); + ASSUME(gBattleMoves[MOVE_STEALTH_ROCK].effect == EFFECT_STEALTH_ROCK); + ASSUME(gBattleMoves[MOVE_SPIKES].effect == EFFECT_SPIKES); + ASSUME(gBattleMoves[MOVE_TOXIC_SPIKES].effect == EFFECT_TOXIC_SPIKES); + ASSUME(gBattleMoves[MOVE_STICKY_WEB].effect == EFFECT_STICKY_WEB); + ASSUME(gBattleMoves[MOVE_TOXIC].effect == EFFECT_TOXIC); + ASSUME(gBattleMoves[MOVE_SCREECH].effect == EFFECT_DEFENSE_DOWN_2); + ASSUME(gBattleMoves[MOVE_TACKLE].split == SPLIT_PHYSICAL); + ASSUME(gBattleMoves[MOVE_GUST].split == SPLIT_SPECIAL); +} + +SINGLE_BATTLE_TEST("Defog lowers evasiveness by 1") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_DEFOG); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DEFOG, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Wobbuffet's evasiveness fell!"); + } +} + +SINGLE_BATTLE_TEST("Defog does not lower evasiveness if target behind Substitute") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) {Speed(4); } + OPPONENT(SPECIES_WOBBUFFET) {Speed(5); } + } WHEN { + TURN { MOVE(opponent, MOVE_SUBSTITUTE); MOVE(player, MOVE_DEFOG); } + } SCENE { + MESSAGE("Foe Wobbuffet used Substitute!"); + MESSAGE("But it failed!"); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DEFOG, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Wobbuffet's evasiveness fell!"); + } + } +} + +DOUBLE_BATTLE_TEST("Defog lowers evasiveness by 1 and removes Reflect and Light Screen from opponent's side", s16 damagePhysical, s16 damageSpecial) +{ + u16 move; + + PARAMETRIZE { move = MOVE_DEFOG; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) {Speed(4); } + PLAYER(SPECIES_WOBBUFFET) {Speed(3); } + OPPONENT(SPECIES_WOBBUFFET) {Speed(2); } + OPPONENT(SPECIES_WOBBUFFET) {Speed(1); } + } WHEN { + TURN { MOVE(opponentLeft, MOVE_REFLECT); MOVE(opponentRight, MOVE_LIGHT_SCREEN); } + TURN { MOVE(playerLeft, move, target:opponentLeft); } + TURN { MOVE(playerLeft, MOVE_TACKLE, target:opponentLeft); MOVE(playerRight, MOVE_GUST, target:opponentRight); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_REFLECT, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_LIGHT_SCREEN, opponentRight); + ANIMATION(ANIM_TYPE_MOVE, move, playerLeft); + if (move == MOVE_DEFOG) { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); + MESSAGE("Foe Wobbuffet's evasiveness fell!"); + MESSAGE("Foe's Reflect wore off!"); + MESSAGE("Foe's Light Screen wore off!"); + } + MESSAGE("Wobbuffet used Tackle!"); + HP_BAR(opponentLeft, captureDamage: &results[i].damagePhysical); + MESSAGE("Wobbuffet used Gust!"); + HP_BAR(opponentRight, captureDamage: &results[i].damageSpecial); + } FINALLY { + EXPECT_MUL_EQ(results[1].damagePhysical, Q_4_12(1.5), results[0].damagePhysical); + EXPECT_MUL_EQ(results[1].damageSpecial, Q_4_12(1.5), results[0].damageSpecial); + } +} + +DOUBLE_BATTLE_TEST("Defog lowers evasiveness by 1 and removes Mist and Safeguard from opponent's side") +{ + u16 move; + + PARAMETRIZE { move = MOVE_DEFOG; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) {Speed(4); } + PLAYER(SPECIES_WOBBUFFET) {Speed(3); } + OPPONENT(SPECIES_WOBBUFFET) {Speed(2); } + OPPONENT(SPECIES_WOBBUFFET) {Speed(1); } + } WHEN { + TURN { MOVE(opponentLeft, MOVE_MIST); MOVE(opponentRight, MOVE_SAFEGUARD); } + TURN { MOVE(playerLeft, move, target:opponentLeft); } + TURN { MOVE(playerLeft, MOVE_SCREECH, target:opponentLeft); MOVE(playerRight, MOVE_TOXIC, target:opponentRight); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_MIST, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SAFEGUARD, opponentRight); + if (move == MOVE_DEFOG) { + MESSAGE("Foe Wobbuffet is protected by MIST!"); + ANIMATION(ANIM_TYPE_MOVE, move, playerLeft); + MESSAGE("Foe's Mist wore off!"); + MESSAGE("Foe's Safeguard wore off!"); + } + MESSAGE("Wobbuffet used Screech!"); + if (move == MOVE_DEFOG) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCREECH, playerLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); + } + else { + MESSAGE("Foe Wobbuffet is protected by MIST!"); + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); + } + MESSAGE("Wobbuffet used Toxic!"); + if (move == MOVE_DEFOG) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, playerRight); + STATUS_ICON(opponentRight, badPoison: TRUE); + } + else { + MESSAGE("Foe Wobbuffet's party is protected by SAFEGUARD!"); + NOT STATUS_ICON(opponentRight, badPoison: TRUE); + } + } +} + +DOUBLE_BATTLE_TEST("Defog lowers evasiveness by 1 and removes Stealth Rock and Sticky Web from player's side") +{ + u16 move; + + PARAMETRIZE { move = MOVE_DEFOG; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) {Speed(4); } + PLAYER(SPECIES_WOBBUFFET) {Speed(3); } + PLAYER(SPECIES_WOBBUFFET) {Speed(3); } + OPPONENT(SPECIES_WOBBUFFET) {Speed(2); } + OPPONENT(SPECIES_WOBBUFFET) {Speed(1); } + } WHEN { + TURN { MOVE(opponentLeft, MOVE_STEALTH_ROCK); MOVE(opponentRight, MOVE_STICKY_WEB); } + TURN { MOVE(playerLeft, move, target:opponentLeft); } + TURN { SWITCH(playerLeft, 2); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STEALTH_ROCK, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, opponentRight); + ANIMATION(ANIM_TYPE_MOVE, move, playerLeft); + if (move == MOVE_DEFOG) { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); + MESSAGE("Foe Wobbuffet's evasiveness fell!"); + MESSAGE("The pointed stones disappeared from around your team!"); + MESSAGE("The sticky web has disappeared from the ground around your team!"); + } + // Switch happens + MESSAGE("Wobbuffet, that's enough! Come back!"); + MESSAGE("Go! Wobbuffet!"); + if (move != MOVE_DEFOG) { + HP_BAR(playerLeft); + MESSAGE("Pointed stones dug into Wobbuffet!"); + MESSAGE("Wobbuffet was caught in a Sticky Web!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + MESSAGE("Wobbuffet's speed fell!"); + } + else { + NONE_OF { + HP_BAR(playerLeft); + MESSAGE("Pointed stones dug into Wobbuffet!"); + MESSAGE("Wobbuffet was caught in a Sticky Web!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + MESSAGE("Wobbuffet's speed fell!"); + } + } + } +} + +SINGLE_BATTLE_TEST("Defog lowers evasiveness by 1 and removes Spikes from player's side") +{ + u16 move; + + PARAMETRIZE { move = MOVE_DEFOG; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) {Speed(2); } + PLAYER(SPECIES_WOBBUFFET) {Speed(2); } + OPPONENT(SPECIES_WOBBUFFET) {Speed(5); } + } WHEN { + TURN { MOVE(opponent, MOVE_SPIKES); MOVE(player, move); } + TURN { SWITCH(player, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SPIKES, opponent); + ANIMATION(ANIM_TYPE_MOVE, move, player); + if (move == MOVE_DEFOG) { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Wobbuffet's evasiveness fell!"); + MESSAGE("The spikes disappeared from the ground around your team!"); + } + // Switch happens + MESSAGE("Wobbuffet, that's enough! Come back!"); + MESSAGE("Go! Wobbuffet!"); + if (move != MOVE_DEFOG) { + HP_BAR(player); + MESSAGE("Wobbuffet is hurt by spikes!"); + } + else { + NONE_OF { + HP_BAR(player); + MESSAGE("Wobbuffet is hurt by spikes!"); + } + } + } +} + +SINGLE_BATTLE_TEST("Defog lowers evasiveness by 1 and removes terrain") +{ + u16 move; + + PARAMETRIZE { move = MOVE_PSYCHIC_TERRAIN; } + PARAMETRIZE { move = MOVE_ELECTRIC_TERRAIN; } + PARAMETRIZE { move = MOVE_MISTY_TERRAIN; } + PARAMETRIZE { move = MOVE_GRASSY_TERRAIN; } + GIVEN { + ASSUME(B_DEFOG_CLEARS_TERRAIN >= GEN_8); + PLAYER(SPECIES_WOBBUFFET) {Speed(50); } + OPPONENT(SPECIES_WOBBUFFET) {Speed(5); } + } WHEN { + TURN { MOVE(player, move); MOVE(opponent, MOVE_DEFOG); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DEFOG, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's evasiveness fell!"); + if (move == MOVE_PSYCHIC_TERRAIN) { + MESSAGE("The weirdness disappeared from the battlefield."); + } + else if (move == MOVE_ELECTRIC_TERRAIN) { + MESSAGE("The electricity disappeared from the battlefield."); + } + else if (move == MOVE_MISTY_TERRAIN) { + MESSAGE("The mist disappeared from the battlefield."); + } + else if (move == MOVE_GRASSY_TERRAIN) { + MESSAGE("The grass disappeared from the battlefield."); + } + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_RESTORE_BG, player); + } +} + +SINGLE_BATTLE_TEST("Defog lowers evasiveness by 1 and removes Toxic Spikes from opponent's side") +{ + u16 move; + + PARAMETRIZE { move = MOVE_DEFOG; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) {Speed(5); } + OPPONENT(SPECIES_WOBBUFFET) {Speed(2); } + OPPONENT(SPECIES_WOBBUFFET) {Speed(2); } + } WHEN { + TURN { MOVE(player, MOVE_TOXIC_SPIKES); MOVE(opponent, move); } + TURN { SWITCH(opponent, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC_SPIKES, player); + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + if (move == MOVE_DEFOG) { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's evasiveness fell!"); + MESSAGE("The poison spikes disappeared from the ground around the opposing team!"); + } + // Switch happens + MESSAGE("2 sent out Wobbuffet!"); + if (move != MOVE_DEFOG) { + MESSAGE("Foe Wobbuffet was poisoned!"); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + STATUS_ICON(opponent, poison: TRUE); + } + else { + NONE_OF { + MESSAGE("Foe Wobbuffet was poisoned!"); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + STATUS_ICON(opponent, poison: TRUE); + } + } + } +} + +DOUBLE_BATTLE_TEST("Defog lowers evasiveness by 1 and removes Aurora Veil from player's side", s16 damagePhysical, s16 damageSpecial) +{ + u16 move; + + PARAMETRIZE { move = MOVE_DEFOG; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + GIVEN { + ASSUME(gBattleMoves[MOVE_HAIL].effect == EFFECT_HAIL); + ASSUME(gSpeciesInfo[SPECIES_GLALIE].types[0] == TYPE_ICE); + PLAYER(SPECIES_GLALIE) {Speed(4); } + PLAYER(SPECIES_GLALIE) {Speed(3); } + OPPONENT(SPECIES_GLALIE) {Speed(2); } + OPPONENT(SPECIES_GLALIE) {Speed(1); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_HAIL); MOVE(playerRight, MOVE_AURORA_VEIL); } + TURN { MOVE(opponentLeft, move, target:playerLeft); } + TURN { MOVE(opponentLeft, MOVE_TACKLE, target:playerLeft); MOVE(opponentRight, MOVE_GUST, target:playerRight); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_HAIL, playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_AURORA_VEIL, playerRight); + ANIMATION(ANIM_TYPE_MOVE, move, opponentLeft); + if (move == MOVE_DEFOG) { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + MESSAGE("Glalie's evasiveness fell!"); + MESSAGE("Ally's Aurora Veil wore off!"); + } + MESSAGE("Foe Glalie used Tackle!"); + HP_BAR(playerLeft, captureDamage: &results[i].damagePhysical); + MESSAGE("Foe Glalie used Gust!"); + HP_BAR(playerRight, captureDamage: &results[i].damageSpecial); + } FINALLY { + EXPECT_MUL_EQ(results[1].damagePhysical, Q_4_12(1.5), results[0].damagePhysical); + EXPECT_MUL_EQ(results[1].damageSpecial, Q_4_12(1.5), results[0].damageSpecial); + } +} + +DOUBLE_BATTLE_TEST("Defog lowers evasiveness by 1 and removes everything it can") +{ + bool32 defogTurn = FALSE; + GIVEN { + ASSUME(gBattleMoves[MOVE_HAIL].effect == EFFECT_HAIL); + ASSUME(gSpeciesInfo[SPECIES_GLALIE].types[0] == TYPE_ICE); + PLAYER(SPECIES_GLALIE) {Speed(4); } + PLAYER(SPECIES_GLALIE) {Speed(3); } + PLAYER(SPECIES_GLALIE) {Speed(12); } + PLAYER(SPECIES_GLALIE) {Speed(3); } + OPPONENT(SPECIES_GLALIE) {Speed(2); } + OPPONENT(SPECIES_GLALIE) {Speed(1); } + OPPONENT(SPECIES_GLALIE) {Speed(1); } + OPPONENT(SPECIES_GLALIE) {Speed(1); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_STICKY_WEB); MOVE(playerRight, MOVE_SPIKES); MOVE(opponentLeft, MOVE_STICKY_WEB); MOVE(opponentRight, MOVE_SPIKES); } + TURN { SWITCH(playerLeft, 2); SWITCH(playerRight, 3); SWITCH(opponentLeft, 2); SWITCH(opponentRight, 3);} + TURN { MOVE(playerLeft, MOVE_TOXIC_SPIKES); MOVE(playerRight, MOVE_STEALTH_ROCK); MOVE(opponentLeft, MOVE_TOXIC_SPIKES); MOVE(opponentRight, MOVE_STEALTH_ROCK); } + TURN { MOVE(playerLeft, MOVE_HAIL); MOVE(playerRight, MOVE_AURORA_VEIL); MOVE(opponentLeft, MOVE_AURORA_VEIL); MOVE(opponentRight, MOVE_STEALTH_ROCK); } + TURN { MOVE(playerLeft, MOVE_REFLECT); MOVE(playerRight, MOVE_LIGHT_SCREEN); MOVE(opponentLeft, MOVE_REFLECT); MOVE(opponentRight, MOVE_LIGHT_SCREEN); } + TURN { MOVE(playerLeft, MOVE_MIST); MOVE(playerRight, MOVE_SAFEGUARD); MOVE(opponentLeft, MOVE_MIST); MOVE(opponentRight, MOVE_SAFEGUARD); } + TURN { defogTurn = TRUE ; MOVE(opponentRight, MOVE_DEFOG, target:playerLeft);} + } SCENE { + if (defogTurn == TRUE) + { + MESSAGE("Foe Glalie used Defog!"); + MESSAGE("Glalie is protected by MIST!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DEFOG, opponentRight); + // Player side + MESSAGE("Ally's Reflect wore off!"); + MESSAGE("Ally's Light Screen wore off!"); + MESSAGE("Ally's Mist wore off!"); + MESSAGE("Ally's Aurora Veil wore off!"); + MESSAGE("Ally's Safeguard wore off!"); + + MESSAGE("The spikes disappeared from the ground around your team!"); + MESSAGE("The pointed stones disappeared from around your team!"); + MESSAGE("The poison spikes disappeared from the ground around your team!"); + MESSAGE("The sticky web has disappeared from the ground around your team!"); + + // Opponent side + MESSAGE("The spikes disappeared from the ground around the opposing team!"); + MESSAGE("The pointed stones disappeared from around the opposing team!"); + MESSAGE("The poison spikes disappeared from the ground around the opposing team!"); + MESSAGE("The sticky web has disappeared from the ground around the opposing team!"); + } + } +}