diff --git a/include/config/battle.h b/include/config/battle.h index 965174d579..242e4bea68 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -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. diff --git a/include/constants/generational_changes.h b/include/constants/generational_changes.h index 0a5331c342..84b9da76b1 100644 --- a/include/constants/generational_changes.h +++ b/include/constants/generational_changes.h @@ -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 */ \ diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 8819c8afad..0627013f5a 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -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; diff --git a/src/battle_util.c b/src/battle_util.c index e352173577..f0b4925229 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -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) diff --git a/test/battle/move_effect/transform.c b/test/battle/move_effect/transform.c index de98de70a6..d82728c184 100644 --- a/test/battle/move_effect/transform.c +++ b/test/battle/move_effect/transform.c @@ -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")