From 97c1d312dc8882c25a415a0984a839c4ca5f372d Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Wed, 24 Apr 2024 20:09:45 -0500 Subject: [PATCH 01/13] Add FORM_CHANGE_BATTLE_TERASTALLIZATION and allow species to force tera types --- data/battle_scripts_1.s | 15 +++++++++ include/battle_scripts.h | 1 + include/constants/form_change_types.h | 4 +++ include/pokemon.h | 6 ++-- src/battle_main.c | 7 +++-- src/battle_tower.c | 31 +++++++++++-------- src/battle_util.c | 16 ++++++++-- src/data/pokemon/form_change_tables.h | 20 +++++++----- .../pokemon/species_info/gen_9_families.h | 24 ++++++++------ src/pokemon.c | 13 ++++++-- 10 files changed, 99 insertions(+), 38 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 9da79c38d877..516c5d44a0fa 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -28,6 +28,21 @@ BattleScript_Terastallization:: waitanimation end3 +BattleScript_TeraFormChange:: + @ TODO: no string prints in S/V, but right now this helps with clarity + printstring STRINGID_PKMNTERASTALLIZEDINTO + @ TODO: replace this animation + playanimation BS_ATTACKER, B_ANIM_TOTEM_FLARE + waitanimation + handleformchange BS_ATTACKER, 0 + handleformchange BS_ATTACKER, 1 + playanimation BS_ATTACKER, B_ANIM_FORM_CHANGE + waitanimation + copybyte sBATTLER, gBattlerAttacker + handleformchange BS_ATTACKER, 2 + 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 diff --git a/include/battle_scripts.h b/include/battle_scripts.h index e4e9ca430716..c94741d4d27f 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -502,6 +502,7 @@ extern const u8 BattleScript_AromaVeilProtectsRet[]; extern const u8 BattleScript_LowerAtkSpAtk[]; extern const u8 BattleScript_Terastallization[]; extern const u8 BattleScript_BoosterEnergyEnd2[]; +extern const u8 BattleScript_TeraFormChange[]; // zmoves extern const u8 BattleScript_ZMoveActivateDamaging[]; diff --git a/include/constants/form_change_types.h b/include/constants/form_change_types.h index 51325baf25fb..8f7b475756de 100644 --- a/include/constants/form_change_types.h +++ b/include/constants/form_change_types.h @@ -120,4 +120,8 @@ // param1: status #define FORM_CHANGE_STATUS 20 +// Form change that activates when terastallized as as a specific type +// param1: tera type +#define FORM_CHANGE_BATTLE_TERASTALLIZATION 21 + #endif // GUARD_CONSTANTS_FORM_CHANGE_TYPES_H diff --git a/include/pokemon.h b/include/pokemon.h index d37e5f4ae946..49d4c5ae35db 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -359,7 +359,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; @@ -377,6 +377,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]; @@ -430,6 +431,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; @@ -438,7 +440,7 @@ struct SpeciesInfo /*0x8C*/ u32 allPerfectIVs:1; u32 dexForceRequired:1; // This species will be taken into account for Pokédex ratings even if they have the "isMythical" flag set. u32 tmIlliterate:1; // This species will be unable to learn the universal moves. - u32 padding4:15; + u32 padding4:14; // Move Data /* 0x80 */ const struct LevelUpMove *levelUpLearnset; /* 0x84 */ const u16 *teachableLearnset; diff --git a/src/battle_main.c b/src/battle_main.c index eba4916ef468..d2225ff76bec 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -5335,7 +5335,7 @@ static void PopulateArrayWithBattlers(u8 *battlers) static bool32 TryDoGimmicksBeforeMoves(void) { if (!(gHitMarker & HITMARKER_RUN) - && (gBattleStruct->mega.toEvolve || gBattleStruct->burst.toBurst + && (gBattleStruct->mega.toEvolve || gBattleStruct->burst.toBurst || gBattleStruct->dynamax.toDynamax || gBattleStruct->tera.toTera)) { u32 i, battler; @@ -5353,7 +5353,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 diff --git a/src/battle_tower.c b/src/battle_tower.c index 59e9a8aeaf96..fd0641cc6f1c 100644 --- a/src/battle_tower.c +++ b/src/battle_tower.c @@ -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); diff --git a/src/battle_util.c b/src/battle_util.c index e11ddc27eaf4..f10a1cd11bb7 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -10506,6 +10506,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) { @@ -10594,6 +10602,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; } } } @@ -10609,7 +10621,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; @@ -10649,7 +10661,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. diff --git a/src/data/pokemon/form_change_tables.h b/src/data/pokemon/form_change_tables.h index 5edd6f42b55c..1a5743c87438 100644 --- a/src/data/pokemon/form_change_tables.h +++ b/src/data/pokemon/form_change_tables.h @@ -1258,21 +1258,27 @@ 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 #if P_FAMILY_TERAPAGOS static const struct FormChange sTerapagosFormChangeTable[] = { - {FORM_CHANGE_BEGIN_BATTLE, SPECIES_TERAPAGOS_TERASTAL}, //needs to be tied to the ability + {FORM_CHANGE_BEGIN_BATTLE, SPECIES_TERAPAGOS_TERASTAL}, //needs to be tied to the ability #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 diff --git a/src/data/pokemon/species_info/gen_9_families.h b/src/data/pokemon/species_info/gen_9_families.h index 304f0560a20b..12281b33efa8 100644 --- a/src/data/pokemon/species_info/gen_9_families.h +++ b/src/data/pokemon/species_info/gen_9_families.h @@ -6256,7 +6256,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, \ @@ -6265,6 +6265,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .baseSpAttack = 60, \ .baseSpDefense = 96, \ .types = MON_TYPES(TYPE_GRASS, type), \ + .forceTeraType = type, \ .catchRate = 5, \ .expYield = 275, \ .evYield_Attack = 3, \ @@ -6305,17 +6306,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, BODY_COLOR_GREEN, 1), - [SPECIES_OGERPON_WELLSPRING_MASK_TERA] = OGERPON_SPECIES_INFO(WellspringMask, TYPE_WATER, ABILITY_EMBODY_ASPECT_WELLSPRING, BODY_COLOR_BLUE, 0), - [SPECIES_OGERPON_HEARTHFLAME_MASK_TERA] = OGERPON_SPECIES_INFO(HearthflameMask, TYPE_FIRE, ABILITY_EMBODY_ASPECT_HEARTHFLAME, BODY_COLOR_RED, 0), - [SPECIES_OGERPON_CORNERSTONE_MASK_TERA] = OGERPON_SPECIES_INFO(CornerstoneMask, TYPE_ROCK, ABILITY_EMBODY_ASPECT_CORNERSTONE, BODY_COLOR_GRAY, 0), + [SPECIES_OGERPON_TEAL_MASK_TERA] = OGERPON_SPECIES_INFO(TealMask, TYPE_GRASS, ABILITY_EMBODY_ASPECT_TEAL, BODY_COLOR_GREEN, 1, TRUE), + [SPECIES_OGERPON_WELLSPRING_MASK_TERA] = OGERPON_SPECIES_INFO(WellspringMask, TYPE_WATER, ABILITY_EMBODY_ASPECT_WELLSPRING, BODY_COLOR_BLUE, 0, TRUE), + [SPECIES_OGERPON_HEARTHFLAME_MASK_TERA] = OGERPON_SPECIES_INFO(HearthflameMask, TYPE_FIRE, ABILITY_EMBODY_ASPECT_HEARTHFLAME, BODY_COLOR_RED, 0, TRUE), + [SPECIES_OGERPON_CORNERSTONE_MASK_TERA] = OGERPON_SPECIES_INFO(CornerstoneMask, TYPE_ROCK, ABILITY_EMBODY_ASPECT_CORNERSTONE, BODY_COLOR_GRAY, 0, TRUE), #endif //P_TERA_FORMS #endif //P_FAMILY_OGERPON @@ -6549,6 +6551,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .baseSpAttack = 65, .baseSpDefense = 85, .types = MON_TYPES(TYPE_NORMAL), + .forceTeraType = TYPE_STELLAR, .catchRate = 255, .expYield = 90, .evYield_Defense = 1, @@ -6604,6 +6607,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .baseSpAttack = 105, .baseSpDefense = 110, .types = MON_TYPES(TYPE_NORMAL), + .forceTeraType = TYPE_STELLAR, .catchRate = 255, .expYield = 120, .evYield_Defense = 2, @@ -6660,6 +6664,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .baseSpAttack = 130, .baseSpDefense = 110, .types = MON_TYPES(TYPE_NORMAL), + .forceTeraType = TYPE_STELLAR, .catchRate = 255, .expYield = 140, .evYield_HP = 3, @@ -6700,6 +6705,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .iconPalIndex = 0, //FOOTPRINT(Terapagos) .isLegendary = TRUE, + .isTeraForm = TRUE, .levelUpLearnset = sTerapagosLevelUpLearnset, .teachableLearnset = sTerapagosTeachableLearnset, .formSpeciesIdTable = sTerapagosFormSpeciesIdTable, diff --git a/src/pokemon.c b/src/pokemon.c index 521e54cbbd49..96e32d97e85f 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -1726,7 +1726,7 @@ void GiveBoxMonInitialMoveset_Fast(struct BoxPokemon *boxMon) //Credit: Asparagu alreadyKnown = TRUE; break; } - + if (!alreadyKnown) { if (addedMoves < MAX_MON_MOVES) @@ -2540,8 +2540,15 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) { if (substruct0->teraType == 0) { - const u8 *types = gSpeciesInfo[substruct0->species].types; - retVal = (boxMon->personality & 0x1) == 0 ? types[0] : types[1]; + if (gSpeciesInfo[substruct0->species].forceTeraType) + { + retVal = gSpeciesInfo[substruct0->species].forceTeraType; + } + else + { + const u8 *types = gSpeciesInfo[substruct0->species].types; + retVal = (boxMon->personality & 0x1) == 0 ? types[0] : types[1]; + } } else { From 46b45a9f08ba03e23de422c0154f77d38f051433 Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Wed, 24 Apr 2024 21:05:51 -0500 Subject: [PATCH 02/13] Fix form change not changing tera type --- include/config/pokemon.h | 2 +- src/pokemon.c | 31 +++++++++++++++---------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/include/config/pokemon.h b/include/config/pokemon.h index 32415e5247ba..1b818ba2e448 100644 --- a/include/config/pokemon.h +++ b/include/config/pokemon.h @@ -43,7 +43,7 @@ #define P_FOOTPRINTS TRUE // If TRUE, Pokémon will have footprints (as was the case up to Gen 5 and in BDSP). Disabling this saves some ROM space. #define P_LEGENDARY_PERFECT_IVS GEN_LATEST // Since Gen 6, Legendaries, Mythicals and Ultra Beasts found in the wild or given through gifts have at least 3 perfect IVs. #define P_EV_CAP GEN_LATEST // Since Gen 6, the max EVs per stat is 252 instead of 255. -#define P_SHOW_TERA_TYPE GEN_8 // Since Gen 9, the Tera Type is shown on the summary screen. +#define P_SHOW_TERA_TYPE GEN_LATEST // Since Gen 9, the Tera Type is shown on the summary screen. #define P_TM_LITERACY GEN_LATEST // Since Gen 6, TM illiterate Pokémon can learn TMs that teach moves that are in their level-up learnsets. // Learnset helper toggles diff --git a/src/pokemon.c b/src/pokemon.c index 96e32d97e85f..668019cce2d1 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -1020,6 +1020,15 @@ void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, SetBoxMonData(boxMon, MON_DATA_ABILITY_NUM, &value); } + if (gSpeciesInfo[species].forceTeraType) + { + SetBoxMonData(boxMon, MON_DATA_TERA_TYPE, &(gSpeciesInfo[species].forceTeraType)); + } + else + { + SetBoxMonData(boxMon, MON_DATA_TERA_TYPE, ((personality & 0x1) == 0 ? &(gSpeciesInfo[species].types[0]) : &(gSpeciesInfo[species].types[1]))); + } + GiveBoxMonInitialMoveset(boxMon); } @@ -1569,6 +1578,11 @@ void CalculateMonStats(struct Pokemon *mon) } SetMonData(mon, MON_DATA_HP, ¤tHP); + + if (gSpeciesInfo[species].forceTeraType) // only change tera type here if the tera type is forced, otherwise it can stay the same + { + SetMonData(mon, MON_DATA_TERA_TYPE, &gSpeciesInfo[species].forceTeraType); + } } void BoxMonToMon(const struct BoxPokemon *src, struct Pokemon *dest) @@ -2538,22 +2552,7 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) break; case MON_DATA_TERA_TYPE: { - if (substruct0->teraType == 0) - { - if (gSpeciesInfo[substruct0->species].forceTeraType) - { - retVal = gSpeciesInfo[substruct0->species].forceTeraType; - } - else - { - const u8 *types = gSpeciesInfo[substruct0->species].types; - retVal = (boxMon->personality & 0x1) == 0 ? types[0] : types[1]; - } - } - else - { - retVal = substruct0->teraType - 1; - } + retVal = substruct0->teraType - 1; break; } case MON_DATA_EVOLUTION_TRACKER: From f4ea3247ebf74cc318c0f259dda4a82f28506789 Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Wed, 24 Apr 2024 21:28:28 -0500 Subject: [PATCH 03/13] Update form_species_tables.h --- src/data/pokemon/form_species_tables.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/data/pokemon/form_species_tables.h b/src/data/pokemon/form_species_tables.h index 9ae2c3ebdb89..f099f8665fd0 100644 --- a/src/data/pokemon/form_species_tables.h +++ b/src/data/pokemon/form_species_tables.h @@ -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 @@ -2169,7 +2175,7 @@ static const u16 sOgerponFormSpeciesIdTable[] = { static const u16 sTerapagosFormSpeciesIdTable[] = { SPECIES_TERAPAGOS_NORMAL, SPECIES_TERAPAGOS_TERASTAL, -#if P_TERA_FORMS +#if P_TERA_FORMS SPECIES_TERAPAGOS_STELLAR, #endif FORM_SPECIES_END, From 3b293b83270c4757ac6cfcb31621e073718dc13a Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Thu, 25 Apr 2024 01:22:04 -0500 Subject: [PATCH 04/13] Address reviews --- include/battle_terastal.h | 1 + src/battle_terastal.c | 23 +++++++++++++++---- src/data/pokemon/species_info.h | 1 + .../pokemon/species_info/gen_9_families.h | 4 ++-- src/pokemon.c | 14 ++++------- src/pokemon_summary_screen.c | 2 +- test/battle/trainer_control.h | 1 + 7 files changed, 29 insertions(+), 17 deletions(-) diff --git a/include/battle_terastal.h b/include/battle_terastal.h index 078bf39079fa..3c034fc874e0 100644 --- a/include/battle_terastal.h +++ b/include/battle_terastal.h @@ -3,6 +3,7 @@ void PrepareBattlerForTera(u32 battler); bool32 CanTerastallize(u32 battler); +u32 GetTeraType(struct Pokemon *mon); u32 GetBattlerTeraType(u32 battler); bool32 IsTerastallized(u32 battler); void ExpendTypeStellarBoost(u32 battler, u32 type); diff --git a/src/battle_terastal.c b/src/battle_terastal.c index 8cc03aca5716..3960cf52193f 100644 --- a/src/battle_terastal.c +++ b/src/battle_terastal.c @@ -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) @@ -79,11 +79,26 @@ bool32 CanTerastallize(u32 battler) return TRUE; } +// Returns a pokemon's Tera type. +u32 GetTeraType(struct Pokemon *mon) +{ + if (GetMonData(mon, MON_DATA_TERA_TYPE) + 1 == 0) // no tera type, so generate it + { + u16 species = GetMonData(mon, MON_DATA_SPECIES); + + if (gSpeciesInfo[species].forceTeraType != TYPE_NONE) + SetMonData(mon, MON_DATA_TERA_TYPE, &gSpeciesInfo[species].forceTeraType); + else + SetMonData(mon, MON_DATA_TERA_TYPE, ((GetMonData(mon, MON_DATA_PERSONALITY) & 0x1) == 0 ? &(gSpeciesInfo[species].types[0]) : &(gSpeciesInfo[species].types[1]))); + } + + return GetMonData(mon, MON_DATA_TERA_TYPE); +} + // 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 GetTeraType(&GetBattlerParty(battler)[gBattlerPartyIndexes[battler]]); } // Returns whether a battler is terastallized. @@ -119,7 +134,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) { diff --git a/src/data/pokemon/species_info.h b/src/data/pokemon/species_info.h index 63c5d752a88c..ee029df1b416 100644 --- a/src/data/pokemon/species_info.h +++ b/src/data/pokemon/species_info.h @@ -25,6 +25,7 @@ const struct SpeciesInfo gSpeciesInfo[] = { [SPECIES_NONE] = { + .forceTeraType = TYPE_NONE, .speciesName = _("??????????"), .cryId = CRY_NONE, .natDexNum = NATIONAL_DEX_NONE, diff --git a/src/data/pokemon/species_info/gen_9_families.h b/src/data/pokemon/species_info/gen_9_families.h index 12281b33efa8..afbb3a89303f 100644 --- a/src/data/pokemon/species_info/gen_9_families.h +++ b/src/data/pokemon/species_info/gen_9_families.h @@ -6256,7 +6256,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = #endif //P_FAMILY_FEZANDIPITI #if P_FAMILY_OGERPON -#define OGERPON_SPECIES_INFO(Form, type, ability, color, iconpalette, isteraform) \ +#define OGERPON_SPECIES_INFO(Form, type, ability, color, iconpalette, isTeraform) \ { \ .baseHP = 80, \ .baseAttack = 120, \ @@ -6306,7 +6306,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .formSpeciesIdTable = sOgerponFormSpeciesIdTable, \ .formChangeTable = sOgerponFormChangeTable, \ .isLegendary = TRUE, \ - .isTeraForm = isteraform, \ + .isTeraForm = isTeraform, \ } [SPECIES_OGERPON_TEAL_MASK] = OGERPON_SPECIES_INFO(TealMask, TYPE_GRASS, ABILITY_DEFIANT, BODY_COLOR_GREEN, 1, FALSE), diff --git a/src/pokemon.c b/src/pokemon.c index 668019cce2d1..56d5d43ca70c 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -1020,14 +1020,10 @@ void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, SetBoxMonData(boxMon, MON_DATA_ABILITY_NUM, &value); } - if (gSpeciesInfo[species].forceTeraType) - { + if (gSpeciesInfo[species].forceTeraType != TYPE_NONE) SetBoxMonData(boxMon, MON_DATA_TERA_TYPE, &(gSpeciesInfo[species].forceTeraType)); - } else - { SetBoxMonData(boxMon, MON_DATA_TERA_TYPE, ((personality & 0x1) == 0 ? &(gSpeciesInfo[species].types[0]) : &(gSpeciesInfo[species].types[1]))); - } GiveBoxMonInitialMoveset(boxMon); } @@ -1578,11 +1574,6 @@ void CalculateMonStats(struct Pokemon *mon) } SetMonData(mon, MON_DATA_HP, ¤tHP); - - if (gSpeciesInfo[species].forceTeraType) // only change tera type here if the tera type is forced, otherwise it can stay the same - { - SetMonData(mon, MON_DATA_TERA_TYPE, &gSpeciesInfo[species].forceTeraType); - } } void BoxMonToMon(const struct BoxPokemon *src, struct Pokemon *dest) @@ -6283,6 +6274,9 @@ u16 GetFormChangeTargetSpeciesBoxMon(struct BoxPokemon *boxMon, u16 method, u32 } } + if (gSpeciesInfo[targetSpecies].forceTeraType != TYPE_NONE) // Doing this here seems to cover all cases + SetBoxMonData(boxMon, MON_DATA_TERA_TYPE, &gSpeciesInfo[targetSpecies].forceTeraType); + return targetSpecies; } diff --git a/src/pokemon_summary_screen.c b/src/pokemon_summary_screen.c index 6e63ce5eab54..e8c9efeda8a2 100644 --- a/src/pokemon_summary_screen.c +++ b/src/pokemon_summary_screen.c @@ -1529,7 +1529,7 @@ static bool8 ExtractMonDataToSummaryStruct(struct Pokemon *mon) break; default: sum->ribbonCount = GetMonData(mon, MON_DATA_RIBBON_COUNT); - sum->teraType = GetMonData(mon, MON_DATA_TERA_TYPE); + sum->teraType = GetTeraType(mon); sum->isShiny = GetMonData(mon, MON_DATA_IS_SHINY); return TRUE; } diff --git a/test/battle/trainer_control.h b/test/battle/trainer_control.h index b0f417bb4d29..125a19d46f47 100644 --- a/test/battle/trainer_control.h +++ b/test/battle/trainer_control.h @@ -52,6 +52,7 @@ .isShiny = TRUE, #line 18 .dynamaxLevel = 5, + .shouldDynamax = TRUE, .moves = { #line 19 MOVE_AIR_SLASH, From 67157250efeaf5ffefc45ac1955b09f27f09788b Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Thu, 25 Apr 2024 01:38:53 -0500 Subject: [PATCH 05/13] Can't change the forced Tera Type anymore --- src/battle_main.c | 3 ++- src/battle_terastal.c | 7 ++----- src/battle_tower.c | 3 ++- src/pokemon.c | 15 +++++++-------- src/script_pokemon_util.c | 3 ++- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/battle_main.c b/src/battle_main.c index d2225ff76bec..c0ff2d9bb53f 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -2330,7 +2330,8 @@ u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer if (partyData[i].teraType > 0) { u32 data = partyData[i].teraType; - SetMonData(&party[i], MON_DATA_TERA_TYPE, &data); + if (gSpeciesInfo[partyData[i].species].forceTeraType == TYPE_NONE) + SetMonData(&party[i], MON_DATA_TERA_TYPE, &data); } CalculateMonStats(&party[i]); diff --git a/src/battle_terastal.c b/src/battle_terastal.c index 3960cf52193f..b0db13bc07c8 100644 --- a/src/battle_terastal.c +++ b/src/battle_terastal.c @@ -82,13 +82,10 @@ bool32 CanTerastallize(u32 battler) // Returns a pokemon's Tera type. u32 GetTeraType(struct Pokemon *mon) { - if (GetMonData(mon, MON_DATA_TERA_TYPE) + 1 == 0) // no tera type, so generate it + if (GetMonData(mon, MON_DATA_TERA_TYPE) == -1) // no tera type, so generate it { u16 species = GetMonData(mon, MON_DATA_SPECIES); - - if (gSpeciesInfo[species].forceTeraType != TYPE_NONE) - SetMonData(mon, MON_DATA_TERA_TYPE, &gSpeciesInfo[species].forceTeraType); - else + if (gSpeciesInfo[species].forceTeraType == TYPE_NONE) SetMonData(mon, MON_DATA_TERA_TYPE, ((GetMonData(mon, MON_DATA_PERSONALITY) & 0x1) == 0 ? &(gSpeciesInfo[species].types[0]) : &(gSpeciesInfo[species].types[1]))); } diff --git a/src/battle_tower.c b/src/battle_tower.c index fd0641cc6f1c..2dfce950fa9f 100644 --- a/src/battle_tower.c +++ b/src/battle_tower.c @@ -1643,7 +1643,8 @@ void CreateFacilityMon(const struct TrainerMon *fmon, u16 level, u8 fixedIV, u32 if (fmon->teraType) { u32 data = fmon->teraType; - SetMonData(dst, MON_DATA_TERA_TYPE, &data); + if (gSpeciesInfo[fmon->species].forceTeraType == TYPE_NONE) + SetMonData(dst, MON_DATA_TERA_TYPE, &data); } diff --git a/src/pokemon.c b/src/pokemon.c index 56d5d43ca70c..992c04115d03 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -1020,9 +1020,7 @@ void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, SetBoxMonData(boxMon, MON_DATA_ABILITY_NUM, &value); } - if (gSpeciesInfo[species].forceTeraType != TYPE_NONE) - SetBoxMonData(boxMon, MON_DATA_TERA_TYPE, &(gSpeciesInfo[species].forceTeraType)); - else + if (gSpeciesInfo[species].forceTeraType == TYPE_NONE) SetBoxMonData(boxMon, MON_DATA_TERA_TYPE, ((personality & 0x1) == 0 ? &(gSpeciesInfo[species].types[0]) : &(gSpeciesInfo[species].types[1]))); GiveBoxMonInitialMoveset(boxMon); @@ -2543,7 +2541,10 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) break; case MON_DATA_TERA_TYPE: { - retVal = substruct0->teraType - 1; + if (gSpeciesInfo[substruct0->species].forceTeraType != TYPE_NONE) + retVal = gSpeciesInfo[substruct0->species].forceTeraType; + else + retVal = substruct0->teraType - 1; break; } case MON_DATA_EVOLUTION_TRACKER: @@ -6274,9 +6275,6 @@ u16 GetFormChangeTargetSpeciesBoxMon(struct BoxPokemon *boxMon, u16 method, u32 } } - if (gSpeciesInfo[targetSpecies].forceTeraType != TYPE_NONE) // Doing this here seems to cover all cases - SetBoxMonData(boxMon, MON_DATA_TERA_TYPE, &gSpeciesInfo[targetSpecies].forceTeraType); - return targetSpecies; } @@ -6526,7 +6524,8 @@ void UpdateMonPersonality(struct BoxPokemon *boxMon, u32 personality) SetBoxMonData(boxMon, MON_DATA_IS_SHINY, &isShiny); SetBoxMonData(boxMon, MON_DATA_HIDDEN_NATURE, &hiddenNature); - SetBoxMonData(boxMon, MON_DATA_TERA_TYPE, &teraType); + if (gSpeciesInfo[GetBoxMonData(boxMon, MON_DATA_SPECIES)].forceTeraType == TYPE_NONE) + SetBoxMonData(boxMon, MON_DATA_TERA_TYPE, &teraType); } void HealPokemon(struct Pokemon *mon) diff --git a/src/script_pokemon_util.c b/src/script_pokemon_util.c index 763bb3f9e629..a9e716b28dc8 100644 --- a/src/script_pokemon_util.c +++ b/src/script_pokemon_util.c @@ -334,7 +334,8 @@ u32 ScriptGiveMonParameterized(u16 species, u8 level, u16 item, u8 ball, u8 natu // tera type if (teraType >= NUMBER_OF_MON_TYPES) teraType = gSpeciesInfo[species].types[0]; - SetMonData(&mon, MON_DATA_TERA_TYPE, &teraType); + if (gSpeciesInfo[species].forceTeraType == TYPE_NONE) + SetMonData(&mon, MON_DATA_TERA_TYPE, &teraType); // EV and IV for (i = 0; i < NUM_STATS; i++) From f0e485041e353d8a6d579f69ab18a56d42dccf20 Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Thu, 25 Apr 2024 02:07:41 -0500 Subject: [PATCH 06/13] Revert "Can't change the forced Tera Type anymore" This reverts commit 67157250efeaf5ffefc45ac1955b09f27f09788b. --- src/battle_main.c | 3 +-- src/battle_terastal.c | 7 +++++-- src/battle_tower.c | 3 +-- src/pokemon.c | 15 ++++++++------- src/script_pokemon_util.c | 3 +-- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/battle_main.c b/src/battle_main.c index c0ff2d9bb53f..d2225ff76bec 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -2330,8 +2330,7 @@ u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer if (partyData[i].teraType > 0) { u32 data = partyData[i].teraType; - if (gSpeciesInfo[partyData[i].species].forceTeraType == TYPE_NONE) - SetMonData(&party[i], MON_DATA_TERA_TYPE, &data); + SetMonData(&party[i], MON_DATA_TERA_TYPE, &data); } CalculateMonStats(&party[i]); diff --git a/src/battle_terastal.c b/src/battle_terastal.c index b0db13bc07c8..3960cf52193f 100644 --- a/src/battle_terastal.c +++ b/src/battle_terastal.c @@ -82,10 +82,13 @@ bool32 CanTerastallize(u32 battler) // Returns a pokemon's Tera type. u32 GetTeraType(struct Pokemon *mon) { - if (GetMonData(mon, MON_DATA_TERA_TYPE) == -1) // no tera type, so generate it + if (GetMonData(mon, MON_DATA_TERA_TYPE) + 1 == 0) // no tera type, so generate it { u16 species = GetMonData(mon, MON_DATA_SPECIES); - if (gSpeciesInfo[species].forceTeraType == TYPE_NONE) + + if (gSpeciesInfo[species].forceTeraType != TYPE_NONE) + SetMonData(mon, MON_DATA_TERA_TYPE, &gSpeciesInfo[species].forceTeraType); + else SetMonData(mon, MON_DATA_TERA_TYPE, ((GetMonData(mon, MON_DATA_PERSONALITY) & 0x1) == 0 ? &(gSpeciesInfo[species].types[0]) : &(gSpeciesInfo[species].types[1]))); } diff --git a/src/battle_tower.c b/src/battle_tower.c index 2dfce950fa9f..fd0641cc6f1c 100644 --- a/src/battle_tower.c +++ b/src/battle_tower.c @@ -1643,8 +1643,7 @@ void CreateFacilityMon(const struct TrainerMon *fmon, u16 level, u8 fixedIV, u32 if (fmon->teraType) { u32 data = fmon->teraType; - if (gSpeciesInfo[fmon->species].forceTeraType == TYPE_NONE) - SetMonData(dst, MON_DATA_TERA_TYPE, &data); + SetMonData(dst, MON_DATA_TERA_TYPE, &data); } diff --git a/src/pokemon.c b/src/pokemon.c index 992c04115d03..56d5d43ca70c 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -1020,7 +1020,9 @@ void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, SetBoxMonData(boxMon, MON_DATA_ABILITY_NUM, &value); } - if (gSpeciesInfo[species].forceTeraType == TYPE_NONE) + if (gSpeciesInfo[species].forceTeraType != TYPE_NONE) + SetBoxMonData(boxMon, MON_DATA_TERA_TYPE, &(gSpeciesInfo[species].forceTeraType)); + else SetBoxMonData(boxMon, MON_DATA_TERA_TYPE, ((personality & 0x1) == 0 ? &(gSpeciesInfo[species].types[0]) : &(gSpeciesInfo[species].types[1]))); GiveBoxMonInitialMoveset(boxMon); @@ -2541,10 +2543,7 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) break; case MON_DATA_TERA_TYPE: { - if (gSpeciesInfo[substruct0->species].forceTeraType != TYPE_NONE) - retVal = gSpeciesInfo[substruct0->species].forceTeraType; - else - retVal = substruct0->teraType - 1; + retVal = substruct0->teraType - 1; break; } case MON_DATA_EVOLUTION_TRACKER: @@ -6275,6 +6274,9 @@ u16 GetFormChangeTargetSpeciesBoxMon(struct BoxPokemon *boxMon, u16 method, u32 } } + if (gSpeciesInfo[targetSpecies].forceTeraType != TYPE_NONE) // Doing this here seems to cover all cases + SetBoxMonData(boxMon, MON_DATA_TERA_TYPE, &gSpeciesInfo[targetSpecies].forceTeraType); + return targetSpecies; } @@ -6524,8 +6526,7 @@ void UpdateMonPersonality(struct BoxPokemon *boxMon, u32 personality) SetBoxMonData(boxMon, MON_DATA_IS_SHINY, &isShiny); SetBoxMonData(boxMon, MON_DATA_HIDDEN_NATURE, &hiddenNature); - if (gSpeciesInfo[GetBoxMonData(boxMon, MON_DATA_SPECIES)].forceTeraType == TYPE_NONE) - SetBoxMonData(boxMon, MON_DATA_TERA_TYPE, &teraType); + SetBoxMonData(boxMon, MON_DATA_TERA_TYPE, &teraType); } void HealPokemon(struct Pokemon *mon) diff --git a/src/script_pokemon_util.c b/src/script_pokemon_util.c index a9e716b28dc8..763bb3f9e629 100644 --- a/src/script_pokemon_util.c +++ b/src/script_pokemon_util.c @@ -334,8 +334,7 @@ u32 ScriptGiveMonParameterized(u16 species, u8 level, u16 item, u8 ball, u8 natu // tera type if (teraType >= NUMBER_OF_MON_TYPES) teraType = gSpeciesInfo[species].types[0]; - if (gSpeciesInfo[species].forceTeraType == TYPE_NONE) - SetMonData(&mon, MON_DATA_TERA_TYPE, &teraType); + SetMonData(&mon, MON_DATA_TERA_TYPE, &teraType); // EV and IV for (i = 0; i < NUM_STATS; i++) From ee6f52fa8a648a303bc50c86dd81584d681dac8a Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Wed, 8 May 2024 20:46:02 -0500 Subject: [PATCH 07/13] Fix a lot of things --- data/battle_anim_scripts.s | 3 ++- data/battle_scripts_1.s | 13 +++++++------ include/battle_terastal.h | 1 - include/config/battle.h | 2 +- src/battle_terastal.c | 18 +----------------- src/data/pokemon/species_info.h | 1 - src/pokemon.c | 20 +++++++++----------- src/pokemon_summary_screen.c | 2 +- 8 files changed, 21 insertions(+), 39 deletions(-) diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index 23697e38ad2b..d7e547a4cba9 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -28340,12 +28340,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 diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 12d4108b2b95..dfa811f953b0 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -34,16 +34,17 @@ BattleScript_Terastallization:: BattleScript_TeraFormChange:: @ TODO: no string prints in S/V, but right now this helps with clarity - printstring STRINGID_PKMNTERASTALLIZEDINTO - @ TODO: replace this animation - playanimation BS_ATTACKER, B_ANIM_TOTEM_FLARE - waitanimation + printstring STRINGID_PKMNSTORINGENERGY handleformchange BS_ATTACKER, 0 handleformchange BS_ATTACKER, 1 - playanimation BS_ATTACKER, B_ANIM_FORM_CHANGE + playanimation BS_ATTACKER, B_ANIM_TERA_CHARGE + waitanimation + applyterastallization + playanimation BS_ATTACKER, B_ANIM_TERA_ACTIVATE waitanimation - copybyte sBATTLER, gBattlerAttacker handleformchange BS_ATTACKER, 2 + printstring STRINGID_PKMNTERASTALLIZEDINTO + waitmessage B_WAIT_TIME_LONG switchinabilities BS_ATTACKER end3 diff --git a/include/battle_terastal.h b/include/battle_terastal.h index c8bb16e4a869..c5428b6418be 100644 --- a/include/battle_terastal.h +++ b/include/battle_terastal.h @@ -4,7 +4,6 @@ void PrepareBattlerForTera(u32 battler); void ApplyBattlerVisualsForTeraAnim(u32 battler); bool32 CanTerastallize(u32 battler); -u32 GetTeraType(struct Pokemon *mon); u32 GetBattlerTeraType(u32 battler); bool32 IsTerastallized(u32 battler); void ExpendTypeStellarBoost(u32 battler, u32 type); diff --git a/include/config/battle.h b/include/config/battle.h index 18379d4add5a..fea668716c3f 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -180,7 +180,7 @@ #define B_FLAG_NO_CATCHING 0 // If this flag is set, the ability to catch wild Pokémon is disabled. #define B_FLAG_AI_VS_AI_BATTLE 0 // If this flag is set, the player's mons will be controlled by the ai next battles. #define B_FLAG_DYNAMAX_BATTLE 0 // If this flag is set, the ability to Dynamax in battle is enabled for all trainers. -#define B_FLAG_TERA_ORB_CHARGED 0 // If this flag is set, the Tera Orb is charged. It is automatically set upon healing and cleared upon Terastallizing once configured. +#define B_FLAG_TERA_ORB_CHARGED FLAG_UNUSED_0x264 // If this flag is set, the Tera Orb is charged. It is automatically set upon healing and cleared upon Terastallizing once configured. #define B_FLAG_TERA_ORB_NO_COST 0 // If this flag is set, the Tera Orb does not use up its charge upon Terastallization. In S/V, this occurs after an event with Terapagos. // Var Settings diff --git a/src/battle_terastal.c b/src/battle_terastal.c index 39b538b1ebef..17ada99d3e0f 100644 --- a/src/battle_terastal.c +++ b/src/battle_terastal.c @@ -87,26 +87,10 @@ bool32 CanTerastallize(u32 battler) return TRUE; } -// Returns a pokemon's Tera type. -u32 GetTeraType(struct Pokemon *mon) -{ - if (GetMonData(mon, MON_DATA_TERA_TYPE) + 1 == 0) // no tera type, so generate it - { - u16 species = GetMonData(mon, MON_DATA_SPECIES); - - if (gSpeciesInfo[species].forceTeraType != TYPE_NONE) - SetMonData(mon, MON_DATA_TERA_TYPE, &gSpeciesInfo[species].forceTeraType); - else - SetMonData(mon, MON_DATA_TERA_TYPE, ((GetMonData(mon, MON_DATA_PERSONALITY) & 0x1) == 0 ? &(gSpeciesInfo[species].types[0]) : &(gSpeciesInfo[species].types[1]))); - } - - return GetMonData(mon, MON_DATA_TERA_TYPE); -} - // Returns a battler's Tera type. u32 GetBattlerTeraType(u32 battler) { - return GetTeraType(&GetBattlerParty(battler)[gBattlerPartyIndexes[battler]]); + return GetMonData(&GetBattlerParty(battler)[gBattlerPartyIndexes[battler]], MON_DATA_TERA_TYPE); } // Returns whether a battler is terastallized. diff --git a/src/data/pokemon/species_info.h b/src/data/pokemon/species_info.h index fc5e4d2a3d00..014996e8c3df 100644 --- a/src/data/pokemon/species_info.h +++ b/src/data/pokemon/species_info.h @@ -31,7 +31,6 @@ const struct SpeciesInfo gSpeciesInfo[] = { [SPECIES_NONE] = { - .forceTeraType = TYPE_NONE, .speciesName = _("??????????"), .cryId = CRY_NONE, .natDexNum = NATIONAL_DEX_NONE, diff --git a/src/pokemon.c b/src/pokemon.c index 953033201fca..324a68fceca8 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -1020,11 +1020,6 @@ void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, SetBoxMonData(boxMon, MON_DATA_ABILITY_NUM, &value); } - if (gSpeciesInfo[species].forceTeraType != TYPE_NONE) - SetBoxMonData(boxMon, MON_DATA_TERA_TYPE, &(gSpeciesInfo[species].forceTeraType)); - else - SetBoxMonData(boxMon, MON_DATA_TERA_TYPE, ((personality & 0x1) == 0 ? &(gSpeciesInfo[species].types[0]) : &(gSpeciesInfo[species].types[1]))); - GiveBoxMonInitialMoveset(boxMon); } @@ -2522,10 +2517,16 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) retVal = substruct3->gigantamaxFactor; break; case MON_DATA_TERA_TYPE: - { - retVal = substruct0->teraType - 1; + if (gSpeciesInfo[substruct0->species].forceTeraType) + retVal = gSpeciesInfo[substruct0->species].forceTeraType; + else if (substruct0->teraType == 0) // 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]; + } + else + retVal = substruct0->teraType - 1; break; - } case MON_DATA_EVOLUTION_TRACKER: evoTracker.asField.a = substruct1->evolutionTracker1; evoTracker.asField.b = substruct1->evolutionTracker2; @@ -6272,9 +6273,6 @@ u16 GetFormChangeTargetSpeciesBoxMon(struct BoxPokemon *boxMon, u16 method, u32 } } - if (gSpeciesInfo[targetSpecies].forceTeraType != TYPE_NONE) // Doing this here seems to cover all cases - SetBoxMonData(boxMon, MON_DATA_TERA_TYPE, &gSpeciesInfo[targetSpecies].forceTeraType); - return targetSpecies; } diff --git a/src/pokemon_summary_screen.c b/src/pokemon_summary_screen.c index 611bd4205b11..f13e57ed8ecc 100644 --- a/src/pokemon_summary_screen.c +++ b/src/pokemon_summary_screen.c @@ -1529,7 +1529,7 @@ static bool8 ExtractMonDataToSummaryStruct(struct Pokemon *mon) break; default: sum->ribbonCount = GetMonData(mon, MON_DATA_RIBBON_COUNT); - sum->teraType = GetTeraType(mon); + sum->teraType = GetMonData(mon, MON_DATA_TERA_TYPE); sum->isShiny = GetMonData(mon, MON_DATA_IS_SHINY); return TRUE; } From 8a18cb153fde624b81e5d0defd219e438a77da3d Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Wed, 8 May 2024 20:46:46 -0500 Subject: [PATCH 08/13] Oops --- include/config/battle.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/config/battle.h b/include/config/battle.h index fea668716c3f..18379d4add5a 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -180,7 +180,7 @@ #define B_FLAG_NO_CATCHING 0 // If this flag is set, the ability to catch wild Pokémon is disabled. #define B_FLAG_AI_VS_AI_BATTLE 0 // If this flag is set, the player's mons will be controlled by the ai next battles. #define B_FLAG_DYNAMAX_BATTLE 0 // If this flag is set, the ability to Dynamax in battle is enabled for all trainers. -#define B_FLAG_TERA_ORB_CHARGED FLAG_UNUSED_0x264 // If this flag is set, the Tera Orb is charged. It is automatically set upon healing and cleared upon Terastallizing once configured. +#define B_FLAG_TERA_ORB_CHARGED 0 // If this flag is set, the Tera Orb is charged. It is automatically set upon healing and cleared upon Terastallizing once configured. #define B_FLAG_TERA_ORB_NO_COST 0 // If this flag is set, the Tera Orb does not use up its charge upon Terastallization. In S/V, this occurs after an event with Terapagos. // Var Settings From e056fc429dc66dab36e55f951c32e238590d3514 Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Wed, 8 May 2024 21:23:10 -0500 Subject: [PATCH 09/13] Update pokemon.h --- include/config/pokemon.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/config/pokemon.h b/include/config/pokemon.h index 1b818ba2e448..34afb80c75c3 100644 --- a/include/config/pokemon.h +++ b/include/config/pokemon.h @@ -43,7 +43,7 @@ #define P_FOOTPRINTS TRUE // If TRUE, Pokémon will have footprints (as was the case up to Gen 5 and in BDSP). Disabling this saves some ROM space. #define P_LEGENDARY_PERFECT_IVS GEN_LATEST // Since Gen 6, Legendaries, Mythicals and Ultra Beasts found in the wild or given through gifts have at least 3 perfect IVs. #define P_EV_CAP GEN_LATEST // Since Gen 6, the max EVs per stat is 252 instead of 255. -#define P_SHOW_TERA_TYPE GEN_LATEST // Since Gen 9, the Tera Type is shown on the summary screen. +#define P_SHOW_TERA_TYPE GEN_8 // Since Gen 9, the Tera Type is shown on the summary screen. #define P_TM_LITERACY GEN_LATEST // Since Gen 6, TM illiterate Pokémon can learn TMs that teach moves that are in their level-up learnsets. // Learnset helper toggles From 721ae27912db21996fa4a9f00fe3140ec1b234d0 Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Wed, 8 May 2024 21:23:20 -0500 Subject: [PATCH 10/13] Update pokemon.h --- include/config/pokemon.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/config/pokemon.h b/include/config/pokemon.h index 34afb80c75c3..32415e5247ba 100644 --- a/include/config/pokemon.h +++ b/include/config/pokemon.h @@ -43,7 +43,7 @@ #define P_FOOTPRINTS TRUE // If TRUE, Pokémon will have footprints (as was the case up to Gen 5 and in BDSP). Disabling this saves some ROM space. #define P_LEGENDARY_PERFECT_IVS GEN_LATEST // Since Gen 6, Legendaries, Mythicals and Ultra Beasts found in the wild or given through gifts have at least 3 perfect IVs. #define P_EV_CAP GEN_LATEST // Since Gen 6, the max EVs per stat is 252 instead of 255. -#define P_SHOW_TERA_TYPE GEN_8 // Since Gen 9, the Tera Type is shown on the summary screen. +#define P_SHOW_TERA_TYPE GEN_8 // Since Gen 9, the Tera Type is shown on the summary screen. #define P_TM_LITERACY GEN_LATEST // Since Gen 6, TM illiterate Pokémon can learn TMs that teach moves that are in their level-up learnsets. // Learnset helper toggles From f18b2eb110e574bd4a1ab32bc91a92a28c383134 Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Mon, 3 Jun 2024 15:52:12 -0500 Subject: [PATCH 11/13] Address reviews --- src/battle_script_commands.c | 18 +++---- src/pokemon.c | 6 ++- test/battle/gimmick/terastal.c | 21 +++++++- test/battle/move_effect/tera_starstorm.c | 61 ++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 11 deletions(-) create mode 100644 test/battle/move_effect/tera_starstorm.c diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 66f819cf8879..6e55da104d05 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -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; } diff --git a/src/pokemon.c b/src/pokemon.c index 75c17c439e8f..a706c9578697 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -2774,14 +2774,18 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data) break; case MON_DATA_TERA_TYPE: if (gSpeciesInfo[substruct0->species].forceTeraType) + { retVal = gSpeciesInfo[substruct0->species].forceTeraType; - else if (substruct0->teraType == 0) // Tera Type hasn't been modified so we can just use the personality + } + 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]; } else + { retVal = substruct0->teraType; + } break; case MON_DATA_EVOLUTION_TRACKER: evoTracker.asField.a = substruct1->evolutionTracker1; diff --git a/test/battle/gimmick/terastal.c b/test/battle/gimmick/terastal.c index c40b6823e70c..610b69e8e510 100644 --- a/test/battle/gimmick/terastal.c +++ b/test/battle/gimmick/terastal.c @@ -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,22 @@ 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 species1; + u32 species2; + PARAMETRIZE { species1 = SPECIES_OGERPON_TEAL_MASK; species2 = SPECIES_OGERPON_TEAL_MASK_TERA; } + PARAMETRIZE { species1 = SPECIES_OGERPON_WELLSPRING_MASK; species2 = SPECIES_OGERPON_WELLSPRING_MASK_TERA; } + PARAMETRIZE { species1 = SPECIES_OGERPON_HEARTHFLAME_MASK; species2 = SPECIES_OGERPON_HEARTHFLAME_MASK_TERA; } + PARAMETRIZE { species1 = SPECIES_OGERPON_CORNERSTONE_MASK; species2 = SPECIES_OGERPON_CORNERSTONE_MASK_TERA; } + PARAMETRIZE { species1 = SPECIES_TERAPAGOS_TERASTAL; species2 = SPECIES_TERAPAGOS_STELLAR; } + GIVEN { + PLAYER(species1); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE, tera: TRUE); } + } THEN { + EXPECT_EQ(player->species, species2); + } +} diff --git a/test/battle/move_effect/tera_starstorm.c b/test/battle/move_effect/tera_starstorm.c new file mode 100644 index 000000000000..c5486a794fe8 --- /dev/null +++ b/test/battle/move_effect/tera_starstorm.c @@ -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); + } +} \ No newline at end of file From 04a47000eba83a47db37379e8991301bce71160e Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Mon, 3 Jun 2024 15:52:48 -0500 Subject: [PATCH 12/13] Update tera_starstorm.c --- test/battle/move_effect/tera_starstorm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/battle/move_effect/tera_starstorm.c b/test/battle/move_effect/tera_starstorm.c index c5486a794fe8..cad4f9a015d0 100644 --- a/test/battle/move_effect/tera_starstorm.c +++ b/test/battle/move_effect/tera_starstorm.c @@ -58,4 +58,4 @@ SINGLE_BATTLE_TEST("Tera Starstorm becomes a physical move if the user is Terapa } FINALLY { EXPECT_MUL_EQ(results[0].damage, UQ_4_12(2.5), results[1].damage); } -} \ No newline at end of file +} From 40de322f4afdb4e87ab07853e61dce43b1b85445 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Mon, 3 Jun 2024 17:39:45 -0400 Subject: [PATCH 13/13] Update test/battle/gimmick/terastal.c --- test/battle/gimmick/terastal.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/test/battle/gimmick/terastal.c b/test/battle/gimmick/terastal.c index 610b69e8e510..5b53eb637392 100644 --- a/test/battle/gimmick/terastal.c +++ b/test/battle/gimmick/terastal.c @@ -796,19 +796,18 @@ SINGLE_BATTLE_TEST("(TERA) All type indicators function correctly") SINGLE_BATTLE_TEST("(TERA) Pokemon with Tera forms change upon Terastallizing") { - u32 species1; - u32 species2; - PARAMETRIZE { species1 = SPECIES_OGERPON_TEAL_MASK; species2 = SPECIES_OGERPON_TEAL_MASK_TERA; } - PARAMETRIZE { species1 = SPECIES_OGERPON_WELLSPRING_MASK; species2 = SPECIES_OGERPON_WELLSPRING_MASK_TERA; } - PARAMETRIZE { species1 = SPECIES_OGERPON_HEARTHFLAME_MASK; species2 = SPECIES_OGERPON_HEARTHFLAME_MASK_TERA; } - PARAMETRIZE { species1 = SPECIES_OGERPON_CORNERSTONE_MASK; species2 = SPECIES_OGERPON_CORNERSTONE_MASK_TERA; } - PARAMETRIZE { species1 = SPECIES_TERAPAGOS_TERASTAL; species2 = SPECIES_TERAPAGOS_STELLAR; } + 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(species1); + PLAYER(species); OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(player, MOVE_CELEBRATE, tera: TRUE); } } THEN { - EXPECT_EQ(player->species, species2); + EXPECT_EQ(player->species, targetSpecies); } }