From 48c08c0368e850b05834df1dfe1c6e9707298903 Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Fri, 9 May 2025 19:41:20 +0100 Subject: [PATCH] Illusion struct cleanup and many Illusion fixes (#6798) --- data/battle_scripts_1.s | 8 +++ include/battle.h | 11 ++-- include/battle_scripts.h | 2 + include/battle_util.h | 3 ++ src/battle_controllers.c | 7 +-- src/battle_dynamax.c | 2 +- src/battle_gfx_sfx_util.c | 7 ++- src/battle_interface.c | 19 ++++--- src/battle_main.c | 2 + src/battle_message.c | 23 +++------ src/battle_script_commands.c | 14 +++--- src/battle_terastal.c | 3 ++ src/battle_util.c | 92 +++++++++++++++++++++++----------- test/battle/ability/illusion.c | 54 ++++++++++++++++++-- test/battle/gimmick/dynamax.c | 18 +++++++ test/battle/gimmick/terastal.c | 29 +++++++++-- 16 files changed, 210 insertions(+), 84 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 9348ba5a80..772f6aa404 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -6845,6 +6845,14 @@ BattleScript_BattlerFormChangeWithStringEnd3:: waitmessage B_WAIT_TIME_LONG end3 +BattleScript_IllusionOffAndTerastallization:: + call BattleScript_IllusionOff + goto BattleScript_Terastallization + +BattleScript_IllusionOffEnd3:: + call BattleScript_IllusionOff + end3 + BattleScript_IllusionOff:: spriteignore0hp TRUE playanimation BS_SCRIPTING, B_ANIM_ILLUSION_OFF diff --git a/include/battle.h b/include/battle.h index 6d63aebac7..cb49fc730e 100644 --- a/include/battle.h +++ b/include/battle.h @@ -523,12 +523,15 @@ struct LinkBattlerHeader struct BattleEnigmaBerry battleEnigmaBerry; }; +enum IllusionState { + ILLUSION_NOT_SET, + ILLUSION_OFF, + ILLUSION_ON +}; + struct Illusion { - u8 on; - u8 set; - u8 broken; - u8 partyId; + enum IllusionState state; struct Pokemon *mon; }; diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 1a60f9b559..f8077aa894 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -366,6 +366,8 @@ extern const u8 BattleScript_FlameOrb[]; extern const u8 BattleScript_MoveEffectIncinerate[]; extern const u8 BattleScript_MoveEffectBugBite[]; extern const u8 BattleScript_IllusionOff[]; +extern const u8 BattleScript_IllusionOffEnd3[]; +extern const u8 BattleScript_IllusionOffAndTerastallization[]; extern const u8 BattleScript_DancerActivates[]; extern const u8 BattleScript_AftermathDmg[]; extern const u8 BattleScript_AttackerFormChange[]; diff --git a/include/battle_util.h b/include/battle_util.h index cc4bb819b2..b7f3074702 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -291,9 +291,12 @@ u16 GetBattleFormChangeTargetSpecies(u32 battler, enum FormChanges method); bool32 TryBattleFormChange(u32 battler, enum FormChanges method); bool32 DoBattlersShareType(u32 battler1, u32 battler2); bool32 CanBattlerGetOrLoseItem(u32 battler, u16 itemId); +u32 GetBattlerVisualSpecies(u32 battler); +bool32 TryClearIllusion(u32 battler, u32 caseID); u32 GetIllusionMonSpecies(u32 battler); struct Pokemon *GetIllusionMonPtr(u32 battler); void ClearIllusionMon(u32 battler); +u32 GetIllusionMonPartyId(struct Pokemon *party, struct Pokemon *mon, struct Pokemon *partnerMon); bool32 SetIllusionMon(struct Pokemon *mon, u32 battler); bool32 ShouldGetStatBadgeBoost(u16 flagId, u32 battler); u32 GetBattleMoveCategory(u32 move); diff --git a/src/battle_controllers.c b/src/battle_controllers.c index 0af749bfed..f8238d8078 100644 --- a/src/battle_controllers.c +++ b/src/battle_controllers.c @@ -3044,7 +3044,7 @@ static void AnimateMonAfterKnockout(u32 battler) static void LaunchKOAnimation(u32 battlerId, u16 animId, bool32 isFront) { - u32 species = gBattleMons[battlerId].species; + u32 species = GetBattlerVisualSpecies(battlerId); u32 spriteId = gBattlerSpriteIds[battlerId]; if (isFront) @@ -3064,9 +3064,10 @@ static void LaunchKOAnimation(u32 battlerId, u16 animId, bool32 isFront) static u32 ReturnAnimIdForBattler(bool32 wasPlayerSideKnockedOut, u32 specificBattler) { + u32 species = GetBattlerVisualSpecies(specificBattler); if (wasPlayerSideKnockedOut) - return gSpeciesInfo[gBattleMons[specificBattler].species].frontAnimId; + return gSpeciesInfo[species].frontAnimId; else - return GetSpeciesBackAnimSet(gBattleMons[specificBattler].species); + return GetSpeciesBackAnimSet(species); } diff --git a/src/battle_dynamax.c b/src/battle_dynamax.c index b56b099eb5..7593f8c14f 100644 --- a/src/battle_dynamax.c +++ b/src/battle_dynamax.c @@ -73,7 +73,7 @@ static const struct GMaxMove sGMaxMoveTable[] = // Returns whether a battler can Dynamax. bool32 CanDynamax(u32 battler) { - u16 species = gBattleMons[battler].species; + u16 species = GetBattlerVisualSpecies(battler); enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(battler, FALSE); // Prevents Zigzagoon from dynamaxing in vanilla. diff --git a/src/battle_gfx_sfx_util.c b/src/battle_gfx_sfx_util.c index 0a35db84b3..76bf5dd191 100644 --- a/src/battle_gfx_sfx_util.c +++ b/src/battle_gfx_sfx_util.c @@ -512,10 +512,7 @@ bool8 TryHandleLaunchBattleTableAnimation(u8 activeBattler, u8 atkBattler, u8 de } if (tableId == B_ANIM_ILLUSION_OFF) - { - gBattleStruct->illusion[activeBattler].broken = 1; - gBattleStruct->illusion[activeBattler].on = 0; - } + gBattleStruct->illusion[activeBattler].state = ILLUSION_OFF; gBattleAnimAttacker = atkBattler; gBattleAnimTarget = defBattler; @@ -951,6 +948,8 @@ void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool32 megaEvo, bo // Get base form if its currently Gigantamax if (IsGigantamaxed(battlerDef)) targetSpecies = gBattleStruct->changedSpecies[GetBattlerSide(battlerDef)][gBattlerPartyIndexes[battlerDef]]; + else if (gBattleStruct->illusion[battlerDef].state == ILLUSION_ON) + targetSpecies = GetIllusionMonSpecies(battlerDef); else targetSpecies = GetMonData(monDef, MON_DATA_SPECIES); personalityValue = GetMonData(monAtk, MON_DATA_PERSONALITY); diff --git a/src/battle_interface.c b/src/battle_interface.c index 96f4ac06f9..7af4375e59 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -2546,20 +2546,23 @@ static void PrintBattlerOnAbilityPopUp(u8 battlerId, u8 spriteId1, u8 spriteId2) u8 lastChar; u8* textPtr; u8 monName[POKEMON_NAME_LENGTH + 3] = {0}; - u8* nick = gBattleMons[battlerId].nickname; // This needs to be updated for Illusion support + struct Pokemon *illusionMon = GetIllusionMonPtr(battlerId); + u8 nick[POKEMON_NAME_LENGTH + 1] = {0}; + + if (illusionMon != NULL) + GetMonData(illusionMon, MON_DATA_NICKNAME, nick); + else + GetMonData(GetPartyBattlerData(battlerId), MON_DATA_NICKNAME, nick); for (i = 0; i < POKEMON_NAME_LENGTH; ++i) { - monName[i] = nick[i]; - - if (nick[i] == EOS || i + 1 == POKEMON_NAME_LENGTH) // End of string + if (nick[i] == EOS) // End of string break; + + monName[i] = nick[i]; } - textPtr = monName + i + 1; - - if (*(textPtr - 1) == EOS) - textPtr--; + textPtr = monName + i; lastChar = *(textPtr - 1); diff --git a/src/battle_main.c b/src/battle_main.c index e53acb1454..2346a2094f 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3829,6 +3829,8 @@ static void TryDoEventsBeforeFirstTurn(void) return; if (AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, i, 0, 0, 0) != 0) return; + if (TryClearIllusion(i, ABILITYEFFECT_ON_SWITCHIN)) + return; } gBattleStruct->switchInBattlerCounter = 0; gBattleStruct->eventsBeforeFirstTurnState++; diff --git a/src/battle_message.c b/src/battle_message.c index 13baf4641f..62d937a02e 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -3173,7 +3173,7 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst, u32 dstSize) static void IllusionNickHack(u32 battler, u32 partyId, u8 *dst) { - s32 id, i; + u32 id = PARTY_SIZE; // we know it's gEnemyParty struct Pokemon *mon = &gEnemyParty[partyId], *partnerMon; @@ -3184,22 +3184,13 @@ static void IllusionNickHack(u32 battler, u32 partyId, u8 *dst) else partnerMon = mon; - // Find last alive non-egg pokemon. - for (i = PARTY_SIZE - 1; i >= 0; i--) - { - id = i; - if (GetMonData(&gEnemyParty[id], MON_DATA_SANITY_HAS_SPECIES) - && GetMonData(&gEnemyParty[id], MON_DATA_HP) - && &gEnemyParty[id] != mon - && &gEnemyParty[id] != partnerMon) - { - GetMonData(&gEnemyParty[id], MON_DATA_NICKNAME, dst); - return; - } - } + id = GetIllusionMonPartyId(gEnemyParty, mon, partnerMon); } - - GetMonData(mon, MON_DATA_NICKNAME, dst); + + if (id != PARTY_SIZE) + GetMonData(&gEnemyParty[id], MON_DATA_NICKNAME, dst); + else + GetMonData(mon, MON_DATA_NICKNAME, dst); } void ExpandBattleTextBuffPlaceholders(const u8 *src, u8 *dst) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 4242277664..b837544541 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -6452,6 +6452,8 @@ static void Cmd_moveend(void) case MOVEEND_ABILITIES: // Such as abilities activating on contact(Poison Spore, Rough Skin, etc.). if (AbilityBattleEffects(ABILITYEFFECT_MOVE_END, gBattlerTarget, 0, 0, 0)) effect = TRUE; + else if (TryClearIllusion(gBattlerTarget, ABILITYEFFECT_MOVE_END)) + effect = TRUE; gBattleScripting.moveendState++; break; case MOVEEND_ABILITIES_ATTACKER: // Poison Touch, possibly other in the future @@ -8332,6 +8334,8 @@ static bool32 DoSwitchInEffectsForBattler(u32 battler) return TRUE; break; } + if (TryClearIllusion(i, ABILITYEFFECT_ON_SWITCHIN)) + return TRUE; } gDisableStructs[battler].stickyWebDone = FALSE; @@ -10164,14 +10168,8 @@ static void Cmd_various(void) case VARIOUS_TRY_ILLUSION_OFF: { VARIOUS_ARGS(); - if (GetIllusionMonPtr(battler) != NULL) - { - gBattleScripting.battler = battler; - gBattlescriptCurrInstr = cmd->nextInstr; - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_IllusionOff; + if (TryClearIllusion(battler, ABILITYEFFECT_MOVE_END)) return; - } break; } case VARIOUS_SET_SPRITEIGNORE0HP: @@ -13140,7 +13138,7 @@ static void Cmd_transformdataexecution(void) gChosenMove = MOVE_UNAVAILABLE; gBattlescriptCurrInstr = cmd->nextInstr; if (gBattleMons[gBattlerTarget].status2 & STATUS2_TRANSFORMED - || gBattleStruct->illusion[gBattlerTarget].on + || gBattleStruct->illusion[gBattlerTarget].state == ILLUSION_ON || gStatuses3[gBattlerTarget] & STATUS3_SEMI_INVULNERABLE_NO_COMMANDER) { gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_FAILED; diff --git a/src/battle_terastal.c b/src/battle_terastal.c index 1f57bdbf6c..fb7ca10f28 100644 --- a/src/battle_terastal.c +++ b/src/battle_terastal.c @@ -39,6 +39,9 @@ void ActivateTera(u32 battler) PREPARE_TYPE_BUFFER(gBattleTextBuff1, GetBattlerTeraType(battler)); if (TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_TERASTALLIZATION)) BattleScriptExecute(BattleScript_TeraFormChange); + else if (gBattleStruct->illusion[gBattlerAttacker].state == ILLUSION_ON + && DoesSpeciesHaveFormChangeMethod(GetIllusionMonSpecies(gBattlerAttacker), FORM_CHANGE_BATTLE_TERASTALLIZATION)) + BattleScriptExecute(BattleScript_IllusionOffAndTerastallization); else BattleScriptExecute(BattleScript_Terastallization); } diff --git a/src/battle_util.c b/src/battle_util.c index 25ebfb55c2..216361e6c6 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -3529,7 +3529,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 if (IsBattlerAlive(diagonalBattler) && !(gBattleMons[diagonalBattler].status2 & (STATUS2_TRANSFORMED | STATUS2_SUBSTITUTE)) && !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED) - && !(gBattleStruct->illusion[diagonalBattler].on) + && gBattleStruct->illusion[diagonalBattler].state != ILLUSION_ON && !(gStatuses3[diagonalBattler] & STATUS3_SEMI_INVULNERABLE_NO_COMMANDER)) { gBattlerAttacker = battler; @@ -4754,7 +4754,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 } break; case ABILITY_ILLUSION: - if (gBattleStruct->illusion[gBattlerTarget].on && !gBattleStruct->illusion[gBattlerTarget].broken && IsBattlerTurnDamaged(gBattlerTarget)) + if (gBattleStruct->illusion[gBattlerTarget].state == ILLUSION_ON && IsBattlerTurnDamaged(gBattlerTarget)) { gBattleScripting.battler = gBattlerTarget; BattleScriptPushCursor(); @@ -5816,7 +5816,7 @@ static bool32 IsNonVolatileStatusBlocked(u32 battlerDef, u32 abilityDef, u32 abi if (abilityAffected) { gLastUsedAbility = abilityDef; - gBattlerAbility = battlerDef; + gBattleScripting.battler = gBattlerAbility = battlerDef; RecordAbilityBattle(battlerDef, abilityDef); } @@ -10334,18 +10334,44 @@ bool32 CanBattlerGetOrLoseItem(u32 battler, u16 itemId) return TRUE; } +u32 GetBattlerVisualSpecies(u32 battler) +{ + u32 illusionSpecies = GetIllusionMonSpecies(battler); + if (illusionSpecies != SPECIES_NONE) + return illusionSpecies; + return gBattleMons[battler].species; +} + +bool32 TryClearIllusion(u32 battler, u32 caseID) +{ + if (gBattleStruct->illusion[battler].state != ILLUSION_ON) + return FALSE; + if (GetBattlerAbility(battler) == ABILITY_ILLUSION && IsBattlerAlive(battler)) + return FALSE; + + gBattleScripting.battler = battler; + if (caseID == ABILITYEFFECT_ON_SWITCHIN) + { + BattleScriptPushCursorAndCallback(BattleScript_IllusionOffEnd3); + } + else + { + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_IllusionOff; + } + return TRUE; +} + struct Pokemon *GetIllusionMonPtr(u32 battler) { - if (gBattleStruct->illusion[battler].broken) - return NULL; - if (!gBattleStruct->illusion[battler].set) + if (gBattleStruct->illusion[battler].state == ILLUSION_NOT_SET) { if (GetBattlerSide(battler) == B_SIDE_PLAYER) SetIllusionMon(GetBattlerMon(battler), battler); else SetIllusionMon(GetBattlerMon(battler), battler); } - if (!gBattleStruct->illusion[battler].on) + if (gBattleStruct->illusion[battler].state != ILLUSION_ON) return NULL; return gBattleStruct->illusion[battler].mon; @@ -10364,12 +10390,34 @@ u32 GetIllusionMonSpecies(u32 battler) return SPECIES_NONE; } +u32 GetIllusionMonPartyId(struct Pokemon *party, struct Pokemon *mon, struct Pokemon *partnerMon) +{ + s32 id; + // Find last alive non-egg pokemon. + for (id = PARTY_SIZE - 1; id >= 0; id--) + { + if (GetMonData(&party[id], MON_DATA_SANITY_HAS_SPECIES) + && GetMonData(&party[id], MON_DATA_HP) + && !GetMonData(&party[id], MON_DATA_IS_EGG)) + { + u32 species = GetMonData(&party[id], MON_DATA_SPECIES); + if (species == SPECIES_TERAPAGOS_STELLAR || (species >= SPECIES_OGERPON_TEAL_TERA && species <= SPECIES_OGERPON_CORNERSTONE_TERA)) + continue; + if (&party[id] != mon && &party[id] != partnerMon) + return id; + else // If this pokemon or its partner is last in the party, ignore Illusion. + return PARTY_SIZE; + } + } + return PARTY_SIZE; +} + bool32 SetIllusionMon(struct Pokemon *mon, u32 battler) { struct Pokemon *party, *partnerMon; - s32 i, id; + u32 id; - gBattleStruct->illusion[battler].set = 1; + gBattleStruct->illusion[battler].state = ILLUSION_OFF; if (GetMonAbility(mon) != ABILITY_ILLUSION) return FALSE; @@ -10380,28 +10428,12 @@ bool32 SetIllusionMon(struct Pokemon *mon, u32 battler) else partnerMon = mon; - // Find last alive non-egg pokemon. - for (i = PARTY_SIZE - 1; i >= 0; i--) + id = GetIllusionMonPartyId(party, mon, partnerMon); + if (id != PARTY_SIZE) { - id = i; - if (GetMonData(&party[id], MON_DATA_SANITY_HAS_SPECIES) - && GetMonData(&party[id], MON_DATA_HP) - && !GetMonData(&party[id], MON_DATA_IS_EGG)) - { - if (&party[id] != mon && &party[id] != partnerMon) - { - gBattleStruct->illusion[battler].on = 1; - gBattleStruct->illusion[battler].broken = 0; - gBattleStruct->illusion[battler].partyId = id; - gBattleStruct->illusion[battler].mon = &party[id]; - return TRUE; - } - else if (&party[id] == mon) - { - // If this pokemon is last in the party, ignore Illusion. - return FALSE; - } - } + gBattleStruct->illusion[battler].state = ILLUSION_ON; + gBattleStruct->illusion[battler].mon = &party[id]; + return TRUE; } return FALSE; diff --git a/test/battle/ability/illusion.c b/test/battle/ability/illusion.c index c76b4f307e..56fad1b62e 100644 --- a/test/battle/ability/illusion.c +++ b/test/battle/ability/illusion.c @@ -14,7 +14,7 @@ SINGLE_BATTLE_TEST("Illusion can only imitate Normal Form terapagos") TURN { SWITCH(player, 1); } // Switch back to Zoroark, should not be Terastal Terapagos TURN { SWITCH(player, 0); MOVE(opponent, MOVE_SCRATCH);} - // Switch back to Terapagos + // 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); } @@ -27,7 +27,7 @@ SINGLE_BATTLE_TEST("Illusion breaks if the target faints") { GIVEN { PLAYER(SPECIES_ZOROARK) { HP(1); } - PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(opponent, MOVE_SCRATCH); SEND_OUT(player, 1); } @@ -44,7 +44,7 @@ SINGLE_BATTLE_TEST("Illusion breaks if the attacker faints") GIVEN { ASSUME(GetMoveEffect(MOVE_FINAL_GAMBIT) == EFFECT_FINAL_GAMBIT); PLAYER(SPECIES_ZOROARK) { HP(1); } - PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(player, MOVE_FINAL_GAMBIT); SEND_OUT(player, 1); } @@ -59,13 +59,57 @@ SINGLE_BATTLE_TEST("Illusion breaks if the attacker faints") SINGLE_BATTLE_TEST("Illusion cannot imitate if the user is on the last slot") { GIVEN { - PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); PLAYER(SPECIES_ZOROARK); OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { SWITCH(player, 1); } } THEN { EXPECT_EQ(player->species, SPECIES_ZOROARK); - EXPECT_EQ(gBattleStruct->illusion[0].on, FALSE); // Battler is Zoroark and not Illusioned + EXPECT_EQ(gBattleStruct->illusion[0].state, ILLUSION_OFF); // Battler is Zoroark and not Illusioned + } +} + +SINGLE_BATTLE_TEST("Illusion breaks in Neutralizing Gas") +{ + GIVEN { + PLAYER(SPECIES_ZOROARK); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); } + } WHEN { + TURN { } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_NEUTRALIZING_GAS); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ILLUSION_OFF, player); + } +} + +SINGLE_BATTLE_TEST("Illusion breaks if affected by Gastro Acid") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_GASTRO_ACID) == EFFECT_GASTRO_ACID); + PLAYER(SPECIES_ZOROARK); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_GASTRO_ACID); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_GASTRO_ACID, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ILLUSION_OFF, player); + } +} + +SINGLE_BATTLE_TEST("Illusion breaks if user loses Illusion due to Worry Seed") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_WORRY_SEED) == EFFECT_WORRY_SEED); + PLAYER(SPECIES_ZOROARK); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_WORRY_SEED); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_WORRY_SEED, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ILLUSION_OFF, player); } } diff --git a/test/battle/gimmick/dynamax.c b/test/battle/gimmick/dynamax.c index fb47682735..ee12bac875 100644 --- a/test/battle/gimmick/dynamax.c +++ b/test/battle/gimmick/dynamax.c @@ -164,6 +164,24 @@ SINGLE_BATTLE_TEST("Dynamax: Dynamax expires after three turns and correctly con } } +// Visual test to make sure Zoroark appears as Wobbuffet/Zigzagoon until illusion breaks +SINGLE_BATTLE_TEST("Dynamax: Illusion doesn't break upon Dynamaxing when illusioned") +{ + u32 species; + PARAMETRIZE { species = SPECIES_WOBBUFFET; } + PARAMETRIZE { species = SPECIES_ZIGZAGOON; } + GIVEN { + PLAYER(SPECIES_ZOROARK); + PLAYER(species); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SCRATCH, gimmick: GIMMICK_DYNAMAX); MOVE(opponent, MOVE_SCRATCH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ILLUSION_OFF, player); + } +} + SINGLE_BATTLE_TEST("Dynamax: Dynamaxed Pokemon cannot be flinched") { GIVEN { diff --git a/test/battle/gimmick/terastal.c b/test/battle/gimmick/terastal.c index a021fbf343..19f42fbc9d 100644 --- a/test/battle/gimmick/terastal.c +++ b/test/battle/gimmick/terastal.c @@ -777,20 +777,39 @@ SINGLE_BATTLE_TEST("(TERA) Terapagos retains its base defensive profile when Ter } } -SINGLE_BATTLE_TEST("(TERA) Illusion breaks if the pokemon Terastalizes") +SINGLE_BATTLE_TEST("(TERA) Illusion breaks if the pokemon Terastallizes when illusioned as a mon that changes forms by Terastallizing") { - KNOWN_FAILING; // #5015 u32 species; PARAMETRIZE { species = SPECIES_TERAPAGOS; } - PARAMETRIZE { species = SPECIES_WOBBUFFET; } + PARAMETRIZE { species = SPECIES_OGERPON; } GIVEN { - PLAYER(SPECIES_ZOROARK) { TeraType(TYPE_DARK); } + ASSUME(DoesSpeciesHaveFormChangeMethod(species, FORM_CHANGE_BATTLE_TERASTALLIZATION)); + PLAYER(SPECIES_ZOROARK) { TeraType(TYPE_BUG); } PLAYER(species); OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); } } SCENE { - MESSAGE("Zoroark's Illusion wore off!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ILLUSION_OFF, player); + } +} + +// Visual test to make sure Zoroark appears as Wobbuffet/Zigzagoon until illusion breaks +SINGLE_BATTLE_TEST("(TERA) Illusion doesn't break upon Terastallizing when illusioned as a mon that doesn't change forms by Terastallizing") +{ + u32 species; + PARAMETRIZE { species = SPECIES_WOBBUFFET; } + PARAMETRIZE { species = SPECIES_ZIGZAGOON; } + GIVEN { + ASSUME(!DoesSpeciesHaveFormChangeMethod(species, FORM_CHANGE_BATTLE_TERASTALLIZATION)); + PLAYER(SPECIES_ZOROARK) { TeraType(TYPE_BUG); } + PLAYER(species); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); MOVE(opponent, MOVE_SCRATCH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ILLUSION_OFF, player); } }