terastal mechanically integrated

This commit is contained in:
cawtds 2024-06-18 18:16:49 +02:00
parent 97b1b5f176
commit 4adea56576
32 changed files with 1002 additions and 153 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

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

View File

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

View File

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

View File

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

31
include/battle_terastal.h Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

775
src/battle_terastal.c Normal file
View File

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

View File

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