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:
parent
167cd7d0f7
commit
7f8f480ecb
16 changed files with 193 additions and 51 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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[];
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
61
test/battle/move_effect/tera_starstorm.c
Normal file
61
test/battle/move_effect/tera_starstorm.c
Normal 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);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue