Illusion struct cleanup and many Illusion fixes (#6798)

This commit is contained in:
PhallenTree 2025-05-09 19:41:20 +01:00 committed by GitHub
parent e587757fa6
commit 48c08c0368
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 210 additions and 84 deletions

View File

@ -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

View File

@ -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;
};

View File

@ -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[];

View File

@ -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);

View File

@ -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);
}

View File

@ -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.

View File

@ -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);

View File

@ -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);

View File

@ -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++;

View File

@ -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)

View File

@ -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;

View File

@ -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);
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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 {

View File

@ -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);
}
}