diff --git a/graphics/battle_interface/bug_indicator.png b/graphics/battle_interface/bug_indicator.png new file mode 100644 index 000000000..7ab7dc6df Binary files /dev/null and b/graphics/battle_interface/bug_indicator.png differ diff --git a/graphics/battle_interface/dark_indicator.png b/graphics/battle_interface/dark_indicator.png new file mode 100644 index 000000000..149d2155a Binary files /dev/null and b/graphics/battle_interface/dark_indicator.png differ diff --git a/graphics/battle_interface/dragon_indicator.png b/graphics/battle_interface/dragon_indicator.png new file mode 100644 index 000000000..4c1d9e1f7 Binary files /dev/null and b/graphics/battle_interface/dragon_indicator.png differ diff --git a/graphics/battle_interface/electric_indicator.png b/graphics/battle_interface/electric_indicator.png new file mode 100644 index 000000000..518907dc0 Binary files /dev/null and b/graphics/battle_interface/electric_indicator.png differ diff --git a/graphics/battle_interface/fairy_indicator.png b/graphics/battle_interface/fairy_indicator.png new file mode 100644 index 000000000..a41736eee Binary files /dev/null and b/graphics/battle_interface/fairy_indicator.png differ diff --git a/graphics/battle_interface/fighting_indicator.png b/graphics/battle_interface/fighting_indicator.png new file mode 100644 index 000000000..6102ab684 Binary files /dev/null and b/graphics/battle_interface/fighting_indicator.png differ diff --git a/graphics/battle_interface/fire_indicator.png b/graphics/battle_interface/fire_indicator.png new file mode 100644 index 000000000..4d3e59d54 Binary files /dev/null and b/graphics/battle_interface/fire_indicator.png differ diff --git a/graphics/battle_interface/flying_indicator.png b/graphics/battle_interface/flying_indicator.png new file mode 100644 index 000000000..767954b70 Binary files /dev/null and b/graphics/battle_interface/flying_indicator.png differ diff --git a/graphics/battle_interface/ghost_indicator.png b/graphics/battle_interface/ghost_indicator.png new file mode 100644 index 000000000..c1aeec605 Binary files /dev/null and b/graphics/battle_interface/ghost_indicator.png differ diff --git a/graphics/battle_interface/grass_indicator.png b/graphics/battle_interface/grass_indicator.png new file mode 100644 index 000000000..81fa2589d Binary files /dev/null and b/graphics/battle_interface/grass_indicator.png differ diff --git a/graphics/battle_interface/ground_indicator.png b/graphics/battle_interface/ground_indicator.png new file mode 100644 index 000000000..7a306510f Binary files /dev/null and b/graphics/battle_interface/ground_indicator.png differ diff --git a/graphics/battle_interface/ice_indicator.png b/graphics/battle_interface/ice_indicator.png new file mode 100644 index 000000000..a40d96f01 Binary files /dev/null and b/graphics/battle_interface/ice_indicator.png differ diff --git a/graphics/battle_interface/normal_indicator.png b/graphics/battle_interface/normal_indicator.png new file mode 100644 index 000000000..029827e29 Binary files /dev/null and b/graphics/battle_interface/normal_indicator.png differ diff --git a/graphics/battle_interface/poison_indicator.png b/graphics/battle_interface/poison_indicator.png new file mode 100644 index 000000000..c2646d0a7 Binary files /dev/null and b/graphics/battle_interface/poison_indicator.png differ diff --git a/graphics/battle_interface/psychic_indicator.png b/graphics/battle_interface/psychic_indicator.png new file mode 100644 index 000000000..f70352d2e Binary files /dev/null and b/graphics/battle_interface/psychic_indicator.png differ diff --git a/graphics/battle_interface/rock_indicator.png b/graphics/battle_interface/rock_indicator.png new file mode 100644 index 000000000..0329ec678 Binary files /dev/null and b/graphics/battle_interface/rock_indicator.png differ diff --git a/graphics/battle_interface/steel_indicator.png b/graphics/battle_interface/steel_indicator.png new file mode 100644 index 000000000..e84d9fa1b Binary files /dev/null and b/graphics/battle_interface/steel_indicator.png differ diff --git a/graphics/battle_interface/stellar_indicator.png b/graphics/battle_interface/stellar_indicator.png new file mode 100644 index 000000000..7551e2b23 Binary files /dev/null and b/graphics/battle_interface/stellar_indicator.png differ diff --git a/graphics/battle_interface/tera_indicator.pal b/graphics/battle_interface/tera_indicator.pal new file mode 100644 index 000000000..d4ab14047 --- /dev/null +++ b/graphics/battle_interface/tera_indicator.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 0 0 +98 83 124 +207 63 109 +83 105 175 +80 144 216 +216 208 179 +254 115 121 +148 155 163 +99 188 91 +147 192 47 +255 156 85 +199 182 140 +113 206 198 +117 206 192 +245 210 55 +255 255 255 diff --git a/graphics/battle_interface/tera_trigger.png b/graphics/battle_interface/tera_trigger.png new file mode 100644 index 000000000..5498ea880 Binary files /dev/null and b/graphics/battle_interface/tera_trigger.png differ diff --git a/graphics/battle_interface/water_indicator.png b/graphics/battle_interface/water_indicator.png new file mode 100644 index 000000000..bdeb401f4 Binary files /dev/null and b/graphics/battle_interface/water_indicator.png differ diff --git a/include/battle.h b/include/battle.h index 16884e21d..29d17bbd0 100644 --- a/include/battle.h +++ b/include/battle.h @@ -14,7 +14,7 @@ #include "pokeball.h" // #include "battle_debug.h" #include "battle_dynamax.h" -// #include "battle_terastal.h" +#include "battle_terastal.h" #include "random.h" // for rng_value_t // Helper for accessing command arguments and advancing gBattlescriptCurrInstr. diff --git a/include/battle_controllers.h b/include/battle_controllers.h index b3cbbd5c1..efb724eaf 100644 --- a/include/battle_controllers.h +++ b/include/battle_controllers.h @@ -105,6 +105,7 @@ enum { #define RET_MEGA_EVOLUTION (1 << 7) #define RET_ULTRA_BURST (1 << 6) #define RET_DYNAMAX (1 << 5) +#define RET_TERASTAL (1 << 4) struct UnusedControllerStruct { diff --git a/include/battle_interface.h b/include/battle_interface.h index a963db269..0061c3a26 100644 --- a/include/battle_interface.h +++ b/include/battle_interface.h @@ -60,12 +60,36 @@ enum #define TAG_DYNAMAX_TRIGGER_TILE 0xD77D #define TAG_DYNAMAX_INDICATOR_TILE 0xD77E +#define TAG_NORMAL_INDICATOR_TILE 0xD77F +#define TAG_FIGHTING_INDICATOR_TILE 0xD780 +#define TAG_FLYING_INDICATOR_TILE 0xD781 +#define TAG_POISON_INDICATOR_TILE 0xD782 +#define TAG_GROUND_INDICATOR_TILE 0xD783 +#define TAG_ROCK_INDICATOR_TILE 0xD784 +#define TAG_BUG_INDICATOR_TILE 0xD785 +#define TAG_GHOST_INDICATOR_TILE 0xD786 +#define TAG_STEEL_INDICATOR_TILE 0xD787 +// empty spot for TYPE_MYSTERY +#define TAG_FIRE_INDICATOR_TILE 0xD789 +#define TAG_WATER_INDICATOR_TILE 0xD78A +#define TAG_GRASS_INDICATOR_TILE 0xD78B +#define TAG_ELECTRIC_INDICATOR_TILE 0xD78C +#define TAG_PSYCHIC_INDICATOR_TILE 0xD78D +#define TAG_ICE_INDICATOR_TILE 0xD78E +#define TAG_DRAGON_INDICATOR_TILE 0xD78F +#define TAG_DARK_INDICATOR_TILE 0xD790 +#define TAG_FAIRY_INDICATOR_TILE 0xD791 +#define TAG_STELLAR_INDICATOR_TILE 0xD792 +#define TAG_TERA_TRIGGER_TILE 0xD793 + #define TAG_MEGA_TRIGGER_PAL 0xD777 #define TAG_MEGA_INDICATOR_PAL 0xD778 #define TAG_MISC_INDICATOR_PAL 0xD779 // Alpha, Omega, and Dynamax indicators use the same palette as each of them only uses 4 different colors. #define TAG_ZMOVE_TRIGGER_PAL 0xD77B #define TAG_BURST_TRIGGER_PAL 0xD77C #define TAG_DYNAMAX_TRIGGER_PAL 0xD77D +#define TAG_TERA_INDICATOR_PAL 0xD77E +#define TAG_TERA_TRIGGER_PAL 0xD77F enum { @@ -94,6 +118,7 @@ void DestoryHealthboxSprite(u8 healthboxSpriteId); void DummyBattleInterfaceFunc(u8 healthboxSpriteId, bool8 isDoubleBattleBankOnly); void UpdateOamPriorityInAllHealthboxes(u8 priority); void InitBattlerHealthboxCoords(u8 battlerId); +void GetBattlerHealthboxCoords(u8 battler, s16 *x, s16 *y); void UpdateHpTextInHealthbox(u32 healthboxSpriteId, u32 maxOrCurrent, s16 currHp, s16 maxHp); void SwapHpBarsWithHpText(void); u8 CreatePartyStatusSummarySprites(u8 battlerId, struct HpAndStatus *partyInfo, u8 isSwitchingMons, bool8 isBattleStart); diff --git a/include/battle_scripts.h b/include/battle_scripts.h index e3e5cb29b..11441fc1d 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -810,6 +810,8 @@ extern const u8 BattleScript_IllusionOff[]; extern const u8 BattleScript_DancerActivates[]; extern const u8 BattleScript_AttackerFormChange[]; extern const u8 BattleScript_AttackerFormChangeEnd3[]; +extern const u8 BattleScript_AttackerFormChangeWithString[]; +extern const u8 BattleScript_AttackerFormChangeWithStringEnd3[]; extern const u8 BattleScript_TargetFormChange[]; extern const u8 BattleScript_AnticipationActivates[]; extern const u8 BattleScript_SlowStartEnds[]; @@ -827,6 +829,7 @@ extern const u8 BattleScript_CheekPouchActivates[]; extern const u8 BattleScript_TotemVar[]; extern const u8 BattleScript_TotemFlaredToLife[]; extern const u8 BattleScript_AnnounceAirLockCloudNine[]; +extern const u8 BattleScript_ActivateTeraformZero[]; extern const u8 BattleScript_BattlerAbilityStatRaiseOnSwitchIn[]; extern const u8 BattleScript_CottonDownActivates[]; extern const u8 BattleScript_BallFetch[]; @@ -880,6 +883,7 @@ extern const u8 BattleScript_PastelVeilActivates[]; extern const u8 BattleScript_MimicryActivatesEnd3[]; extern const u8 BattleScript_ApplyMimicry[]; extern const u8 BattleScript_AttackerFormChangeEnd3NoPopup[]; +extern const u8 BattleScript_AttackerFormChangeWithStringEnd3NoPopup[]; extern const u8 BattleScript_AttackerFormChangeMoveEffect[]; extern const u8 BattleScript_BothCanNoLongerEscape[]; extern const u8 BattleScript_OctolockEndTurn[]; diff --git a/include/battle_terastal.h b/include/battle_terastal.h new file mode 100644 index 000000000..c5428b641 --- /dev/null +++ b/include/battle_terastal.h @@ -0,0 +1,31 @@ +#ifndef GUARD_BATTLE_TERASTAL_H +#define GUARD_BATTLE_TERASTAL_H + +void PrepareBattlerForTera(u32 battler); +void ApplyBattlerVisualsForTeraAnim(u32 battler); +bool32 CanTerastallize(u32 battler); +u32 GetBattlerTeraType(u32 battler); +bool32 IsTerastallized(u32 battler); +void ExpendTypeStellarBoost(u32 battler, u32 type); +bool32 IsTypeStellarBoosted(u32 battler, u32 type); +uq4_12_t GetTeraMultiplier(u32 battler, u32 type); + +u16 GetTeraTypeRGB(u32 type); + +void ChangeTeraTriggerSprite(u8 spriteId, u8 animId); +void CreateTeraTriggerSprite(u8 battler, u8 palId); +bool32 IsTeraTriggerSpriteActive(void); +void HideTeraTriggerSprite(void); +void DestroyTeraTriggerSprite(void); + +void TeraIndicator_LoadSpriteGfx(void); +bool32 TeraIndicator_ShouldBeInvisible(u32 battler); +u8 TeraIndicator_GetSpriteId(u32 healthboxSpriteId); +void TeraIndicator_SetVisibilities(u32 healthboxId, bool32 invisible); +void TeraIndicator_UpdateOamPriorities(u32 healthboxId, u32 oamPriority); +void TeraIndicator_UpdateLevel(u32 healthboxId, u32 level); +void TeraIndicator_CreateSprite(u32 battler, u32 healthboxSpriteId); +void TeraIndicator_DestroySprite(u32 healthboxSpriteId); +void TeraIndicator_UpdateType(u32 battler, u32 healthboxSpriteId); + +#endif diff --git a/include/constants/form_change_types.h b/include/constants/form_change_types.h index 30bb54d02..ac1f53bb5 100644 --- a/include/constants/form_change_types.h +++ b/include/constants/form_change_types.h @@ -24,6 +24,8 @@ // param2: time of day to check, optional. // - DAY if Form change that activates in the daytime. // - NIGHT if Form change that activates at nighttime. +// - 0 if irrelevant, but param3 is necessary. +// param3: illegal statuses to have, optional. #define FORM_CHANGE_ITEM_USE 2 // TODO: Form change that activates when the Pokémon learns or forgets the move. @@ -61,7 +63,7 @@ #define FORM_CHANGE_END_BATTLE_TERRAIN 8 // Form change that activates when the Pokémon is switched out in battle. -// - No parameters. +// param1: ability to check, optional #define FORM_CHANGE_BATTLE_SWITCH 9 // Form change that activates when the Pokémon's HP % passes a certain threshold. @@ -121,6 +123,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 diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 5955ebcb1..6694991d9 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -8,7 +8,7 @@ #include "battle_controllers.h" #include "battle_setup.h" #include "battle_z_move.h" -// #include "battle_terastal.h" +#include "battle_terastal.h" #include "data.h" // #include "debug.h" #include "event_data.h" @@ -403,7 +403,7 @@ static void SetBattlerAiGimmickData(u32 battler, struct AiLogicData *aiData) if (party != NULL) { aiData->shouldDynamax[battler] = CanDynamax(battler) && (party[isSecondTrainer ? gBattlerPartyIndexes[battler] - MULTI_PARTY_SIZE : gBattlerPartyIndexes[battler]].shouldDynamax); - aiData->shouldTerastal[battler] = FALSE; // TODO: Tera CanTerastallize(battler) && (party[isSecondTrainer ? gBattlerPartyIndexes[battler] - MULTI_PARTY_SIZE : gBattlerPartyIndexes[battler]].shouldTerastal); + aiData->shouldTerastal[battler] = CanTerastallize(battler) && (party[isSecondTrainer ? gBattlerPartyIndexes[battler] - MULTI_PARTY_SIZE : gBattlerPartyIndexes[battler]].shouldTerastal); } else { @@ -2440,11 +2440,11 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) switch (move) { case MOVE_TRICK_OR_TREAT: - if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GHOST) || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove) /* || IsTerastallized(battlerDef) */) // TODO: Tera + if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GHOST) || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove) || IsTerastallized(battlerDef)) ADJUST_SCORE(-10); break; case MOVE_FORESTS_CURSE: - if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS) || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove) /* || IsTerastallized(battlerDef) */) // TODO: Tera + if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS) || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove) || IsTerastallized(battlerDef)) ADJUST_SCORE(-10); break; } @@ -3006,7 +3006,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) case EFFECT_SOAK: if (atkPartnerAbility == ABILITY_WONDER_GUARD && IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_WATER) - /* && !IsTerastallized(battlerAtkPartner) */) // TODO: Tera + && !IsTerastallized(battlerAtkPartner)) { RETURN_SCORE_PLUS(WEAK_EFFECT); } diff --git a/src/battle_interface.c b/src/battle_interface.c index 901209d7e..30af2a05f 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -774,36 +774,42 @@ void UpdateOamPriorityInAllHealthboxes(u8 priority) } } -void InitBattlerHealthboxCoords(u8 battler) +void GetBattlerHealthboxCoords(u8 battler, s16 *x, s16 *y) { - s16 x = 0, y = 0; + *x = 0, *y = 0; if (!WhichBattleCoords(battler)) { if (GetBattlerSide(battler) != B_SIDE_PLAYER) - x = 44, y = 30; + *x = 44, *y = 30; else - x = 158, y = 88; + *x = 158, *y = 88; } else { switch (GetBattlerPosition(battler)) { case B_POSITION_PLAYER_LEFT: - x = 159, y = 75; + *x = 159, *y = 75; break; case B_POSITION_PLAYER_RIGHT: - x = 171, y = 100; + *x = 171, *y = 100; break; case B_POSITION_OPPONENT_LEFT: - x = 44, y = 19; + *x = 44, *y = 19; break; case B_POSITION_OPPONENT_RIGHT: - x = 32, y = 44; + *x = 32, *y = 44; break; } } +} +void InitBattlerHealthboxCoords(u8 battler) +{ + s16 x, y; + + GetBattlerHealthboxCoords(battler, &x, &y); UpdateSpritePos(gHealthboxSpriteIds[battler], x, y); } diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 68c6d82fe..41f758e64 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1175,7 +1175,7 @@ bool32 ProteanTryChangeType(u32 battler, u32 ability, u32 move, u32 moveType) && (gBattleMons[battler].type1 != moveType || gBattleMons[battler].type2 != moveType || (gBattleMons[battler].type3 != moveType && gBattleMons[battler].type3 != TYPE_MYSTERY)) && move != MOVE_STRUGGLE - /* && !IsTerastallized(battler) */) // TODO: Tera + && !IsTerastallized(battler)) { SET_BATTLER_TYPE(battler, moveType); return TRUE; @@ -1183,19 +1183,18 @@ bool32 ProteanTryChangeType(u32 battler, u32 ability, u32 move, u32 moveType) return FALSE; } -// TODO: Tera -// bool32 ShouldTeraShellDistortTypeMatchups(u32 move, u32 battlerDef) -// { -// if (!(gBattleStruct->distortedTypeMatchups & gBitTable[battlerDef]) -// && GetBattlerAbility(battlerDef) == ABILITY_TERA_SHELL -// && gBattleMons[battlerDef].species == SPECIES_TERAPAGOS_TERASTAL -// && !IS_MOVE_STATUS(move) -// && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) -// && gBattleMons[battlerDef].hp == gBattleMons[battlerDef].maxHP) -// return TRUE; +bool32 ShouldTeraShellDistortTypeMatchups(u32 move, u32 battlerDef) +{ + if (!(gBattleStruct->distortedTypeMatchups & gBitTable[battlerDef]) + && GetBattlerAbility(battlerDef) == ABILITY_TERA_SHELL + && gBattleMons[battlerDef].species == SPECIES_TERAPAGOS_TERASTAL + && !IS_MOVE_STATUS(move) + && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) + && gBattleMons[battlerDef].hp == gBattleMons[battlerDef].maxHP) + return TRUE; -// return FALSE; -// } + return FALSE; +} bool32 IsMoveNotAllowedInSkyBattles(u32 move) { @@ -1874,14 +1873,13 @@ static void Cmd_ppreduce(void) gHitMarker &= ~HITMARKER_NO_PPDEDUCT; gBattlescriptCurrInstr = cmd->nextInstr; - // TODO: Tera - // if (ShouldTeraShellDistortTypeMatchups(gCurrentMove, gBattlerTarget)) - // { - // gBattleStruct->distortedTypeMatchups |= gBitTable[gBattlerTarget]; - // gBattlerAbility = gBattlerTarget; - // BattleScriptPushCursor(); - // gBattlescriptCurrInstr = BattleScript_TeraShellDistortingTypeMatchups; - // } + if (ShouldTeraShellDistortTypeMatchups(gCurrentMove, gBattlerTarget)) + { + gBattleStruct->distortedTypeMatchups |= gBitTable[gBattlerTarget]; + gBattlerAbility = gBattlerTarget; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_TeraShellDistortingTypeMatchups; + } } // The chance is 1/N for each stage. @@ -3829,16 +3827,15 @@ void SetMoveEffect(bool32 primary, bool32 certain) gBattlescriptCurrInstr = BattleScript_EffectPsychicNoise; } break; - // TODO: Tera - // case MOVE_EFFECT_TERA_BLAST: - // if (IsTerastallized(gEffectBattler) - // && GetBattlerTeraType(gEffectBattler) == TYPE_STELLAR - // && !NoAliveMonsForEitherParty()) - // { - // BattleScriptPush(gBattlescriptCurrInstr + 1); - // gBattlescriptCurrInstr = BattleScript_LowerAtkSpAtk; - // } - // break; + case MOVE_EFFECT_TERA_BLAST: + if (IsTerastallized(gEffectBattler) + && GetBattlerTeraType(gEffectBattler) == TYPE_STELLAR + && !NoAliveMonsForEitherParty()) + { + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_LowerAtkSpAtk; + } + break; } } } @@ -9669,7 +9666,7 @@ static void Cmd_various(void) VARIOUS_ARGS(const u8 *failInstr); if ((GetBattlerType(gBattlerTarget, 0, FALSE) == gMovesInfo[gCurrentMove].type && GetBattlerType(gBattlerTarget, 1, FALSE) == gMovesInfo[gCurrentMove].type) - /* || IsTerastallized(gBattlerTarget) */) // TODO: Tera + || IsTerastallized(gBattlerTarget)) { gBattlescriptCurrInstr = cmd->failInstr; } @@ -10002,7 +9999,7 @@ static void Cmd_various(void) case VARIOUS_TRY_THIRD_TYPE: { VARIOUS_ARGS(const u8 *failInstr); - if (IS_BATTLER_OF_TYPE(battler, gMovesInfo[gCurrentMove].argument)/* || IsTerastallized(battler) */) // TODO: Tera + if (IS_BATTLER_OF_TYPE(battler, gMovesInfo[gCurrentMove].argument) || IsTerastallized(battler)) { gBattlescriptCurrInstr = cmd->failInstr; } @@ -12111,12 +12108,11 @@ static void Cmd_tryconversiontypechange(void) u8 moveChecked = 0; u8 moveType = 0; - // TODO: Tera - // if (IsTerastallized(gBattlerAttacker)) - // { - // gBattlescriptCurrInstr = cmd->failInstr; - // return; - // } + if (IsTerastallized(gBattlerAttacker)) + { + gBattlescriptCurrInstr = cmd->failInstr; + return; + } if (B_UPDATED_CONVERSION >= GEN_6) { @@ -12889,15 +12885,14 @@ static void Cmd_settypetorandomresistance(void) { gBattlescriptCurrInstr = cmd->failInstr; } - // TODO: Tera - // else if (IsTerastallized(gBattlerAttacker)) - // { - // gBattlescriptCurrInstr = cmd->failInstr; - // } - // else if (gLastHitByType[gBattlerAttacker] == TYPE_STELLAR) - // { - // gBattlescriptCurrInstr = cmd->failInstr; - // } + else if (IsTerastallized(gBattlerAttacker)) + { + gBattlescriptCurrInstr = cmd->failInstr; + } + else if (gLastHitByType[gBattlerAttacker] == TYPE_STELLAR) + { + gBattlescriptCurrInstr = cmd->failInstr; + } else { u32 i, resistTypes = 0; @@ -14848,7 +14843,7 @@ static void Cmd_settypetoterrain(void) break; } - if (!IS_BATTLER_OF_TYPE(gBattlerAttacker, terrainType) /* && !IsTerastallized(gBattlerAttacker) */) // TODO: Tera + if (!IS_BATTLER_OF_TYPE(gBattlerAttacker, terrainType) && !IsTerastallized(gBattlerAttacker)) { SET_BATTLER_TYPE(gBattlerAttacker, terrainType); PREPARE_TYPE_BUFFER(gBattleTextBuff1, terrainType); @@ -16479,11 +16474,10 @@ void BS_TryReflectType(void) { gBattlescriptCurrInstr = cmd->failInstr; } - // TODO: Tera - // else if (IsTerastallized(gBattlerAttacker)) - // { - // gBattlescriptCurrInstr = cmd->failInstr; - // } + else if (IsTerastallized(gBattlerAttacker)) + { + gBattlescriptCurrInstr = cmd->failInstr; + } else if (IS_BATTLER_TYPELESS(gBattlerTarget)) { gBattlescriptCurrInstr = cmd->failInstr; @@ -16848,9 +16842,8 @@ void BS_AllySwitchFailChance(void) void BS_SetPhotonGeyserCategory(void) { NATIVE_ARGS(); - // TODO: Tera - // if (!((gMovesInfo[gCurrentMove].effect == EFFECT_TERA_BLAST && !IsTerastallized(gBattlerAttacker)) - // || (gMovesInfo[gCurrentMove].effect == EFFECT_TERA_STARSTORM && !(IsTerastallized(gBattlerAttacker) && 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; } @@ -17047,8 +17040,7 @@ void BS_RemoveWeather(void) void BS_ApplyTerastallization(void) { NATIVE_ARGS(); - // TODO: Tera - // ApplyBattlerVisualsForTeraAnim(gBattlerAttacker); + ApplyBattlerVisualsForTeraAnim(gBattlerAttacker); gBattlescriptCurrInstr = cmd->nextInstr; } diff --git a/src/battle_terastal.c b/src/battle_terastal.c new file mode 100644 index 000000000..62ab3afac --- /dev/null +++ b/src/battle_terastal.c @@ -0,0 +1,775 @@ +#include "global.h" +#include "battle.h" +#include "battle_anim.h" +#include "battle_controllers.h" +#include "battle_interface.h" +#include "battle_terastal.h" +#include "event_data.h" +#include "item.h" +#include "palette.h" +#include "pokemon.h" +#include "safari_zone.h" +#include "sprite.h" +#include "util.h" +#include "constants/abilities.h" +#include "constants/hold_effects.h" +#include "constants/rgb.h" + +// Sets flags and variables upon a battler's Terastallization. +void PrepareBattlerForTera(u32 battler) +{ + u32 side = GetBattlerSide(battler); + u32 index = gBattlerPartyIndexes[battler]; + + // Update TeraData fields. + gBattleStruct->tera.isTerastallized[side] |= gBitTable[index]; + gBattleStruct->tera.alreadyTerastallized[battler] = TRUE; + + // 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 + && !(gBattleTypeFlags & BATTLE_TYPE_DOUBLE && !IsPartnerMonFromSameTrainer(battler))) + { + FlagClear(B_FLAG_TERA_ORB_CHARGED); + } +} + +// Applies palette blend and enables UI indicator after animation has played +void ApplyBattlerVisualsForTeraAnim(u32 battler) +{ + struct Pokemon *party = GetBattlerParty(battler); + u32 index = gBattlerPartyIndexes[battler]; + + // Show indicator and do palette blend. + UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], &party[index], HEALTHBOX_ALL); + BlendPalette(OBJ_PLTT_ID(battler), 16, 8, GetTeraTypeRGB(GetBattlerTeraType(battler))); + CpuCopy32(gPlttBufferFaded + OBJ_PLTT_ID(battler), gPlttBufferUnfaded + OBJ_PLTT_ID(battler), PLTT_SIZEOF(16)); + + // We apply the animation behind a white screen, so restore the blended color here to avoid a pop + BlendPalette(OBJ_PLTT_ID(battler), 16, 16, RGB_WHITEALPHA); +} + +// Returns whether a battler can Terastallize. +bool32 CanTerastallize(u32 battler) +{ + u32 holdEffect = GetBattlerHoldEffect(battler, FALSE); + + // Check if Player has Tera Orb and has charge. + if (!CheckBagHasItem(ITEM_TERA_ORB, 1) + || !((B_FLAG_TERA_ORB_NO_COST != 0 && FlagGet(B_FLAG_TERA_ORB_NO_COST)) + || (B_FLAG_TERA_ORB_CHARGED != 0 && FlagGet(B_FLAG_TERA_ORB_CHARGED) + && ((battler == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && battler == B_POSITION_PLAYER_RIGHT)))))) + { + return FALSE; + } + + // Check if Trainer has already Terastallized. + if (gBattleStruct->tera.alreadyTerastallized[battler]) + { + return FALSE; + } + + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE + && IsPartnerMonFromSameTrainer(battler) + && (gBattleStruct->tera.alreadyTerastallized[BATTLE_PARTNER(battler)] + || (gBattleStruct->tera.toTera & gBitTable[BATTLE_PARTNER(battler)]))) + { + return FALSE; + } + + // Check if battler is holding a Z-Crystal or Mega Stone. + if (holdEffect == HOLD_EFFECT_Z_CRYSTAL || holdEffect == HOLD_EFFECT_MEGA_STONE) + { + return FALSE; + } + + // Every check passed! + return TRUE; +} + +// Returns a battler's Tera type. +u32 GetBattlerTeraType(u32 battler) +{ + return GetMonData(&GetBattlerParty(battler)[gBattlerPartyIndexes[battler]], MON_DATA_TERA_TYPE); +} + +// Returns whether a battler is terastallized. +bool32 IsTerastallized(u32 battler) +{ + return gBattleStruct->tera.isTerastallized[GetBattlerSide(battler)] & gBitTable[gBattlerPartyIndexes[battler]]; +} + + +// Uses up a type's Stellar boost. +void ExpendTypeStellarBoost(u32 battler, u32 type) +{ + if (type < 32) // avoid OOB access + gBattleStruct->tera.stellarBoostFlags[GetBattlerSide(battler)] |= gBitTable[type]; +} + +// Checks whether a type's Stellar boost has been expended. +bool32 IsTypeStellarBoosted(u32 battler, u32 type) +{ + if (type < 32) // avoid OOB access + return !(gBattleStruct->tera.stellarBoostFlags[GetBattlerSide(battler)] & gBitTable[type]); + else + return FALSE; +} + +// Returns the STAB power multiplier to use when Terastallized. +// Power multipliers from Smogon Research thread. +uq4_12_t GetTeraMultiplier(u32 battler, u32 type) +{ + u32 teraType = GetBattlerTeraType(battler); + bool32 hasAdaptability = (GetBattlerAbility(battler) == ABILITY_ADAPTABILITY); + + // Safety check. + if (!IsTerastallized(battler)) + return UQ_4_12(1.0); + + // Stellar-type checks. + if (teraType == TYPE_STELLAR) + { + bool32 shouldBoost = IsTypeStellarBoosted(battler, type); + if (IS_BATTLER_OF_BASE_TYPE(battler, type)) + { + if (shouldBoost) + return UQ_4_12(2.0); + else + return UQ_4_12(1.5); + } + else if (shouldBoost) + return UQ_4_12(1.2); + else + return UQ_4_12(1.0); + } + // Base and Tera type. + if (type == teraType && IS_BATTLER_OF_BASE_TYPE(battler, type)) + { + if (hasAdaptability) + return UQ_4_12(2.25); + else + return UQ_4_12(2.0); + } + // Base or Tera type only. + else if ((type == teraType && !IS_BATTLER_OF_BASE_TYPE(battler, type)) + || (type != teraType && IS_BATTLER_OF_BASE_TYPE(battler, type))) + { + if (hasAdaptability) + return UQ_4_12(2.0); + else + return UQ_4_12(1.5); + } + // Neither base or Tera type. + else + { + return UQ_4_12(1.0); + } +} + +u16 GetTeraTypeRGB(u32 type) +{ + return gTypesInfo[type].teraTypeRGBValue; +} + +// TERASTAL TRIGGER: +static const u8 ALIGNED(4) sTeraTriggerGfx[] = INCBIN_U8("graphics/battle_interface/tera_trigger.4bpp"); +static const u16 sTeraTriggerPal[] = INCBIN_U16("graphics/battle_interface/tera_trigger.gbapal"); + +static const struct SpriteSheet sSpriteSheet_TeraTrigger = +{ + sTeraTriggerGfx, sizeof(sTeraTriggerGfx), TAG_TERA_TRIGGER_TILE +}; +static const struct SpritePalette sSpritePalette_TeraTrigger = +{ + sTeraTriggerPal, TAG_TERA_TRIGGER_PAL +}; + +static const struct OamData sOamData_TeraTrigger = +{ + .y = 0, + .affineMode = 0, + .objMode = 0, + .mosaic = 0, + .bpp = 0, + .shape = ST_OAM_SQUARE, + .x = 0, + .matrixNum = 0, + .size = 2, + .tileNum = 0, + .priority = 1, + .paletteNum = 0, + .affineParam = 0, +}; + +static const union AnimCmd sSpriteAnim_TeraTriggerOff[] = +{ + ANIMCMD_FRAME(0, 0), + ANIMCMD_END +}; + +static const union AnimCmd sSpriteAnim_TeraTriggerOn[] = +{ + ANIMCMD_FRAME(16, 0), + ANIMCMD_END +}; + +static const union AnimCmd *const sSpriteAnimTable_TeraTrigger[] = +{ + sSpriteAnim_TeraTriggerOff, + sSpriteAnim_TeraTriggerOn, +}; + +static void SpriteCb_TeraTrigger(struct Sprite *sprite); +static const struct SpriteTemplate sSpriteTemplate_TeraTrigger = +{ + .tileTag = TAG_TERA_TRIGGER_TILE, + .paletteTag = TAG_TERA_TRIGGER_PAL, + .oam = &sOamData_TeraTrigger, + .anims = sSpriteAnimTable_TeraTrigger, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraTrigger +}; + +// Tera Evolution Trigger icon functions. +void ChangeTeraTriggerSprite(u8 spriteId, u8 animId) +{ + StartSpriteAnim(&gSprites[spriteId], animId); +} + +#define SINGLES_TERA_TRIGGER_POS_X_OPTIMAL (30) +#define SINGLES_TERA_TRIGGER_POS_X_PRIORITY (31) +#define SINGLES_TERA_TRIGGER_POS_X_SLIDE (15) +#define SINGLES_TERA_TRIGGER_POS_Y_DIFF (-11) + +#define DOUBLES_TERA_TRIGGER_POS_X_OPTIMAL (30) +#define DOUBLES_TERA_TRIGGER_POS_X_PRIORITY (31) +#define DOUBLES_TERA_TRIGGER_POS_X_SLIDE (15) +#define DOUBLES_TERA_TRIGGER_POS_Y_DIFF (-4) + +#define tBattler data[0] +#define tHide data[1] + +void CreateTeraTriggerSprite(u8 battler, u8 palId) +{ + LoadSpritePalette(&sSpritePalette_TeraTrigger); + if (GetSpriteTileStartByTag(TAG_TERA_TRIGGER_TILE) == 0xFFFF) + { + LoadSpriteSheet(&sSpriteSheet_TeraTrigger); + } + if (gBattleStruct->tera.triggerSpriteId == 0xFF) + { + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + gBattleStruct->tera.triggerSpriteId = CreateSprite(&sSpriteTemplate_TeraTrigger, + gSprites[gHealthboxSpriteIds[battler]].x - DOUBLES_TERA_TRIGGER_POS_X_SLIDE, + gSprites[gHealthboxSpriteIds[battler]].y - DOUBLES_TERA_TRIGGER_POS_Y_DIFF, 0); + else + gBattleStruct->tera.triggerSpriteId = CreateSprite(&sSpriteTemplate_TeraTrigger, + gSprites[gHealthboxSpriteIds[battler]].x - SINGLES_TERA_TRIGGER_POS_X_SLIDE, + gSprites[gHealthboxSpriteIds[battler]].y - SINGLES_TERA_TRIGGER_POS_Y_DIFF, 0); + } + gSprites[gBattleStruct->tera.triggerSpriteId].tBattler = battler; + gSprites[gBattleStruct->tera.triggerSpriteId].tHide = FALSE; + + ChangeTeraTriggerSprite(gBattleStruct->tera.triggerSpriteId, palId); +} + +static void SpriteCb_TeraTrigger(struct Sprite *sprite) +{ + s32 xSlide, xPriority, xOptimal; + s32 yDiff; + + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + { + xSlide = DOUBLES_TERA_TRIGGER_POS_X_SLIDE; + xPriority = DOUBLES_TERA_TRIGGER_POS_X_PRIORITY; + xOptimal = DOUBLES_TERA_TRIGGER_POS_X_OPTIMAL; + yDiff = DOUBLES_TERA_TRIGGER_POS_Y_DIFF; + } + else + { + xSlide = SINGLES_TERA_TRIGGER_POS_X_SLIDE; + xPriority = SINGLES_TERA_TRIGGER_POS_X_PRIORITY; + xOptimal = SINGLES_TERA_TRIGGER_POS_X_OPTIMAL; + yDiff = SINGLES_TERA_TRIGGER_POS_Y_DIFF; + } + + if (sprite->tHide) + { + if (sprite->x != gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xSlide) + sprite->x++; + + if (sprite->x >= gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xPriority) + sprite->oam.priority = 2; + else + sprite->oam.priority = 1; + + sprite->y = gSprites[gHealthboxSpriteIds[sprite->tBattler]].y - yDiff; + sprite->y2 = gSprites[gHealthboxSpriteIds[sprite->tBattler]].y2 - yDiff; + if (sprite->x == gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xSlide) + DestroyTeraTriggerSprite(); + } + else + { + if (sprite->x != gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xOptimal) + sprite->x--; + + if (sprite->x >= gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xPriority) + sprite->oam.priority = 2; + else + sprite->oam.priority = 1; + + sprite->y = gSprites[gHealthboxSpriteIds[sprite->tBattler]].y - yDiff; + sprite->y2 = gSprites[gHealthboxSpriteIds[sprite->tBattler]].y2 - yDiff; + } +} + +bool32 IsTeraTriggerSpriteActive(void) +{ + if (GetSpriteTileStartByTag(TAG_TERA_TRIGGER_TILE) == 0xFFFF) + return FALSE; + else if (IndexOfSpritePaletteTag(TAG_TERA_TRIGGER_PAL) != 0xFF) + return TRUE; + else + return FALSE; +} + +void HideTeraTriggerSprite(void) +{ + if (gBattleStruct->tera.triggerSpriteId != 0xFF) + { + ChangeTeraTriggerSprite(gBattleStruct->tera.triggerSpriteId, 0); + gSprites[gBattleStruct->tera.triggerSpriteId].tHide = TRUE; + } +} + +void DestroyTeraTriggerSprite(void) +{ + FreeSpritePaletteByTag(TAG_TERA_TRIGGER_PAL); + FreeSpriteTilesByTag(TAG_TERA_TRIGGER_TILE); + if (gBattleStruct->tera.triggerSpriteId != 0xFF) + DestroySprite(&gSprites[gBattleStruct->tera.triggerSpriteId]); + gBattleStruct->tera.triggerSpriteId = 0xFF; +} + +#undef tBattler +#undef tHide + +// TERA INDICATOR: +static const u16 sTeraIndicatorPal[] = INCBIN_U16("graphics/battle_interface/tera_indicator.gbapal"); +static const u8 ALIGNED(4) sNormalIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/normal_indicator.4bpp"); +static const u8 ALIGNED(4) sFightingIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/fighting_indicator.4bpp"); +static const u8 ALIGNED(4) sFlyingIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/flying_indicator.4bpp"); +static const u8 ALIGNED(4) sPoisonIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/poison_indicator.4bpp"); +static const u8 ALIGNED(4) sGroundIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/ground_indicator.4bpp"); +static const u8 ALIGNED(4) sRockIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/rock_indicator.4bpp"); +static const u8 ALIGNED(4) sBugIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/bug_indicator.4bpp"); +static const u8 ALIGNED(4) sGhostIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/ghost_indicator.4bpp"); +static const u8 ALIGNED(4) sSteelIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/steel_indicator.4bpp"); +static const u8 ALIGNED(4) sFireIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/fire_indicator.4bpp"); +static const u8 ALIGNED(4) sWaterIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/water_indicator.4bpp"); +static const u8 ALIGNED(4) sGrassIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/grass_indicator.4bpp"); +static const u8 ALIGNED(4) sElectricIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/electric_indicator.4bpp"); +static const u8 ALIGNED(4) sPsychicIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/psychic_indicator.4bpp"); +static const u8 ALIGNED(4) sIceIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/ice_indicator.4bpp"); +static const u8 ALIGNED(4) sDragonIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/dragon_indicator.4bpp"); +static const u8 ALIGNED(4) sDarkIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/dark_indicator.4bpp"); +static const u8 ALIGNED(4) sFairyIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/fairy_indicator.4bpp"); +static const u8 ALIGNED(4) sStellarIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/stellar_indicator.4bpp"); + +static void SpriteCb_TeraIndicator(struct Sprite *sprite); +static const s8 sIndicatorPositions[][2] = +{ + [B_POSITION_PLAYER_LEFT] = {53, -9}, + [B_POSITION_OPPONENT_LEFT] = {44, -9}, + [B_POSITION_PLAYER_RIGHT] = {52, -9}, + [B_POSITION_OPPONENT_RIGHT] = {44, -9}, +}; + +static const struct SpritePalette sSpritePalette_TeraIndicator = +{ + sTeraIndicatorPal, TAG_TERA_INDICATOR_PAL +}; + +static const struct OamData sOamData_TeraIndicator = +{ + .shape = SPRITE_SHAPE(16x16), + .size = SPRITE_SIZE(16x16), + .priority = 1, +}; + +static const struct SpriteTemplate sSpriteTemplate_NormalIndicator = +{ + .tileTag = TAG_NORMAL_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_FightingIndicator = +{ + .tileTag = TAG_FIGHTING_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_FlyingIndicator = +{ + .tileTag = TAG_FLYING_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_PoisonIndicator = +{ + .tileTag = TAG_POISON_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_GroundIndicator = +{ + .tileTag = TAG_GROUND_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_RockIndicator = +{ + .tileTag = TAG_ROCK_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_BugIndicator = +{ + .tileTag = TAG_BUG_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_GhostIndicator = +{ + .tileTag = TAG_GHOST_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_SteelIndicator = +{ + .tileTag = TAG_STEEL_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_FireIndicator = +{ + .tileTag = TAG_FIRE_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_WaterIndicator = +{ + .tileTag = TAG_WATER_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_GrassIndicator = +{ + .tileTag = TAG_GRASS_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_ElectricIndicator = +{ + .tileTag = TAG_ELECTRIC_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_PsychicIndicator = +{ + .tileTag = TAG_PSYCHIC_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_IceIndicator = +{ + .tileTag = TAG_ICE_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_DragonIndicator = +{ + .tileTag = TAG_DRAGON_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_DarkIndicator = +{ + .tileTag = TAG_DARK_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_FairyIndicator = +{ + .tileTag = TAG_FAIRY_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_StellarIndicator = +{ + .tileTag = TAG_STELLAR_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteSheet sTeraIndicatorSpriteSheets[NUMBER_OF_MON_TYPES + 1] = +{ + {sNormalIndicatorGfx, sizeof(sNormalIndicatorGfx), TAG_NORMAL_INDICATOR_TILE}, // TYPE_NONE + {sNormalIndicatorGfx, sizeof(sNormalIndicatorGfx), TAG_NORMAL_INDICATOR_TILE}, + {sFightingIndicatorGfx, sizeof(sFightingIndicatorGfx), TAG_FIGHTING_INDICATOR_TILE}, + {sFlyingIndicatorGfx, sizeof(sFlyingIndicatorGfx), TAG_FLYING_INDICATOR_TILE}, + {sPoisonIndicatorGfx, sizeof(sPoisonIndicatorGfx), TAG_POISON_INDICATOR_TILE}, + {sGroundIndicatorGfx, sizeof(sGroundIndicatorGfx), TAG_GROUND_INDICATOR_TILE}, + {sRockIndicatorGfx, sizeof(sRockIndicatorGfx), TAG_ROCK_INDICATOR_TILE}, + {sBugIndicatorGfx, sizeof(sBugIndicatorGfx), TAG_BUG_INDICATOR_TILE}, + {sGhostIndicatorGfx, sizeof(sGhostIndicatorGfx), TAG_GHOST_INDICATOR_TILE}, + {sSteelIndicatorGfx, sizeof(sSteelIndicatorGfx), TAG_STEEL_INDICATOR_TILE}, + {sNormalIndicatorGfx, sizeof(sNormalIndicatorGfx), TAG_NORMAL_INDICATOR_TILE}, // TYPE_MYSTERY + {sFireIndicatorGfx, sizeof(sFireIndicatorGfx), TAG_FIRE_INDICATOR_TILE}, + {sWaterIndicatorGfx, sizeof(sWaterIndicatorGfx), TAG_WATER_INDICATOR_TILE}, + {sGrassIndicatorGfx, sizeof(sGrassIndicatorGfx), TAG_GRASS_INDICATOR_TILE}, + {sElectricIndicatorGfx, sizeof(sElectricIndicatorGfx), TAG_ELECTRIC_INDICATOR_TILE}, + {sPsychicIndicatorGfx, sizeof(sPsychicIndicatorGfx), TAG_PSYCHIC_INDICATOR_TILE}, + {sIceIndicatorGfx, sizeof(sIceIndicatorGfx), TAG_ICE_INDICATOR_TILE}, + {sDragonIndicatorGfx, sizeof(sDragonIndicatorGfx), TAG_DRAGON_INDICATOR_TILE}, + {sDarkIndicatorGfx, sizeof(sDarkIndicatorGfx), TAG_DARK_INDICATOR_TILE}, + {sFairyIndicatorGfx, sizeof(sFairyIndicatorGfx), TAG_FAIRY_INDICATOR_TILE}, + {sStellarIndicatorGfx, sizeof(sStellarIndicatorGfx), TAG_STELLAR_INDICATOR_TILE}, + {0} +}; + +static const struct SpriteTemplate * const sTeraIndicatorSpriteTemplates[NUMBER_OF_MON_TYPES] = +{ + [TYPE_NONE] = &sSpriteTemplate_NormalIndicator, // just in case + [TYPE_NORMAL] = &sSpriteTemplate_NormalIndicator, + [TYPE_FIGHTING] = &sSpriteTemplate_FightingIndicator, + [TYPE_FLYING] = &sSpriteTemplate_FlyingIndicator, + [TYPE_POISON] = &sSpriteTemplate_PoisonIndicator, + [TYPE_GROUND] = &sSpriteTemplate_GroundIndicator, + [TYPE_ROCK] = &sSpriteTemplate_RockIndicator, + [TYPE_BUG] = &sSpriteTemplate_BugIndicator, + [TYPE_GHOST] = &sSpriteTemplate_GhostIndicator, + [TYPE_STEEL] = &sSpriteTemplate_SteelIndicator, + [TYPE_MYSTERY] = &sSpriteTemplate_NormalIndicator, // just in case + [TYPE_FIRE] = &sSpriteTemplate_FireIndicator, + [TYPE_WATER] = &sSpriteTemplate_WaterIndicator, + [TYPE_GRASS] = &sSpriteTemplate_GrassIndicator, + [TYPE_ELECTRIC] = &sSpriteTemplate_ElectricIndicator, + [TYPE_PSYCHIC] = &sSpriteTemplate_PsychicIndicator, + [TYPE_ICE] = &sSpriteTemplate_IceIndicator, + [TYPE_DRAGON] = &sSpriteTemplate_DragonIndicator, + [TYPE_DARK] = &sSpriteTemplate_DarkIndicator, + [TYPE_FAIRY] = &sSpriteTemplate_FairyIndicator, + [TYPE_STELLAR] = &sSpriteTemplate_StellarIndicator, +}; + +// for sprite data fields +#define tBattler data[0] +#define tType data[1] // Indicator type: tera +#define tPosX data[2] +#define tLevelXDelta data[3] // X position depends whether level has 3, 2 or 1 digit + +// data fields for healthboxMain +// oam.affineParam holds healthboxRight spriteId +#define hMain_TeraIndicatorId data[3] +#define hMain_HealthBarSpriteId data[5] +#define hMain_Battler data[6] +#define hMain_Data7 data[7] + +// data fields for healthboxRight +#define hOther_HealthBoxSpriteId data[5] + +// data fields for healthbar +#define hBar_HealthBoxSpriteId data[5] + +void TeraIndicator_LoadSpriteGfx(void) +{ + LoadSpriteSheets(sTeraIndicatorSpriteSheets); + LoadSpritePalette(&sSpritePalette_TeraIndicator); +} + +bool32 TeraIndicator_ShouldBeInvisible(u32 battler) +{ + return !IsTerastallized(battler); +} + +u8 TeraIndicator_GetSpriteId(u32 healthboxSpriteId) +{ + return gBattleStruct->tera.indicatorSpriteId[gSprites[healthboxSpriteId].hMain_Battler]; +} + +void TeraIndicator_SetVisibilities(u32 healthboxId, bool32 invisible) +{ + u8 spriteId = TeraIndicator_GetSpriteId(healthboxId); + u32 battler = gSprites[healthboxId].hMain_Battler; + + if (GetSafariZoneFlag()) + return; + + if (invisible == TRUE) + gSprites[spriteId].invisible = TRUE; + else // Try visible. + gSprites[spriteId].invisible = TeraIndicator_ShouldBeInvisible(battler); +} + +void TeraIndicator_UpdateOamPriorities(u32 healthboxId, u32 oamPriority) +{ + u8 spriteId = TeraIndicator_GetSpriteId(healthboxId); + gSprites[spriteId].oam.priority = oamPriority; +} + +void TeraIndicator_UpdateLevel(u32 healthboxId, u32 level) +{ + s16 xDelta = 0; + u8 spriteId = TeraIndicator_GetSpriteId(healthboxId); + + if (level >= 100) + xDelta -= 4; + else if (level < 10) + xDelta += 5; + + gSprites[spriteId].tLevelXDelta = xDelta; +} + +void TeraIndicator_CreateSprite(u32 battler, u32 healthboxSpriteId) +{ + u32 position; + u8 spriteId; + s16 xHealthbox = 0, y = 0; + s32 x = 0; + u32 type = GetBattlerTeraType(battler); + + position = GetBattlerPosition(battler); + GetBattlerHealthboxCoords(battler, &xHealthbox, &y); + + x = sIndicatorPositions[position][0]; + y += sIndicatorPositions[position][1]; + + spriteId = gBattleStruct->tera.indicatorSpriteId[battler] = CreateSpriteAtEnd(sTeraIndicatorSpriteTemplates[type], 0, y, 0); + gSprites[spriteId].tBattler = battler; + gSprites[spriteId].tPosX = x; + gSprites[spriteId].invisible = TRUE; +} + +void TeraIndicator_DestroySprite(u32 healthboxSpriteId) +{ + u8 spriteId = TeraIndicator_GetSpriteId(healthboxSpriteId); + DestroySprite(&gSprites[spriteId]); +} + +void TeraIndicator_UpdateType(u32 battler, u32 healthboxSpriteId) +{ + TeraIndicator_DestroySprite(healthboxSpriteId); + TeraIndicator_CreateSprite(battler, healthboxSpriteId); +} + +static void SpriteCb_TeraIndicator(struct Sprite *sprite) +{ + u32 battler = sprite->tBattler; + + sprite->x = gSprites[gHealthboxSpriteIds[battler]].x + sprite->tPosX + sprite->tLevelXDelta; + sprite->x2 = gSprites[gHealthboxSpriteIds[battler]].x2; + sprite->y2 = gSprites[gHealthboxSpriteIds[battler]].y2; +} + +#undef tBattler +#undef tType +#undef tPosX +#undef tLevelXDelta diff --git a/src/battle_util.c b/src/battle_util.c index 1fb0359b3..90040f771 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -650,7 +650,7 @@ void HandleAction_NothingIsFainted(void) void HandleAction_ActionFinished(void) { - u32 i, j/* , moveType */; + u32 i, j, moveType; bool32 afterYouActive = gSpecialStatuses[gBattlerByTurnOrder[gCurrentTurnActionNumber + 1]].afterYou; *(gBattleStruct->monToSwitchIntoId + gBattlerByTurnOrder[gCurrentTurnActionNumber]) = gSelectedMonPartyId = PARTY_SIZE; gCurrentTurnActionNumber++; @@ -661,16 +661,15 @@ void HandleAction_ActionFinished(void) | HITMARKER_OBEYS | HITMARKER_WAKE_UP_CLEAR | HITMARKER_SYNCHRONISE_EFFECT | HITMARKER_CHARGING | HITMARKER_NEVER_SET | HITMARKER_IGNORE_DISGUISE); - // TODO: Tera // check if Stellar type boost should be used up - // GET_MOVE_TYPE(gCurrentMove, moveType); - // if (IsTerastallized(gBattlerAttacker) - // && GetBattlerTeraType(gBattlerAttacker) == TYPE_STELLAR - // && gMovesInfo[gCurrentMove].category != DAMAGE_CATEGORY_STATUS - // && IsTypeStellarBoosted(gBattlerAttacker, moveType)) - // { - // ExpendTypeStellarBoost(gBattlerAttacker, moveType); - // } + GET_MOVE_TYPE(gCurrentMove, moveType); + if (IsTerastallized(gBattlerAttacker) + && GetBattlerTeraType(gBattlerAttacker) == TYPE_STELLAR + && gMovesInfo[gCurrentMove].category != DAMAGE_CATEGORY_STATUS + && IsTypeStellarBoosted(gBattlerAttacker, moveType)) + { + ExpendTypeStellarBoost(gBattlerAttacker, moveType); + } gCurrentMove = 0; gBattleMoveDamage = 0; @@ -1202,7 +1201,7 @@ bool32 IsBelchPreventingMove(u32 battler, u32 move) u32 TrySetCantSelectMoveBattleScript(u32 battler) { u32 limitations = 0; - u8 moveId = gBattleResources->bufferB[battler][2] & ~(RET_MEGA_EVOLUTION | RET_ULTRA_BURST | RET_DYNAMAX/* | RET_TERASTAL */); // TODO: Tera + u8 moveId = gBattleResources->bufferB[battler][2] & ~(RET_MEGA_EVOLUTION | RET_ULTRA_BURST | RET_DYNAMAX | RET_TERASTAL); u32 move = gBattleMons[battler].moves[moveId]; u32 holdEffect = GetBattlerHoldEffect(battler, TRUE); u16 *choicedMove = &gBattleStruct->choicedMove[battler]; @@ -4427,15 +4426,14 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 effect++; } break; - // TODO: Tera - // case ABILITY_TERAFORM_ZERO: - // if (!gSpecialStatuses[battler].switchInAbilityDone - // && gBattleMons[battler].species == SPECIES_TERAPAGOS_STELLAR) - // { - // gSpecialStatuses[battler].switchInAbilityDone = TRUE; - // BattleScriptPushCursorAndCallback(BattleScript_ActivateTeraformZero); - // effect++; - // } + case ABILITY_TERAFORM_ZERO: + if (!gSpecialStatuses[battler].switchInAbilityDone + && gBattleMons[battler].species == SPECIES_TERAPAGOS_STELLAR) + { + gSpecialStatuses[battler].switchInAbilityDone = TRUE; + BattleScriptPushCursorAndCallback(BattleScript_ActivateTeraformZero); + effect++; + } case ABILITY_SCHOOLING: if (gBattleMons[battler].level < 20) break; @@ -4627,19 +4625,18 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 effect++; } break; - // TODO: Tera - // case ABILITY_TERA_SHIFT: - // if (!gSpecialStatuses[battler].switchInAbilityDone - // && gBattleMons[battler].species == SPECIES_TERAPAGOS_NORMAL - // && TryBattleFormChange(battler, FORM_CHANGE_BATTLE_SWITCH)) - // { - // gBattlerAttacker = battler; - // gBattleScripting.abilityPopupOverwrite = gLastUsedAbility = ABILITY_TERA_SHIFT; - // gSpecialStatuses[battler].switchInAbilityDone = TRUE; - // BattleScriptPushCursorAndCallback(BattleScript_AttackerFormChangeWithStringEnd3); - // effect++; - // } - // break; + case ABILITY_TERA_SHIFT: + if (!gSpecialStatuses[battler].switchInAbilityDone + && gBattleMons[battler].species == SPECIES_TERAPAGOS_NORMAL + && TryBattleFormChange(battler, FORM_CHANGE_BATTLE_SWITCH)) + { + gBattlerAttacker = battler; + gBattleScripting.abilityPopupOverwrite = gLastUsedAbility = ABILITY_TERA_SHIFT; + gSpecialStatuses[battler].switchInAbilityDone = TRUE; + BattleScriptPushCursorAndCallback(BattleScript_AttackerFormChangeWithStringEnd3); + effect++; + } + break; } break; case ABILITYEFFECT_ENDTURN: @@ -8718,11 +8715,10 @@ static inline u32 CalcMoveBasePower(u32 move, u32 battlerAtk, u32 battlerDef, u3 if (RandomPercentage(RNG_FICKLE_BEAM, 30)) basePower *= 2; break; - // TODO: Tera - // case EFFECT_TERA_BLAST: - // if (IsTerastallized(battlerAtk) && GetBattlerTeraType(battlerAtk) == TYPE_STELLAR) - // basePower = 100; - // break; + case EFFECT_TERA_BLAST: + if (IsTerastallized(battlerAtk) && GetBattlerTeraType(battlerAtk) == TYPE_STELLAR) + basePower = 100; + break; case EFFECT_LAST_RESPECTS: basePower += (basePower * min(100, GetBattlerSideFaintCounter(battlerAtk))); break; @@ -9091,18 +9087,17 @@ static inline u32 CalcMoveBasePowerAfterModifiers(u32 move, u32 battlerAtk, u32 break; } - // TODO: Tera // Terastallization boosts weak, non-priority, non-multi hit moves after modifiers to 60 BP. - // if (IsTerastallized(battlerAtk) - // && (moveType == GetBattlerTeraType(battlerAtk) - // || (GetBattlerTeraType(battlerAtk) == TYPE_STELLAR && IsTypeStellarBoosted(battlerAtk, moveType))) - // && uq4_12_multiply_by_int_half_down(modifier, basePower) < 60 - // && gMovesInfo[move].strikeCount < 2 - // && gMovesInfo[move].effect != EFFECT_MULTI_HIT - // && gMovesInfo[move].priority == 0) - // { - // return 60; - // } + if (IsTerastallized(battlerAtk) + && (moveType == GetBattlerTeraType(battlerAtk) + || (GetBattlerTeraType(battlerAtk) == TYPE_STELLAR && IsTypeStellarBoosted(battlerAtk, moveType))) + && uq4_12_multiply_by_int_half_down(modifier, basePower) < 60 + && gMovesInfo[move].strikeCount < 2 + && gMovesInfo[move].effect != EFFECT_MULTI_HIT + && gMovesInfo[move].priority == 0) + { + return 60; + } return uq4_12_multiply_by_int_half_down(modifier, basePower); } @@ -9806,10 +9801,9 @@ static inline s32 DoMoveDamageCalcVars(u32 move, u32 battlerAtk, u32 battlerDef, dmg /= 100; } - // TODO: Tera - // if (IsTerastallized(battlerAtk)) - // DAMAGE_APPLY_MODIFIER(GetTeraMultiplier(battlerAtk, moveType)); - // else + if (IsTerastallized(battlerAtk)) + DAMAGE_APPLY_MODIFIER(GetTeraMultiplier(battlerAtk, moveType)); + else DAMAGE_APPLY_MODIFIER(GetSameTypeAttackBonusModifier(battlerAtk, moveType, move, abilityAtk)); DAMAGE_APPLY_MODIFIER(typeEffectivenessModifier); DAMAGE_APPLY_MODIFIER(GetBurnOrFrostBiteModifier(battlerAtk, move, abilityAtk)); @@ -9965,7 +9959,7 @@ static inline void MulByTypeEffectiveness(uq4_12_t *modifier, u32 move, u32 move mod = UQ_4_12(2.0); if (moveType == TYPE_GROUND && defType == TYPE_FLYING && IsBattlerGrounded(battlerDef) && mod == UQ_4_12(0.0)) mod = UQ_4_12(1.0); - if (FALSE/* moveType == TYPE_STELLAR && IsTerastallized(battlerDef) */) // TODO: Tera + if (moveType == TYPE_STELLAR && IsTerastallized(battlerDef)) mod = UQ_4_12(2.0); // B_WEATHER_STRONG_WINDS weakens Super Effective moves against Flying-type Pokémon @@ -9975,7 +9969,7 @@ static inline void MulByTypeEffectiveness(uq4_12_t *modifier, u32 move, u32 move mod = UQ_4_12(1.0); } - if (gBattleStruct->distortedTypeMatchups & gBitTable[battlerDef] /* || (gBattleStruct->aiCalcInProgress && ShouldTeraShellDistortTypeMatchups(move, battlerDef)) */) // TODO: Tera + if (gBattleStruct->distortedTypeMatchups & gBitTable[battlerDef] || (gBattleStruct->aiCalcInProgress && ShouldTeraShellDistortTypeMatchups(move, battlerDef))) { mod = UQ_4_12(0.5); if (recordAbilities) @@ -10409,14 +10403,13 @@ bool32 IsBattlerUltraBursted(u32 battler) return (gSpeciesInfo[gBattleMons[battler].species].isUltraBurst); } -// TODO: Tera -// 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); -// } +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) @@ -10508,11 +10501,10 @@ u16 GetBattleFormChangeTargetSpecies(u32 battler, u16 method) if (gBattleMons[battler].status1 & formChanges[i].param1) targetSpecies = formChanges[i].targetSpecies; break; - // TODO: Tera - // case FORM_CHANGE_BATTLE_TERASTALLIZATION: - // if (GetBattlerTeraType(battler) == formChanges[i].param1) - // targetSpecies = formChanges[i].targetSpecies; - // break; + case FORM_CHANGE_BATTLE_TERASTALLIZATION: + if (GetBattlerTeraType(battler) == formChanges[i].param1) + targetSpecies = formChanges[i].targetSpecies; + break; } } } @@ -10528,7 +10520,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)/* || IsBattlerInTeraForm(battler) */) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_END_BATTLE)) // TODO: Tera + 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; @@ -10568,7 +10560,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) /* || IsBattlerInTeraForm(battler) */) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_END_BATTLE)) // TODO: Tera + 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. @@ -11347,22 +11339,21 @@ bool8 IsMonBannedFromSkyBattles(u16 species) u8 GetBattlerType(u32 battler, u8 typeIndex, bool32 ignoreTera) { - // u32 teraType = GetBattlerTeraType(battler); // TODO: Tera + u32 teraType = GetBattlerTeraType(battler); u16 types[3] = {0}; types[0] = gBattleMons[battler].type1; types[1] = gBattleMons[battler].type2; types[2] = gBattleMons[battler].type3; // Handle Terastallization - // TODO: Tera - // if (IsTerastallized(battler) && teraType != TYPE_STELLAR && !ignoreTera) - // return GetBattlerTeraType(battler); + if (IsTerastallized(battler) && teraType != TYPE_STELLAR && !ignoreTera) + return GetBattlerTeraType(battler); // Handle Roost's Flying-type suppression if (typeIndex == 0 || typeIndex == 1) { if (gBattleResources->flags->flags[battler] & RESOURCE_FLAG_ROOST - /* && !IsTerastallized(battler) */) // TODO: Tera + && !IsTerastallized(battler)) { if (types[0] == TYPE_FLYING && types[1] == TYPE_FLYING) return B_ROOST_PURE_FLYING >= GEN_5 ? TYPE_NORMAL : TYPE_MYSTERY; @@ -11377,9 +11368,8 @@ u8 GetBattlerType(u32 battler, u8 typeIndex, bool32 ignoreTera) void RemoveBattlerType(u32 battler, u8 type) { u32 i; - // TODO: Tera - // if (IsTerastallized(battler)) // don't remove type if Terastallized - // return; + if (IsTerastallized(battler)) // don't remove type if Terastallized + return; for (i = 0; i < 3; i++) { if (*(u8 *)(&gBattleMons[battler].type1 + i) == type)