From f3d43e286df7f9b837597fabecba408daacfd5e4 Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Thu, 4 Dec 2025 15:24:53 +0100 Subject: [PATCH] Fix substitute/defog interactions (#8418) --- data/battle_scripts_1.s | 7 +++ include/config/battle.h | 2 +- src/battle_gfx_sfx_util.c | 1 + src/battle_script_commands.c | 14 ++++-- test/battle/move_effect/defog.c | 83 ++++++++++++++++++++++++++++++--- 5 files changed, 96 insertions(+), 11 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 85cd9cbef4..6eedabb64e 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -1493,12 +1493,17 @@ BattleScript_EffectHitEnemyHealAlly:: BattleScript_EffectDefog:: setstatchanger STAT_EVASION, 1, TRUE attackcanceler + jumpifgenconfiglowerthan CONFIG_DEFOG_EFFECT_CLEARING, GEN_5, BattleScript_DefogAfterSubstituteCheck jumpifsubstituteblocks BattleScript_DefogIfCanClearHazards +BattleScript_DefogAfterSubstituteCheck: jumpifstat BS_TARGET, CMP_NOT_EQUAL, STAT_EVASION, MIN_STAT_STAGE, BattleScript_DefogWorks BattleScript_DefogIfCanClearHazards: trydefog FALSE, BattleScript_ButItFailed BattleScript_DefogWorks: accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE + jumpifgenconfiglowerthan CONFIG_DEFOG_EFFECT_CLEARING, GEN_5, BattleScript_DefogWorksAfterSubstituteCheck + jumpifsubstituteblocks BattleScript_DefogTryHazardsWithAnim +BattleScript_DefogWorksAfterSubstituteCheck: statbuffchange BS_TARGET, STAT_CHANGE_ALLOW_PTR | STAT_CHANGE_ONLY_CHECKING, BattleScript_DefogTryHazardsWithAnim jumpifbyte CMP_LESS_THAN, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_CHANGE, BattleScript_DefogDoAnim jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_CHANGE_EMPTY, BattleScript_DefogTryHazardsWithAnim @@ -1508,7 +1513,9 @@ BattleScript_DefogWorks: BattleScript_DefogDoAnim:: attackanimation waitanimation + call BattleScript_SwapFromSubstitute statbuffchange BS_TARGET, STAT_CHANGE_ALLOW_PTR, BattleScript_DefogTryHazards + call BattleScript_SwapToSubstitute BattleScript_DefogPrintString:: printfromtable gStatDownStringIds waitmessage B_WAIT_TIME_LONG diff --git a/include/config/battle.h b/include/config/battle.h index 3a702ef225..c446ed99be 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -111,7 +111,7 @@ #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. -#define B_DEFOG_EFFECT_CLEARING GEN_LATEST // In Gen6+, Defog clears Spikes, Toxic Spikes, Stealth Rock and Sticky Web from both sides. In Gen8+, Defog also clears active Terrain. +#define B_DEFOG_EFFECT_CLEARING GEN_LATEST // In Gen5+, Defog does not lower Evasion of target behind Subsitute. In Gen6+, Defog clears Spikes, Toxic Spikes, Stealth Rock and Sticky Web from both sides. 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. #define B_TRANSFORM_SHINY GEN_LATEST // In Gen4+, Transform will copy the shiny state of the opponent instead of maintaining its own shiny state. #define B_TRANSFORM_FORM_CHANGES GEN_LATEST // In Gen5+, Transformed Pokemon cannot change forms. diff --git a/src/battle_gfx_sfx_util.c b/src/battle_gfx_sfx_util.c index 5a30edcdef..d749b427fd 100644 --- a/src/battle_gfx_sfx_util.c +++ b/src/battle_gfx_sfx_util.c @@ -557,6 +557,7 @@ static bool8 ShouldAnimBeDoneRegardlessOfSubstitute(u8 animId) case B_ANIM_SNOW_CONTINUES: case B_ANIM_FOG_CONTINUES: case B_ANIM_SNATCH_MOVE: + case B_ANIM_STATS_CHANGE: return TRUE; default: return FALSE; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 7de9159b6d..54162af52a 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -9392,14 +9392,20 @@ static bool32 TryDefogClear(u32 battlerAtk, bool32 clear) } if (gBattleWeather & B_WEATHER_FOG) { - gBattleWeather &= ~B_WEATHER_FOG; - BattleScriptCall(BattleScript_FogEnded_Ret); + if (clear) + { + gBattleWeather &= ~B_WEATHER_FOG; + BattleScriptCall(BattleScript_FogEnded_Ret); + } return TRUE; } if (GetConfig(CONFIG_DEFOG_EFFECT_CLEARING) >= GEN_8 && (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY)) { - RemoveAllTerrains(); - BattleScriptCall(BattleScript_TerrainEnds_Ret); + if (clear) + { + RemoveAllTerrains(); + BattleScriptCall(BattleScript_TerrainEnds_Ret); + } return TRUE; } } diff --git a/test/battle/move_effect/defog.c b/test/battle/move_effect/defog.c index f02e39f354..286a9c00bb 100644 --- a/test/battle/move_effect/defog.c +++ b/test/battle/move_effect/defog.c @@ -34,20 +34,92 @@ SINGLE_BATTLE_TEST("Defog lowers evasiveness by 1 stage") } } -SINGLE_BATTLE_TEST("Defog does not lower evasiveness if target behind Substitute") +SINGLE_BATTLE_TEST("Defog fails if target has minimum evasion stat change") { GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) {Ability(ABILITY_SIMPLE);}; + } WHEN { + TURN { MOVE(player, MOVE_DEFOG); } + TURN { MOVE(player, MOVE_DEFOG); } + TURN { MOVE(player, MOVE_DEFOG); } + TURN { MOVE(player, MOVE_DEFOG); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DEFOG, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("The opposing Wobbuffet's evasiveness harshly fell!"); + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("Defog lowers evasiveness of target behind Substitute (Gen4-)") +{ + GIVEN { + WITH_CONFIG(CONFIG_DEFOG_EFFECT_CLEARING, GEN_4); PLAYER(SPECIES_WOBBUFFET) { Speed(4); } OPPONENT(SPECIES_WOBBUFFET) { Speed(5); } } WHEN { TURN { MOVE(opponent, MOVE_SUBSTITUTE); MOVE(player, MOVE_DEFOG); } } SCENE { MESSAGE("The opposing Wobbuffet used Substitute!"); + NOT MESSAGE("But it failed!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DEFOG, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("The opposing Wobbuffet's evasiveness fell!"); + } +} + +SINGLE_BATTLE_TEST("Defog fails if target has minimum evasion stat change behind Substitute (Gen4-)") +{ + GIVEN { + WITH_CONFIG(CONFIG_DEFOG_EFFECT_CLEARING, GEN_4); + PLAYER(SPECIES_WOBBUFFET) { Speed(4); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(5); Ability(ABILITY_SIMPLE);} + } WHEN { + TURN { MOVE(opponent, MOVE_SUBSTITUTE); MOVE(player, MOVE_DEFOG); } + TURN { MOVE(player, MOVE_DEFOG); } + TURN { MOVE(player, MOVE_DEFOG); } + TURN { MOVE(player, MOVE_DEFOG); } + } SCENE { + MESSAGE("The opposing Wobbuffet used Substitute!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DEFOG, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("The opposing Wobbuffet's evasiveness harshly fell!"); MESSAGE("But it failed!"); - NONE_OF { - ANIMATION(ANIM_TYPE_MOVE, MOVE_DEFOG, player); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); - MESSAGE("The opposing Wobbuffet's evasiveness fell!"); + } +} + +SINGLE_BATTLE_TEST("Defog does not lower evasiveness if target behind Substitute (Gen5+)") +{ + u32 move; + + PARAMETRIZE { move = MOVE_LIGHT_SCREEN; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + + GIVEN { + ASSUME(CONFIG_DEFOG_EFFECT_CLEARING >= GEN_5); + PLAYER(SPECIES_WOBBUFFET) { Speed(4); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(5); } + } WHEN { + TURN { MOVE(opponent, move); } + TURN { MOVE(opponent, MOVE_SUBSTITUTE); MOVE(player, MOVE_DEFOG); } + } SCENE { + MESSAGE("The opposing Wobbuffet used Substitute!"); + if (move == MOVE_CELEBRATE) + { + MESSAGE("But it failed!"); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DEFOG, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("The opposing Wobbuffet's evasiveness fell!"); + } + } + else + { + NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("The opposing Wobbuffet's evasiveness fell!"); + } } } } @@ -423,7 +495,6 @@ SINGLE_BATTLE_TEST("Defog is used on the correct side if opposing mon is behind ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, opponent); MESSAGE("Wobbuffet used Defog!"); ANIMATION(ANIM_TYPE_MOVE, MOVE_DEFOG, player); - MESSAGE("The opposing Wobbuffet's evasiveness fell!"); MESSAGE("The opposing team's Light Screen wore off!"); } }