Fix Transform fail conditions with gen-specific checks (#9070)

This commit is contained in:
GGbond 2026-02-10 18:59:09 +08:00 committed by GitHub
parent 20a986519d
commit 57c51c0702
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 146 additions and 7 deletions

View File

@ -115,6 +115,10 @@
// 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 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_SEMI_INV_FAIL GEN_LATEST // In Gen2+, Transform fails if the target is semi-invulnerable.
#define B_TRANSFORM_TARGET_FAIL GEN_LATEST // In Gen2+, Transform fails if the target is already transformed.
#define B_TRANSFORM_USER_FAIL GEN_LATEST // In Gen5+, Transform fails if the user is already transformed.
#define B_TRANSFORM_SUBSTITUTE_FAIL GEN_LATEST // In Gen5+, Transform fails if the target is behind a Substitute.
#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.
#define B_WIDE_GUARD GEN_LATEST // In Gen5 only, Wide Guard has a chance to fail if used consecutively.

View File

@ -106,8 +106,12 @@
F(B_HEALING_WISH_SWITCH, healingWishSwitch, (u32, GEN_COUNT - 1)) \
F(B_DEFOG_EFFECT_CLEARING, defogEffectClearing, (u32, GEN_COUNT - 1)) \
F(B_STOCKPILE_RAISES_DEFS, stockpileRaisesDefs, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
F(B_TRANSFORM_SEMI_INV_FAIL, transformSemiInvFail, (u32, GEN_COUNT - 1)) \
F(B_TRANSFORM_TARGET_FAIL, transformTargetFail, (u32, GEN_COUNT - 1)) \
F(B_TRANSFORM_USER_FAIL, transformUserFail, (u32, GEN_COUNT - 1)) \
F(B_TRANSFORM_SUBSTITUTE_FAIL, transformSubstituteFail, (u32, GEN_COUNT - 1)) \
F(B_TRANSFORM_SHINY, transformShiny, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
F(B_TRANSFORM_FORM_CHANGES, transformFormChanges, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
F(B_TRANSFORM_FORM_CHANGES, transformFormChanges, (u32, GEN_COUNT - 1)) \
F(B_WIDE_GUARD, wideGuard, (u32, GEN_COUNT - 1)) \
F(B_QUICK_GUARD, quickGuard, (u32, GEN_COUNT - 1)) \
F(B_IMPRISON, imprison, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \

View File

@ -11278,10 +11278,11 @@ static void Cmd_transformdataexecution(void)
gChosenMove = MOVE_UNAVAILABLE;
gBattlescriptCurrInstr = cmd->nextInstr;
if (gBattleMons[gBattlerTarget].volatiles.transformed
|| DoesSubstituteBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove)
|| gBattleStruct->illusion[gBattlerTarget].state == ILLUSION_ON
|| IsSemiInvulnerable(gBattlerTarget, EXCLUDE_COMMANDER))
if ((GetConfig(B_TRANSFORM_SEMI_INV_FAIL) >= GEN_2 && IsSemiInvulnerable(gBattlerTarget, EXCLUDE_COMMANDER))
|| (GetConfig(B_TRANSFORM_TARGET_FAIL) >= GEN_2 && gBattleMons[gBattlerTarget].volatiles.transformed)
|| (GetConfig(B_TRANSFORM_USER_FAIL) >= GEN_5 && gBattleMons[gBattlerAttacker].volatiles.transformed)
|| (GetConfig(B_TRANSFORM_SUBSTITUTE_FAIL) >= GEN_5 && DoesSubstituteBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove))
|| gBattleStruct->illusion[gBattlerTarget].state == ILLUSION_ON)
{
gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_FAILED;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TRANSFORM_FAILED;

View File

@ -9342,7 +9342,7 @@ static bool32 CanBattlerFormChange(u32 battler, enum FormChanges method)
{
// Can't change form if transformed.
if (gBattleMons[battler].volatiles.transformed
&& B_TRANSFORM_FORM_CHANGES >= GEN_5)
&& GetConfig(B_TRANSFORM_FORM_CHANGES) >= GEN_5)
return FALSE;
switch (method)

View File

@ -1,7 +1,135 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Transform (Move Effect) test titles")
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_TRANSFORM) == EFFECT_TRANSFORM);
}
SINGLE_BATTLE_TEST("Transform fails on semi-invulnerable target in Gen2+")
{
u32 genConfig;
bool32 expectFail;
PARAMETRIZE { genConfig = GEN_1; expectFail = FALSE; }
PARAMETRIZE { genConfig = GEN_2; expectFail = TRUE; }
GIVEN {
WITH_CONFIG(B_TRANSFORM_SEMI_INV_FAIL, genConfig);
PLAYER(SPECIES_WOBBUFFET) { Speed(50); Moves(MOVE_DIG); }
OPPONENT(SPECIES_DITTO) { Speed(10); Moves(MOVE_TRANSFORM); }
} WHEN {
TURN { MOVE(player, MOVE_DIG); MOVE(opponent, MOVE_TRANSFORM); }
} SCENE {
if (expectFail)
MESSAGE("But it failed!");
else
MESSAGE("The opposing Ditto transformed into Wobbuffet!");
}
}
SINGLE_BATTLE_TEST("Transform fails on transformed target in Gen2+")
{
u32 genConfig;
bool32 expectFail;
PARAMETRIZE { genConfig = GEN_1; expectFail = FALSE; }
PARAMETRIZE { genConfig = GEN_2; expectFail = TRUE; }
GIVEN {
WITH_CONFIG(B_TRANSFORM_TARGET_FAIL, genConfig);
PLAYER(SPECIES_DITTO) { Speed(50); Moves(MOVE_TRANSFORM, MOVE_CELEBRATE); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(10); Moves(MOVE_TRANSFORM, MOVE_CELEBRATE); }
} WHEN {
TURN { MOVE(player, MOVE_TRANSFORM); MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_TRANSFORM); }
} SCENE {
MESSAGE("Ditto transformed into Wobbuffet!");
if (expectFail)
MESSAGE("But it failed!");
else
MESSAGE("The opposing Wobbuffet transformed into Wobbuffet!");
}
}
SINGLE_BATTLE_TEST("Transform fails when the user is already transformed in Gen5+")
{
u32 genConfig;
bool32 expectFail;
PARAMETRIZE { genConfig = GEN_4; expectFail = FALSE; }
PARAMETRIZE { genConfig = GEN_5; expectFail = TRUE; }
GIVEN {
WITH_CONFIG(B_TRANSFORM_USER_FAIL, genConfig);
PLAYER(SPECIES_WOBBUFFET) { Speed(50); Moves(MOVE_TRANSFORM, MOVE_CELEBRATE); }
OPPONENT(SPECIES_DITTO) { Speed(10); Moves(MOVE_TRANSFORM, MOVE_CELEBRATE); }
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_TRANSFORM); }
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_TRANSFORM); }
} SCENE {
MESSAGE("The opposing Ditto transformed into Wobbuffet!");
if (expectFail)
MESSAGE("But it failed!");
else
MESSAGE("The opposing Ditto transformed into Wobbuffet!");
}
}
SINGLE_BATTLE_TEST("Transform fails on target behind substitute in Gen5+")
{
u32 genConfig;
bool32 expectFail;
PARAMETRIZE { genConfig = GEN_4; expectFail = FALSE; }
PARAMETRIZE { genConfig = GEN_5; expectFail = TRUE; }
GIVEN {
WITH_CONFIG(B_TRANSFORM_SUBSTITUTE_FAIL, genConfig);
PLAYER(SPECIES_WOBBUFFET) { Speed(50); Moves(MOVE_SUBSTITUTE); }
OPPONENT(SPECIES_DITTO) { Speed(10); Moves(MOVE_TRANSFORM); }
} WHEN {
TURN { MOVE(player, MOVE_SUBSTITUTE); MOVE(opponent, MOVE_TRANSFORM); }
} SCENE {
if (expectFail)
MESSAGE("But it failed!");
else
MESSAGE("The opposing Ditto transformed into Wobbuffet!");
}
}
SINGLE_BATTLE_TEST("Transformed Pokemon cannot change forms in Gen5+")
{
u32 genConfig;
bool32 expectFormChange;
PARAMETRIZE { genConfig = GEN_4; expectFormChange = TRUE; }
PARAMETRIZE { genConfig = GEN_5; expectFormChange = FALSE; }
GIVEN {
WITH_CONFIG(B_TRANSFORM_FORM_CHANGES, genConfig);
PLAYER(SPECIES_AEGISLASH) { Moves(MOVE_TACKLE, MOVE_CELEBRATE); }
OPPONENT(SPECIES_DITTO) { Moves(MOVE_TACKLE, MOVE_TRANSFORM); }
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_TRANSFORM); }
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_TACKLE); }
} SCENE {
if (expectFormChange) {
ABILITY_POPUP(opponent, ABILITY_STANCE_CHANGE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, opponent);
} else {
NONE_OF {
ABILITY_POPUP(opponent, ABILITY_STANCE_CHANGE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, opponent);
}
}
} THEN {
if (expectFormChange)
EXPECT_EQ(opponent->species, SPECIES_AEGISLASH_BLADE);
else
EXPECT_EQ(opponent->species, SPECIES_AEGISLASH);
}
}
SINGLE_BATTLE_TEST("(TERA) Transform does not copy the target's Tera Type, and if the user is Terastallized it keeps its own Tera Type")
{
@ -42,3 +170,5 @@ SINGLE_BATTLE_TEST("Transform returns the user to normal at the end of the battl
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), SPECIES_DITTO);
}
}
TO_DO_BATTLE_TEST("TODO: Write Transform (Move Effect) test titles")