Add FORM_CHANGE_BATTLE_TERASTALLIZATION + allow species to force tera types (#4438)

* Add FORM_CHANGE_BATTLE_TERASTALLIZATION and allow species to force tera types

* Fix form change not changing tera type

* Update form_species_tables.h

* Address reviews

* Can't change the forced Tera Type anymore

* Revert "Can't change the forced Tera Type anymore"

This reverts commit 67157250ef.

* Fix a lot of things

* Oops

* Update pokemon.h

* Update pokemon.h

* Address reviews

* Update tera_starstorm.c

* Update test/battle/gimmick/terastal.c

---------

Co-authored-by: Eduardo Quezada <eduardo602002@gmail.com>
This commit is contained in:
kittenchilly 2024-06-04 06:38:49 -05:00 committed by GitHub
parent 167cd7d0f7
commit 7f8f480ecb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 193 additions and 51 deletions

View file

@ -27436,12 +27436,13 @@ General_TeraCharge:
delay 20
createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 0, 16, RGB_WHITEALPHA
waitforvisualfinish
createvisualtask AnimTask_TransformMon, 2, 1, 0
call TeraChargeParticles
playsewithpan SE_M_BRICK_BREAK, SOUND_PAN_ATTACKER
clearmonbg ANIM_ATK_PARTNER
blendoff
end
TeraChargeParticles:
createsprite gTeraCrystalSpreadSpriteTemplate, ANIM_TARGET, 0, 0, -5, 8
createsprite gTeraCrystalSpreadSpriteTemplate, ANIM_TARGET, 0, 1, 5, 9

View file

@ -32,6 +32,22 @@ BattleScript_Terastallization::
waitmessage B_WAIT_TIME_LONG
end3
BattleScript_TeraFormChange::
@ TODO: no string prints in S/V, but right now this helps with clarity
printstring STRINGID_PKMNSTORINGENERGY
handleformchange BS_ATTACKER, 0
handleformchange BS_ATTACKER, 1
playanimation BS_ATTACKER, B_ANIM_TERA_CHARGE
waitanimation
applyterastallization
playanimation BS_ATTACKER, B_ANIM_TERA_ACTIVATE
waitanimation
handleformchange BS_ATTACKER, 2
printstring STRINGID_PKMNTERASTALLIZEDINTO
waitmessage B_WAIT_TIME_LONG
switchinabilities BS_ATTACKER
end3
BattleScript_LowerAtkSpAtk::
jumpifstat BS_EFFECT_BATTLER, CMP_GREATER_THAN, STAT_ATK, MIN_STAT_STAGE, BattleScript_LowerAtkSpAtkDoAnim
jumpifstat BS_EFFECT_BATTLER, CMP_EQUAL, STAT_SPATK, MIN_STAT_STAGE, BattleScript_LowerAtkSpAtkEnd

View file

@ -510,6 +510,7 @@ extern const u8 BattleScript_LowerAtkSpAtk[];
extern const u8 BattleScript_Terastallization[];
extern const u8 BattleScript_BoosterEnergyEnd2[];
extern const u8 BattleScript_TeraShellDistortingTypeMatchups[];
extern const u8 BattleScript_TeraFormChange[];
// zmoves
extern const u8 BattleScript_ZMoveActivateDamaging[];

View file

@ -121,6 +121,10 @@
#define FORM_CHANGE_STATUS 20
// Form change that activates after move is used. Currently only used for activating Gulp Missile.
#define FORM_CHANGE_HIT_BY_MOVE 21
#define FORM_CHANGE_HIT_BY_MOVE 21
// Form change that activates when terastallized as as a specific type
// param1: tera type
#define FORM_CHANGE_BATTLE_TERASTALLIZATION 22
#endif // GUARD_CONSTANTS_FORM_CHANGE_TYPES_H

View file

@ -360,7 +360,7 @@ struct SpeciesInfo /*0x8C*/
/* 0x05 */ u8 baseSpDefense;
/* 0x06 */ u8 types[2];
/* 0x08 */ u8 catchRate;
/* 0x09 */ u8 padding1;
/* 0x09 */ u8 forceTeraType;
/* 0x0A */ u16 expYield; // expYield was changed from u8 to u16 for the new Exp System.
/* 0x0C */ u16 evYield_HP:2;
u16 evYield_Attack:2;
@ -378,6 +378,7 @@ struct SpeciesInfo /*0x8C*/
/* 0x16 */ u8 eggGroups[2];
/* 0x18 */ u16 abilities[NUM_ABILITY_SLOTS]; // 3 abilities, no longer u8 because we have over 255 abilities now.
/* 0x1E */ u8 safariZoneFleeRate;
// Pokédex data
/* 0x1F */ u8 categoryName[13];
/* 0x1F */ u8 speciesName[POKEMON_NAME_LENGTH + 1];
@ -431,6 +432,7 @@ struct SpeciesInfo /*0x8C*/
u32 isPrimalReversion:1;
u32 isUltraBurst:1;
u32 isGigantamax:1;
u32 isTeraForm:1;
u32 isAlolanForm:1;
u32 isGalarianForm:1;
u32 isHisuianForm:1;

View file

@ -5086,7 +5086,10 @@ static bool32 TryDoGimmicksBeforeMoves(void)
gBattleStruct->tera.toTera &= ~(gBitTable[gBattlerAttacker]);
PrepareBattlerForTera(gBattlerAttacker);
PREPARE_TYPE_BUFFER(gBattleTextBuff1, GetBattlerTeraType(gBattlerAttacker));
BattleScriptExecute(BattleScript_Terastallization);
if (TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_TERASTALLIZATION))
BattleScriptExecute(BattleScript_TeraFormChange);
else
BattleScriptExecute(BattleScript_Terastallization);
return TRUE;
}
// Dynamax Check

View file

@ -2050,7 +2050,7 @@ static void Cmd_adjustdamage(void)
gLastUsedItem = gBattleMons[gBattlerTarget].item;
gSpecialStatuses[gBattlerTarget].focusBanded = FALSE;
gSpecialStatuses[gBattlerTarget].focusSashed = FALSE;
}
else if (gSpecialStatuses[gBattlerTarget].sturdied)
{
@ -3172,8 +3172,8 @@ void SetMoveEffect(bool32 primary, bool32 certain)
{
gBattleMons[gEffectBattler].status2 |= sStatusFlagsForMoveEffects[gBattleScripting.moveEffect];
gBattlescriptCurrInstr++;
}
else
}
else
{
gBattlescriptCurrInstr++;
}
@ -6243,7 +6243,7 @@ static void Cmd_moveend(void)
break;
}
}
if (!(gBattleStruct->lastMoveFailed & gBitTable[gBattlerAttacker]
|| (!gSpecialStatuses[gBattlerAttacker].dancerUsedMove
&& gBattleStruct->bouncedMoveIsUsed)))
@ -6341,9 +6341,9 @@ static void Cmd_moveend(void)
&& (gMoveResultFlags & MOVE_RESULT_NO_EFFECT) // And it is unusable
&& (gBattleMons[gBattlerAttacker].status2 & STATUS2_LOCK_CONFUSE) != STATUS2_LOCK_CONFUSE_TURN(1)) // And won't end this turn
CancelMultiTurnMoves(gBattlerAttacker); // Cancel it
if (gBattleStruct->savedAttackerCount > 0)
{
// #if TESTING
@ -16845,8 +16845,8 @@ void BS_AllySwitchFailChance(void)
void BS_SetPhotonGeyserCategory(void)
{
NATIVE_ARGS();
if (!(gMovesInfo[gCurrentMove].effect == EFFECT_TERA_BLAST && !IsTerastallized(gBattlerAttacker))
&& !(gMovesInfo[gCurrentMove].effect == EFFECT_TERA_STARSTORM && gBattleMons[gBattlerAttacker].species != SPECIES_TERAPAGOS_STELLAR))
if (!((gMovesInfo[gCurrentMove].effect == EFFECT_TERA_BLAST && !IsTerastallized(gBattlerAttacker))
|| (gMovesInfo[gCurrentMove].effect == EFFECT_TERA_STARSTORM && !(IsTerastallized(gBattlerAttacker) && gBattleMons[gBattlerAttacker].species == SPECIES_TERAPAGOS_STELLAR))))
gBattleStruct->swapDamageCategory = (GetCategoryBasedOnStats(gBattlerAttacker) == DAMAGE_CATEGORY_PHYSICAL);
gBattlescriptCurrInstr = cmd->nextInstr;
}

View file

@ -25,7 +25,7 @@ void PrepareBattlerForTera(u32 battler)
gBattleStruct->tera.isTerastallized[side] |= gBitTable[index];
gBattleStruct->tera.alreadyTerastallized[battler] = TRUE;
// Remove Tera Orb charge.
// Remove Tera Orb charge.
if (B_FLAG_TERA_ORB_CHARGED != 0
&& (B_FLAG_TERA_ORB_NO_COST == 0 || !FlagGet(B_FLAG_TERA_ORB_NO_COST))
&& side == B_SIDE_PLAYER
@ -91,8 +91,7 @@ bool32 CanTerastallize(u32 battler)
// Returns a battler's Tera type.
u32 GetBattlerTeraType(u32 battler)
{
struct Pokemon *mon = &GetBattlerParty(battler)[gBattlerPartyIndexes[battler]];
return GetMonData(mon, MON_DATA_TERA_TYPE);
return GetMonData(&GetBattlerParty(battler)[gBattlerPartyIndexes[battler]], MON_DATA_TERA_TYPE);
}
// Returns whether a battler is terastallized.
@ -128,7 +127,7 @@ uq4_12_t GetTeraMultiplier(u32 battler, u32 type)
// Safety check.
if (!IsTerastallized(battler))
return UQ_4_12(1.0);
// Stellar-type checks.
if (teraType == TYPE_STELLAR)
{

View file

@ -1568,7 +1568,7 @@ void CreateFacilityMon(const struct TrainerMon *fmon, u16 level, u8 fixedIV, u32
u8 ball = (fmon->ball == 0xFF) ? Random() % POKEBALL_COUNT : fmon->ball;
u16 move;
u32 personality, ability, friendship, j;
if (fmon->gender == TRAINER_MON_MALE)
{
personality = GeneratePersonalityForGender(MON_MALE, fmon->species);
@ -1577,10 +1577,10 @@ void CreateFacilityMon(const struct TrainerMon *fmon, u16 level, u8 fixedIV, u32
{
personality = GeneratePersonalityForGender(MON_FEMALE, fmon->species);
}
ModifyPersonalityForNature(&personality, fmon->nature);
CreateMon(dst, fmon->species, level, fixedIV, TRUE, personality, otID, OT_ID_PRESET);
friendship = MAX_FRIENDSHIP;
// Give the chosen Pokémon its specified moves.
for (j = 0; j < MAX_MON_MOVES; j++)
@ -1588,7 +1588,7 @@ void CreateFacilityMon(const struct TrainerMon *fmon, u16 level, u8 fixedIV, u32
move = fmon->moves[j];
if (flags & FLAG_FRONTIER_MON_FACTORY && move == MOVE_RETURN)
move = MOVE_FRUSTRATION;
SetMonMoveSlot(dst, move, j);
if (gMovesInfo[move].effect == EFFECT_FRUSTRATION)
friendship = 0; // Frustration is more powerful the lower the pokemon's friendship is.
@ -1596,7 +1596,7 @@ void CreateFacilityMon(const struct TrainerMon *fmon, u16 level, u8 fixedIV, u32
SetMonData(dst, MON_DATA_FRIENDSHIP, &friendship);
SetMonData(dst, MON_DATA_HELD_ITEM, &fmon->heldItem);
// try to set ability. Otherwise, random of non-hidden as per vanilla
if (fmon->ability != ABILITY_NONE)
{
@ -1611,7 +1611,7 @@ void CreateFacilityMon(const struct TrainerMon *fmon, u16 level, u8 fixedIV, u32
ability = 0;
SetMonData(dst, MON_DATA_ABILITY_NUM, &ability);
}
if (fmon->ev != NULL)
{
SetMonData(dst, MON_DATA_HP_EV, &(fmon->ev[0]));
@ -1621,10 +1621,10 @@ void CreateFacilityMon(const struct TrainerMon *fmon, u16 level, u8 fixedIV, u32
SetMonData(dst, MON_DATA_SPDEF_EV, &(fmon->ev[4]));
SetMonData(dst, MON_DATA_SPEED_EV, &(fmon->ev[5]));
}
if (fmon->iv)
SetMonData(dst, MON_DATA_IVS, &(fmon->iv));
if (fmon->isShiny)
{
u32 data = TRUE;
@ -1640,8 +1640,13 @@ void CreateFacilityMon(const struct TrainerMon *fmon, u16 level, u8 fixedIV, u32
u32 data = fmon->gigantamaxFactor;
SetMonData(dst, MON_DATA_GIGANTAMAX_FACTOR, &data);
}
if (fmon->teraType)
{
u32 data = fmon->teraType;
SetMonData(dst, MON_DATA_TERA_TYPE, &data);
}
SetMonData(dst, MON_DATA_POKEBALL, &ball);
CalculateMonStats(dst);
}
@ -1743,7 +1748,7 @@ static void FillTrainerParty(u16 trainerId, u8 firstMonId, u8 monCount)
continue;
chosenMonIndices[i] = monId;
// Place the chosen Pokémon into the trainer's party.
CreateFacilityMon(&gFacilityTrainerMons[monId], level, fixedIV, otID, 0, &gEnemyParty[i + firstMonId]);
@ -2032,7 +2037,7 @@ void DoSpecialTrainerBattle(void)
BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_SECRET_BASE));
break;
case SPECIAL_BATTLE_EREADER:
#if FREE_BATTLE_TOWER_E_READER == FALSE
#if FREE_BATTLE_TOWER_E_READER == FALSE
ZeroEnemyPartyMons();
for (i = 0; i < (int)ARRAY_COUNT(gSaveBlock2Ptr->frontier.ereaderTrainer.party); i++)
CreateBattleTowerMon(&gEnemyParty[i], &gSaveBlock2Ptr->frontier.ereaderTrainer.party[i]);
@ -3095,7 +3100,7 @@ static void FillPartnerParty(u16 trainerId)
for (i = 0; i < FRONTIER_MULTI_PARTY_SIZE; i++)
{
monId = gSaveBlock2Ptr->frontier.trainerIds[i + 18];
CreateFacilityMon(&gFacilityTrainerMons[monId], level, ivs, otID, 0, &gPlayerParty[MULTI_PARTY_SIZE + i]);
CreateFacilityMon(&gFacilityTrainerMons[monId], level, ivs, otID, 0, &gPlayerParty[MULTI_PARTY_SIZE + i]);
for (j = 0; j < PLAYER_NAME_LENGTH + 1; j++)
trainerName[j] = gFacilityTrainers[trainerId].trainerName[j];
SetMonData(&gPlayerParty[MULTI_PARTY_SIZE + i], MON_DATA_OT_NAME, &trainerName);

View file

@ -10595,6 +10595,14 @@ bool32 IsBattlerUltraBursted(u32 battler)
return (gSpeciesInfo[gBattleMons[battler].species].isUltraBurst);
}
bool32 IsBattlerInTeraForm(u32 battler)
{
// While Transform does copy stats and visuals, it shouldn't be counted as a true Tera Form.
if (gBattleMons[battler].status2 & STATUS2_TRANSFORMED)
return FALSE;
return (gSpeciesInfo[gBattleMons[battler].species].isTeraForm);
}
// Returns SPECIES_NONE if no form change is possible
u16 GetBattleFormChangeTargetSpecies(u32 battler, u16 method)
{
@ -10685,6 +10693,10 @@ u16 GetBattleFormChangeTargetSpecies(u32 battler, u16 method)
if (gBattleMons[battler].status1 & formChanges[i].param1)
targetSpecies = formChanges[i].targetSpecies;
break;
case FORM_CHANGE_BATTLE_TERASTALLIZATION:
if (GetBattlerTeraType(battler) == formChanges[i].param1)
targetSpecies = formChanges[i].targetSpecies;
break;
}
}
}
@ -10700,7 +10712,7 @@ bool32 CanBattlerFormChange(u32 battler, u16 method)
&& B_TRANSFORM_FORM_CHANGES >= GEN_5)
return FALSE;
// Mega Evolved and Ultra Bursted Pokémon should always revert to normal upon fainting or ending the battle.
if ((IsBattlerMegaEvolved(battler) || IsBattlerUltraBursted(battler)) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_END_BATTLE))
if ((IsBattlerMegaEvolved(battler) || IsBattlerUltraBursted(battler) || IsBattlerInTeraForm(battler)) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_END_BATTLE))
return TRUE;
else if (IsBattlerPrimalReverted(battler) && (method == FORM_CHANGE_END_BATTLE))
return TRUE;
@ -10740,7 +10752,7 @@ bool32 TryBattleFormChange(u32 battler, u16 method)
bool32 restoreSpecies = FALSE;
// Mega Evolved and Ultra Bursted Pokémon should always revert to normal upon fainting or ending the battle, so no need to add it to the form change tables.
if ((IsBattlerMegaEvolved(battler) || IsBattlerUltraBursted(battler)) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_END_BATTLE))
if ((IsBattlerMegaEvolved(battler) || IsBattlerUltraBursted(battler) || IsBattlerInTeraForm(battler)) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_END_BATTLE))
restoreSpecies = TRUE;
// Unlike Megas, Primal Reversion isn't canceled on fainting.

View file

@ -1259,10 +1259,16 @@ static const struct FormChange sPalafinZeroFormChangeTable[] =
#if P_FAMILY_OGERPON
static const struct FormChange sOgerponFormChangeTable[] = {
{FORM_CHANGE_ITEM_HOLD, SPECIES_OGERPON_TEAL_MASK, ITEM_NONE},
{FORM_CHANGE_ITEM_HOLD, SPECIES_OGERPON_WELLSPRING_MASK, ITEM_WELLSPRING_MASK},
{FORM_CHANGE_ITEM_HOLD, SPECIES_OGERPON_HEARTHFLAME_MASK, ITEM_HEARTHFLAME_MASK},
{FORM_CHANGE_ITEM_HOLD, SPECIES_OGERPON_CORNERSTONE_MASK, ITEM_CORNERSTONE_MASK},
{FORM_CHANGE_ITEM_HOLD, SPECIES_OGERPON_TEAL_MASK, ITEM_NONE},
{FORM_CHANGE_ITEM_HOLD, SPECIES_OGERPON_WELLSPRING_MASK, ITEM_WELLSPRING_MASK},
{FORM_CHANGE_ITEM_HOLD, SPECIES_OGERPON_HEARTHFLAME_MASK, ITEM_HEARTHFLAME_MASK},
{FORM_CHANGE_ITEM_HOLD, SPECIES_OGERPON_CORNERSTONE_MASK, ITEM_CORNERSTONE_MASK},
#if P_TERA_FORMS
{FORM_CHANGE_BATTLE_TERASTALLIZATION, SPECIES_OGERPON_TEAL_MASK_TERA, TYPE_GRASS},
{FORM_CHANGE_BATTLE_TERASTALLIZATION, SPECIES_OGERPON_WELLSPRING_MASK_TERA, TYPE_WATER},
{FORM_CHANGE_BATTLE_TERASTALLIZATION, SPECIES_OGERPON_HEARTHFLAME_MASK_TERA, TYPE_FIRE},
{FORM_CHANGE_BATTLE_TERASTALLIZATION, SPECIES_OGERPON_CORNERSTONE_MASK_TERA, TYPE_ROCK},
#endif
{FORM_CHANGE_TERMINATOR},
};
#endif //P_FAMILY_OGERPON
@ -1271,9 +1277,9 @@ static const struct FormChange sOgerponFormChangeTable[] = {
static const struct FormChange sTerapagosFormChangeTable[] = {
{FORM_CHANGE_BATTLE_SWITCH, SPECIES_TERAPAGOS_TERASTAL, ABILITY_TERA_SHIFT},
#if P_TERA_FORMS
//{FORM_CHANGE_TERASTALLIZATION, SPECIES_TERAPAGOS_STELLAR},
{FORM_CHANGE_BATTLE_TERASTALLIZATION, SPECIES_TERAPAGOS_STELLAR, TYPE_STELLAR},
#endif
{FORM_CHANGE_END_BATTLE, SPECIES_TERAPAGOS_NORMAL},
{FORM_CHANGE_END_BATTLE, SPECIES_TERAPAGOS_NORMAL},
{FORM_CHANGE_TERMINATOR},
};
#endif //P_FAMILY_TERAPAGOS

View file

@ -2161,6 +2161,12 @@ static const u16 sOgerponFormSpeciesIdTable[] = {
SPECIES_OGERPON_WELLSPRING_MASK,
SPECIES_OGERPON_HEARTHFLAME_MASK,
SPECIES_OGERPON_CORNERSTONE_MASK,
#if P_TERA_FORMS
SPECIES_OGERPON_TEAL_MASK_TERA,
SPECIES_OGERPON_WELLSPRING_MASK_TERA,
SPECIES_OGERPON_HEARTHFLAME_MASK_TERA,
SPECIES_OGERPON_CORNERSTONE_MASK_TERA,
#endif
FORM_SPECIES_END,
};
#endif //P_FAMILY_OGERPON

View file

@ -6301,7 +6301,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
#endif //P_FAMILY_FEZANDIPITI
#if P_FAMILY_OGERPON
#define OGERPON_SPECIES_INFO(Form, type, ability, color, iconpalette) \
#define OGERPON_SPECIES_INFO(Form, type, ability, color, iconpalette, isTeraform) \
{ \
.baseHP = 80, \
.baseAttack = 120, \
@ -6310,6 +6310,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
.baseSpAttack = 60, \
.baseSpDefense = 96, \
.types = MON_TYPES(TYPE_GRASS, type), \
.forceTeraType = type, \
.catchRate = 5, \
.expYield = 275, \
.evYield_Attack = 3, \
@ -6350,17 +6351,18 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
.formSpeciesIdTable = sOgerponFormSpeciesIdTable, \
.formChangeTable = sOgerponFormChangeTable, \
.isLegendary = TRUE, \
.isTeraForm = isTeraform, \
}
[SPECIES_OGERPON_TEAL_MASK] = OGERPON_SPECIES_INFO(TealMask, TYPE_GRASS, ABILITY_DEFIANT, BODY_COLOR_GREEN, 1),
[SPECIES_OGERPON_WELLSPRING_MASK] = OGERPON_SPECIES_INFO(WellspringMask, TYPE_WATER, ABILITY_WATER_ABSORB, BODY_COLOR_BLUE, 0),
[SPECIES_OGERPON_HEARTHFLAME_MASK] = OGERPON_SPECIES_INFO(HearthflameMask, TYPE_FIRE, ABILITY_MOLD_BREAKER, BODY_COLOR_RED, 0),
[SPECIES_OGERPON_CORNERSTONE_MASK] = OGERPON_SPECIES_INFO(CornerstoneMask, TYPE_ROCK, ABILITY_STURDY, BODY_COLOR_GRAY, 0),
[SPECIES_OGERPON_TEAL_MASK] = OGERPON_SPECIES_INFO(TealMask, TYPE_GRASS, ABILITY_DEFIANT, BODY_COLOR_GREEN, 1, FALSE),
[SPECIES_OGERPON_WELLSPRING_MASK] = OGERPON_SPECIES_INFO(WellspringMask, TYPE_WATER, ABILITY_WATER_ABSORB, BODY_COLOR_BLUE, 0, FALSE),
[SPECIES_OGERPON_HEARTHFLAME_MASK] = OGERPON_SPECIES_INFO(HearthflameMask, TYPE_FIRE, ABILITY_MOLD_BREAKER, BODY_COLOR_RED, 0, FALSE),
[SPECIES_OGERPON_CORNERSTONE_MASK] = OGERPON_SPECIES_INFO(CornerstoneMask, TYPE_ROCK, ABILITY_STURDY, BODY_COLOR_GRAY, 0, FALSE),
#if P_TERA_FORMS
[SPECIES_OGERPON_TEAL_MASK_TERA] = OGERPON_SPECIES_INFO(TealMask, TYPE_GRASS, ABILITY_EMBODY_ASPECT_TEAL_MASK, BODY_COLOR_GREEN, 1),
[SPECIES_OGERPON_WELLSPRING_MASK_TERA] = OGERPON_SPECIES_INFO(WellspringMask, TYPE_WATER, ABILITY_EMBODY_ASPECT_WELLSPRING_MASK, BODY_COLOR_BLUE, 0),
[SPECIES_OGERPON_HEARTHFLAME_MASK_TERA] = OGERPON_SPECIES_INFO(HearthflameMask, TYPE_FIRE, ABILITY_EMBODY_ASPECT_HEARTHFLAME_MASK, BODY_COLOR_RED, 0),
[SPECIES_OGERPON_CORNERSTONE_MASK_TERA] = OGERPON_SPECIES_INFO(CornerstoneMask, TYPE_ROCK, ABILITY_EMBODY_ASPECT_CORNERSTONE_MASK, BODY_COLOR_GRAY, 0),
[SPECIES_OGERPON_TEAL_MASK_TERA] = OGERPON_SPECIES_INFO(TealMask, TYPE_GRASS, ABILITY_EMBODY_ASPECT_TEAL_MASK, BODY_COLOR_GREEN, 1, TRUE),
[SPECIES_OGERPON_WELLSPRING_MASK_TERA] = OGERPON_SPECIES_INFO(WellspringMask, TYPE_WATER, ABILITY_EMBODY_ASPECT_WELLSPRING_MASK, BODY_COLOR_BLUE, 0, TRUE),
[SPECIES_OGERPON_HEARTHFLAME_MASK_TERA] = OGERPON_SPECIES_INFO(HearthflameMask, TYPE_FIRE, ABILITY_EMBODY_ASPECT_HEARTHFLAME_MASK, BODY_COLOR_RED, 0, TRUE),
[SPECIES_OGERPON_CORNERSTONE_MASK_TERA] = OGERPON_SPECIES_INFO(CornerstoneMask, TYPE_ROCK, ABILITY_EMBODY_ASPECT_CORNERSTONE_MASK, BODY_COLOR_GRAY, 0, TRUE),
#endif //P_TERA_FORMS
#endif //P_FAMILY_OGERPON
@ -6594,6 +6596,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
.baseSpAttack = 65,
.baseSpDefense = 85,
.types = MON_TYPES(TYPE_NORMAL),
.forceTeraType = TYPE_STELLAR,
.catchRate = 255,
.expYield = 90,
.evYield_Defense = 1,
@ -6651,6 +6654,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
.baseSpAttack = 105,
.baseSpDefense = 110,
.types = MON_TYPES(TYPE_NORMAL),
.forceTeraType = TYPE_STELLAR,
.catchRate = 255,
.expYield = 120,
.evYield_Defense = 2,
@ -6709,6 +6713,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
.baseSpAttack = 130,
.baseSpDefense = 110,
.types = MON_TYPES(TYPE_NORMAL),
.forceTeraType = TYPE_STELLAR,
.catchRate = 255,
.expYield = 140,
.evYield_HP = 3,
@ -6749,6 +6754,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
.iconPalIndex = 0,
FOOTPRINT(TerapagosStellar)
.isLegendary = TRUE,
.isTeraForm = TRUE,
.isFrontierBanned = TRUE,
.levelUpLearnset = sTerapagosLevelUpLearnset,
.teachableLearnset = sTerapagosTeachableLearnset,

View file

@ -2773,8 +2773,11 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data)
retVal = substruct3->gigantamaxFactor;
break;
case MON_DATA_TERA_TYPE:
{
if (substruct0->teraType == TYPE_NONE)
if (gSpeciesInfo[substruct0->species].forceTeraType)
{
retVal = gSpeciesInfo[substruct0->species].forceTeraType;
}
else if (substruct0->teraType == TYPE_NONE) // Tera Type hasn't been modified so we can just use the personality
{
const u8 *types = gSpeciesInfo[substruct0->species].types;
retVal = (boxMon->personality & 0x1) == 0 ? types[0] : types[1];
@ -2784,7 +2787,6 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data)
retVal = substruct0->teraType;
}
break;
}
case MON_DATA_EVOLUTION_TRACKER:
evoTracker.asField.a = substruct1->evolutionTracker1;
evoTracker.asField.b = substruct1->evolutionTracker2;

View file

@ -686,7 +686,7 @@ SINGLE_BATTLE_TEST("(TERA) Protean cannot change the type of a Terastallized Pok
PLAYER(SPECIES_GRENINJA) { Ability(ABILITY_PROTEAN); TeraType(TYPE_GRASS); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_BUBBLE, tera: TRUE);
TURN { MOVE(player, MOVE_BUBBLE, tera: TRUE);
MOVE(opponent, MOVE_EMBER); }
} SCENE {
MESSAGE("Greninja used Bubble!");
@ -793,3 +793,21 @@ SINGLE_BATTLE_TEST("(TERA) All type indicators function correctly")
TURN { MOVE(player, MOVE_CELEBRATE, tera: TRUE); }
}
}
SINGLE_BATTLE_TEST("(TERA) Pokemon with Tera forms change upon Terastallizing")
{
u32 species, targetSpecies;
PARAMETRIZE { species = SPECIES_OGERPON_TEAL_MASK; targetSpecies = SPECIES_OGERPON_TEAL_MASK_TERA; }
PARAMETRIZE { species = SPECIES_OGERPON_WELLSPRING_MASK; targetSpecies = SPECIES_OGERPON_WELLSPRING_MASK_TERA; }
PARAMETRIZE { species = SPECIES_OGERPON_HEARTHFLAME_MASK; targetSpecies = SPECIES_OGERPON_HEARTHFLAME_MASK_TERA; }
PARAMETRIZE { species = SPECIES_OGERPON_CORNERSTONE_MASK; targetSpecies = SPECIES_OGERPON_CORNERSTONE_MASK_TERA; }
PARAMETRIZE { species = SPECIES_TERAPAGOS_TERASTAL; targetSpecies = SPECIES_TERAPAGOS_STELLAR; }
GIVEN {
PLAYER(species);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, tera: TRUE); }
} THEN {
EXPECT_EQ(player->species, targetSpecies);
}
}

View file

@ -0,0 +1,61 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(gMovesInfo[MOVE_TERA_STARSTORM].effect == EFFECT_TERA_STARSTORM);
}
SINGLE_BATTLE_TEST("Tera Starstorm changes from Normal-type to Stellar-type if used by Terapagos-Stellar")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_TERA_STARSTORM].type == TYPE_NORMAL);
PLAYER(SPECIES_TERAPAGOS_STELLAR);
OPPONENT(SPECIES_MISDREAVUS);
} WHEN {
TURN { MOVE(player, MOVE_TERA_STARSTORM); }
} SCENE {
MESSAGE("Terapagos used Tera Starstorm!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_STARSTORM, player);
HP_BAR(opponent);
NOT { MESSAGE("It doesn't affect Foe Misdreavus…"); }
}
}
DOUBLE_BATTLE_TEST("Tera Starstorm targets both opponents in a double battle if used by Terapagos-Stellar")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_TERA_STARSTORM].target == MOVE_TARGET_SELECTED);
PLAYER(SPECIES_TERAPAGOS_STELLAR);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(playerLeft, MOVE_TERA_STARSTORM, target:opponentLeft); }
} SCENE {
MESSAGE("Terapagos used Tera Starstorm!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_STARSTORM, playerLeft);
HP_BAR(opponentLeft);
HP_BAR(opponentRight);
}
}
SINGLE_BATTLE_TEST("Tera Starstorm becomes a physical move if the user is Terapagos-Stellar, is Terastallized, and has a higher Attack stat", s16 damage)
{
bool32 tera;
PARAMETRIZE { tera = FALSE; }
PARAMETRIZE { tera = TRUE; }
GIVEN {
ASSUME(gMovesInfo[MOVE_TERA_STARSTORM].category == DAMAGE_CATEGORY_SPECIAL);
PLAYER(SPECIES_TERAPAGOS_STELLAR) { Attack(100); SpAttack(50); }
OPPONENT(SPECIES_WOBBUFFET) { Defense(200); SpDefense(200); }
} WHEN {
TURN { MOVE(player, MOVE_TERA_STARSTORM, tera: tera); }
} SCENE {
MESSAGE("Terapagos used Tera Starstorm!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_STARSTORM, player);
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, UQ_4_12(2.5), results[1].damage);
}
}