Terastallization (#4110)
* wrote foundational terastal tests * implemented baseline test-only Tera functionality; modified GetBattlerType + STAB calculations, misc. changes to some moves * added tests and func. for Stellar type, more tests for Tera Blast * more tests for Stellar type, Conversion fixes, Color Change + Conversion2 future proof * implemented tera blast, expanded stellar type func., fixed tests * last set of Tera/Tera Blast tests for checklist, protean fix * implemented in-battle Terastallization, WIP stellar indicator and tera animation * fixed bad merge * expanded NUMBER_OF_MON_TYPES, cut down on TYPE_STELLAR hackiness, added Stellar type to summary * fixed type indicators * added tera logic to AI * implemented code review changes, added B_TERA_ORB_NO_COST * updated AI to calc damage with Tera when applicable; minor rework to AI gimmick handling * fixed Tera Blast split choice occuring when not Terastallized * fixed Tera Blast using Last Respects BP calcs * added tera type to TrainerMon, code review tweaks
|
@ -20,6 +20,34 @@
|
|||
|
||||
.section script_data, "aw", %progbits
|
||||
|
||||
BattleScript_Terastallization::
|
||||
@ TODO: no string prints in S/V, but right now this helps with clarity
|
||||
printstring STRINGID_PKMNTERASTALLIZEDINTO
|
||||
@ TODO: replace this animation
|
||||
playanimation BS_ATTACKER, B_ANIM_TOTEM_FLARE
|
||||
waitanimation
|
||||
end3
|
||||
|
||||
BattleScript_LowerAtkSpAtk::
|
||||
jumpifstat BS_EFFECT_BATTLER, CMP_GREATER_THAN, STAT_ATK, MIN_STAT_STAGE, BattleScript_LowerAtkSpAtkDoAnim
|
||||
jumpifstat BS_EFFECT_BATTLER, CMP_EQUAL, STAT_SPATK, MIN_STAT_STAGE, BattleScript_LowerAtkSpAtkEnd
|
||||
BattleScript_LowerAtkSpAtkDoAnim::
|
||||
setbyte sSTAT_ANIM_PLAYED, FALSE
|
||||
playstatchangeanimation BS_EFFECT_BATTLER, BIT_ATK | BIT_SPATK, STAT_CHANGE_NEGATIVE
|
||||
setstatchanger STAT_ATK, 1, TRUE
|
||||
statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_ALLOW_PTR, BattleScript_LowerAtkSpAtkTrySpAtk
|
||||
jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_DECREASE, BattleScript_LowerAtkSpAtkTrySpAtk
|
||||
printfromtable gStatDownStringIds
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
BattleScript_LowerAtkSpAtkTrySpAtk::
|
||||
setstatchanger STAT_SPATK, 1, TRUE
|
||||
statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_ALLOW_PTR, BattleScript_LowerAtkSpAtkEnd
|
||||
jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_DECREASE, BattleScript_LowerAtkSpAtkEnd
|
||||
printfromtable gStatDownStringIds
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
BattleScript_LowerAtkSpAtkEnd:
|
||||
return
|
||||
|
||||
BattleScript_EffectTidyUp::
|
||||
attackcanceler
|
||||
attackstring
|
||||
|
|
BIN
graphics/battle_interface/bug_indicator.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
graphics/battle_interface/dark_indicator.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
graphics/battle_interface/dragon_indicator.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
graphics/battle_interface/electric_indicator.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
graphics/battle_interface/fairy_indicator.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
graphics/battle_interface/fighting_indicator.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
graphics/battle_interface/fire_indicator.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
graphics/battle_interface/flying_indicator.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
graphics/battle_interface/ghost_indicator.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
graphics/battle_interface/grass_indicator.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
graphics/battle_interface/ground_indicator.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
graphics/battle_interface/ice_indicator.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
graphics/battle_interface/normal_indicator.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
graphics/battle_interface/poison_indicator.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
graphics/battle_interface/psychic_indicator.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
graphics/battle_interface/rock_indicator.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
graphics/battle_interface/steel_indicator.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
graphics/battle_interface/stellar_indicator.png
Normal file
After Width: | Height: | Size: 5.3 KiB |
19
graphics/battle_interface/tera_indicator.pal
Normal 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
|
BIN
graphics/battle_interface/tera_trigger.png
Normal file
After Width: | Height: | Size: 7.3 KiB |
BIN
graphics/battle_interface/water_indicator.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
graphics/types/stellar.png
Normal file
After Width: | Height: | Size: 5.7 KiB |
|
@ -22,7 +22,7 @@ STARTERGFXDIR := graphics/starter_choose
|
|||
NAMINGGFXDIR := graphics/naming_screen
|
||||
SPINDAGFXDIR := graphics/pokemon/spinda/spots
|
||||
|
||||
types := normal fight flying poison ground rock bug ghost steel mystery fire water grass electric psychic ice dragon dark fairy
|
||||
types := normal fight flying poison ground rock bug ghost steel mystery fire water grass electric psychic ice dragon dark fairy stellar
|
||||
contest_types := cool beauty cute smart tough
|
||||
|
||||
### Tilesets ###
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "pokeball.h"
|
||||
#include "battle_debug.h"
|
||||
#include "battle_dynamax.h"
|
||||
#include "battle_terastal.h"
|
||||
#include "random.h" // for rng_value_t
|
||||
|
||||
// Helper for accessing command arguments and advancing gBattlescriptCurrInstr.
|
||||
|
@ -363,6 +364,8 @@ struct AiLogicData
|
|||
bool8 weatherHasEffect; // The same as WEATHER_HAS_EFFECT. Stored here, so it's called only once.
|
||||
u8 mostSuitableMonId[MAX_BATTLERS_COUNT]; // Stores result of GetMostSuitableMonToSwitchInto, which decides which generic mon the AI would switch into if they decide to switch. This can be overruled by specific mons found in ShouldSwitch; the final resulting mon is stored in AI_monToSwitchIntoId.
|
||||
struct SwitchinCandidate switchinCandidate; // Struct used for deciding which mon to switch to in battle_ai_switch_items.c
|
||||
bool8 shouldTerastal[MAX_BATTLERS_COUNT];
|
||||
bool8 shouldDynamax[MAX_BATTLERS_COUNT];
|
||||
};
|
||||
|
||||
struct AI_ThinkingStruct
|
||||
|
@ -611,6 +614,17 @@ struct DynamaxData
|
|||
u16 levelUpHP;
|
||||
};
|
||||
|
||||
struct TeraData
|
||||
{
|
||||
bool8 toTera; // flags using gBitTable
|
||||
bool8 isTerastallized[NUM_BATTLE_SIDES]; // stored as a bitfield for each side's party members
|
||||
bool8 alreadyTerastallized[MAX_BATTLERS_COUNT];
|
||||
bool8 playerSelect;
|
||||
u32 stellarBoostFlags[NUM_BATTLE_SIDES]; // stored as a bitfield of flags for all types for each side
|
||||
u8 triggerSpriteId;
|
||||
u8 indicatorSpriteId[MAX_BATTLERS_COUNT];
|
||||
};
|
||||
|
||||
struct LostItem
|
||||
{
|
||||
u16 originalItem:15;
|
||||
|
@ -733,6 +747,7 @@ struct BattleStruct
|
|||
struct UltraBurstData burst;
|
||||
struct ZMoveData zmove;
|
||||
struct DynamaxData dynamax;
|
||||
struct TeraData tera;
|
||||
const u8 *trainerSlideMsg;
|
||||
bool8 trainerSlideLowHpMsgDone;
|
||||
u8 introState;
|
||||
|
@ -830,9 +845,9 @@ STATIC_ASSERT(sizeof(((struct BattleStruct *)0)->palaceFlags) * 8 >= MAX_BATTLER
|
|||
#define TARGET_TURN_DAMAGED ((gSpecialStatuses[gBattlerTarget].physicalDmg != 0 || gSpecialStatuses[gBattlerTarget].specialDmg != 0) || (gBattleStruct->enduredDamage & gBitTable[gBattlerTarget]))
|
||||
#define BATTLER_TURN_DAMAGED(battlerId) ((gSpecialStatuses[battlerId].physicalDmg != 0 || gSpecialStatuses[battlerId].specialDmg != 0) || (gBattleStruct->enduredDamage & gBitTable[battler]))
|
||||
|
||||
#define IS_BATTLER_OF_TYPE(battlerId, type)((GetBattlerType(battlerId, 0) == type || GetBattlerType(battlerId, 1) == type || (GetBattlerType(battlerId, 2) != TYPE_MYSTERY && GetBattlerType(battlerId, 2) == type)))
|
||||
|
||||
#define IS_BATTLER_TYPELESS(battlerId)(GetBattlerType(battlerId, 0) == TYPE_MYSTERY && GetBattlerType(battlerId, 1) == TYPE_MYSTERY && GetBattlerType(battlerId, 2) == TYPE_MYSTERY)
|
||||
#define IS_BATTLER_OF_TYPE(battlerId, type)((GetBattlerType(battlerId, 0, FALSE) == type || GetBattlerType(battlerId, 1, FALSE) == type || (GetBattlerType(battlerId, 2, FALSE) != TYPE_MYSTERY && GetBattlerType(battlerId, 2, FALSE) == type)))
|
||||
#define IS_BATTLER_OF_BASE_TYPE(battlerId, type)((GetBattlerType(battlerId, 0, TRUE) == type || GetBattlerType(battlerId, 1, TRUE) == type || (GetBattlerType(battlerId, 2, TRUE) != TYPE_MYSTERY && GetBattlerType(battlerId, 2, TRUE) == type)))
|
||||
#define IS_BATTLER_TYPELESS(battlerId)(GetBattlerType(battlerId, 0, FALSE) == TYPE_MYSTERY && GetBattlerType(battlerId, 1, FALSE) == TYPE_MYSTERY && GetBattlerType(battlerId, 2, FALSE) == TYPE_MYSTERY)
|
||||
|
||||
#define SET_BATTLER_TYPE(battlerId, type) \
|
||||
{ \
|
||||
|
|
|
@ -101,6 +101,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
|
||||
{
|
||||
|
|
|
@ -57,12 +57,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
|
||||
{
|
||||
|
|
|
@ -499,6 +499,8 @@ extern const u8 BattleScript_TheSwampDisappeared[];
|
|||
extern const u8 BattleScript_ItemRestoreHP_Party[];
|
||||
extern const u8 BattleScript_EffectPsychicNoise[];
|
||||
extern const u8 BattleScript_AromaVeilProtectsRet[];
|
||||
extern const u8 BattleScript_LowerAtkSpAtk[];
|
||||
extern const u8 BattleScript_Terastallization[];
|
||||
extern const u8 BattleScript_BoosterEnergyEnd2[];
|
||||
|
||||
// zmoves
|
||||
|
|
30
include/battle_terastal.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#ifndef GUARD_BATTLE_TERASTAL_H
|
||||
#define GUARD_BATTLE_TERASTAL_H
|
||||
|
||||
void PrepareBattlerForTera(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
|
|
@ -250,7 +250,7 @@ bool32 AreBattlersOfOppositeGender(u32 battler1, u32 battler2);
|
|||
bool32 AreBattlersOfSameGender(u32 battler1, u32 battler2);
|
||||
u32 CalcSecondaryEffectChance(u32 battler, u32 battlerAbility, const struct AdditionalEffect *additionalEffect);
|
||||
bool32 MoveEffectIsGuaranteed(u32 battler, u32 battlerAbility, const struct AdditionalEffect *additionalEffect);
|
||||
u8 GetBattlerType(u32 battler, u8 typeIndex);
|
||||
u8 GetBattlerType(u32 battler, u8 typeIndex, bool32 ignoreTera);
|
||||
bool8 CanMonParticipateInSkyBattle(struct Pokemon *mon);
|
||||
bool8 IsMonBannedFromSkyBattles(u16 species);
|
||||
void RemoveBattlerType(u32 battler, u8 type);
|
||||
|
|
|
@ -179,6 +179,8 @@
|
|||
#define B_FLAG_NO_CATCHING 0 // If this flag is set, the ability to catch wild Pokémon is disabled.
|
||||
#define B_FLAG_AI_VS_AI_BATTLE 0 // If this flag is set, the player's mons will be controlled by the ai next battles.
|
||||
#define B_FLAG_DYNAMAX_BATTLE 0 // If this flag is set, the ability to Dynamax in battle is enabled for all trainers.
|
||||
#define B_FLAG_TERA_ORB_CHARGED 0 // If this flag is set, the Tera Orb is charged. It is automatically set upon healing and cleared upon Terastallizing once configured.
|
||||
#define B_FLAG_TERA_ORB_NO_COST 0 // If this flag is set, the Tera Orb does not use up its charge upon Terastallization. In S/V, this occurs after an event with Terapagos.
|
||||
|
||||
// Var Settings
|
||||
// To use the following features in scripting, replace the 0s with the var ID you're assigning it to.
|
||||
|
|
|
@ -402,8 +402,9 @@
|
|||
#define MOVE_EFFECT_FLORAL_HEALING 77
|
||||
#define MOVE_EFFECT_SECRET_POWER 78
|
||||
#define MOVE_EFFECT_PSYCHIC_NOISE 79
|
||||
#define MOVE_EFFECT_TERA_BLAST 80
|
||||
|
||||
#define NUM_MOVE_EFFECTS 80
|
||||
#define NUM_MOVE_EFFECTS 81
|
||||
|
||||
#define MOVE_EFFECT_AFFECTS_USER 0x2000
|
||||
#define MOVE_EFFECT_CERTAIN 0x4000
|
||||
|
|
|
@ -350,6 +350,7 @@ enum {
|
|||
EFFECT_DRAGON_CHEER,
|
||||
EFFECT_LAST_RESPECTS,
|
||||
EFFECT_TIDY_UP,
|
||||
EFFECT_TERA_BLAST,
|
||||
NUM_BATTLE_MOVE_EFFECTS,
|
||||
};
|
||||
|
||||
|
|
|
@ -707,12 +707,13 @@
|
|||
#define STRINGID_BIZARREARENACREATED 705
|
||||
#define STRINGID_BIZARREAREACREATED 706
|
||||
#define STRINGID_TIDYINGUPCOMPLETE 707
|
||||
#define STRINGID_BOOSTERENERGYACTIVATES 708
|
||||
#define STRINGID_FOGCREPTUP 709
|
||||
#define STRINGID_FOGISDEEP 710
|
||||
#define STRINGID_FOGLIFTED 711
|
||||
#define STRINGID_PKMNTERASTALLIZEDINTO 708
|
||||
#define STRINGID_BOOSTERENERGYACTIVATES 709
|
||||
#define STRINGID_FOGCREPTUP 710
|
||||
#define STRINGID_FOGISDEEP 711
|
||||
#define STRINGID_FOGLIFTED 712
|
||||
|
||||
#define BATTLESTRINGS_COUNT 712
|
||||
#define BATTLESTRINGS_COUNT 713
|
||||
|
||||
// This is the string id that gBattleStringsTable starts with.
|
||||
// String ids before this (e.g. STRINGID_INTROMSG) are not in the table,
|
||||
|
|
|
@ -22,7 +22,8 @@
|
|||
#define TYPE_DRAGON 16
|
||||
#define TYPE_DARK 17
|
||||
#define TYPE_FAIRY 18
|
||||
#define NUMBER_OF_MON_TYPES 19
|
||||
#define TYPE_STELLAR 19
|
||||
#define NUMBER_OF_MON_TYPES 20
|
||||
|
||||
// Pokémon egg groups
|
||||
#define EGG_GROUP_NONE 0
|
||||
|
|
|
@ -71,6 +71,7 @@ struct TrainerMon
|
|||
bool8 gender:2;
|
||||
bool8 isShiny:1;
|
||||
u8 dynamaxLevel:4;
|
||||
u8 teraType:5;
|
||||
bool8 gigantamaxFactor:1;
|
||||
bool8 shouldDynamax:1;
|
||||
bool8 shouldTerastal:1;
|
||||
|
|
|
@ -935,6 +935,8 @@ struct MoveContext
|
|||
// TODO: u8 zMove:1;
|
||||
u16 dynamax:1;
|
||||
u16 explicitDynamax:1;
|
||||
u16 tera:1;
|
||||
u16 explicitTera:1;
|
||||
u16 allowed:1;
|
||||
u16 explicitAllowed:1;
|
||||
u16 notExpected:1; // Has effect only with EXPECT_MOVE
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "battle_factory.h"
|
||||
#include "battle_setup.h"
|
||||
#include "battle_z_move.h"
|
||||
#include "battle_terastal.h"
|
||||
#include "data.h"
|
||||
#include "debug.h"
|
||||
#include "event_data.h"
|
||||
|
@ -398,6 +399,23 @@ static void SetBattlerAiData(u32 battler, struct AiLogicData *aiData)
|
|||
aiData->speedStats[battler] = GetBattlerTotalSpeedStatArgs(battler, ability, holdEffect);
|
||||
}
|
||||
|
||||
static void SetBattlerAiGimmickData(u32 battler, struct AiLogicData *aiData)
|
||||
{
|
||||
bool32 isSecondTrainer = (GetBattlerPosition(battler) == B_POSITION_OPPONENT_RIGHT) && (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) && !BATTLE_TWO_VS_ONE_OPPONENT;
|
||||
u16 trainerId = isSecondTrainer ? gTrainerBattleOpponent_B : gTrainerBattleOpponent_A;
|
||||
const struct TrainerMon *party = GetTrainerPartyFromId(trainerId);
|
||||
if (party != NULL)
|
||||
{
|
||||
aiData->shouldDynamax[battler] = CanDynamax(battler) && (party[isSecondTrainer ? gBattlerPartyIndexes[battler] - MULTI_PARTY_SIZE : gBattlerPartyIndexes[battler]].shouldDynamax);
|
||||
aiData->shouldTerastal[battler] = CanTerastallize(battler) && (party[isSecondTrainer ? gBattlerPartyIndexes[battler] - MULTI_PARTY_SIZE : gBattlerPartyIndexes[battler]].shouldTerastal);
|
||||
}
|
||||
else
|
||||
{
|
||||
aiData->shouldDynamax[battler] = FALSE;
|
||||
aiData->shouldTerastal[battler] = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static u32 Ai_SetMoveAccuracy(struct AiLogicData *aiData, u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
{
|
||||
u32 accuracy;
|
||||
|
@ -467,6 +485,7 @@ void SetAiLogicDataForTurn(struct AiLogicData *aiData)
|
|||
continue;
|
||||
|
||||
SetBattlerAiData(battlerAtk, aiData);
|
||||
SetBattlerAiGimmickData(battlerAtk, aiData);
|
||||
SetBattlerAiMovesData(aiData, battlerAtk, battlersCount);
|
||||
}
|
||||
}
|
||||
|
@ -2367,20 +2386,20 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
|||
break;
|
||||
case EFFECT_SOAK:
|
||||
if (PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)
|
||||
|| (GetBattlerType(battlerDef, 0) == TYPE_WATER
|
||||
&& GetBattlerType(battlerDef, 1) == TYPE_WATER
|
||||
&& GetBattlerType(battlerDef, 2) == TYPE_MYSTERY))
|
||||
|| (GetBattlerType(battlerDef, 0, FALSE) == TYPE_WATER
|
||||
&& GetBattlerType(battlerDef, 1, FALSE) == TYPE_WATER
|
||||
&& GetBattlerType(battlerDef, 2, FALSE) == TYPE_MYSTERY))
|
||||
ADJUST_SCORE(-10); // target is already water-only
|
||||
break;
|
||||
case EFFECT_THIRD_TYPE:
|
||||
switch (move)
|
||||
{
|
||||
case MOVE_TRICK_OR_TREAT:
|
||||
if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GHOST) || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove))
|
||||
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))
|
||||
if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS) || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove) || IsTerastallized(battlerDef))
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
}
|
||||
|
@ -2514,9 +2533,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
|||
case EFFECT_SYNCHRONOISE:
|
||||
//Check holding ring target or is of same type
|
||||
if (aiData->holdEffects[battlerDef] == HOLD_EFFECT_RING_TARGET
|
||||
|| IS_BATTLER_OF_TYPE(battlerDef, GetBattlerType(battlerAtk, 0))
|
||||
|| IS_BATTLER_OF_TYPE(battlerDef, GetBattlerType(battlerAtk, 1))
|
||||
|| IS_BATTLER_OF_TYPE(battlerDef, GetBattlerType(battlerAtk, 2)))
|
||||
|| DoBattlersShareType(battlerAtk, battlerDef))
|
||||
break;
|
||||
else
|
||||
ADJUST_SCORE(-10);
|
||||
|
@ -2940,9 +2957,8 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
|||
break;
|
||||
case EFFECT_SOAK:
|
||||
if (atkPartnerAbility == ABILITY_WONDER_GUARD
|
||||
&& (GetBattlerType(battlerAtkPartner, 0) != TYPE_WATER
|
||||
|| GetBattlerType(battlerAtkPartner, 1) != TYPE_WATER
|
||||
|| GetBattlerType(battlerAtkPartner, 2) != TYPE_WATER))
|
||||
&& IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_WATER)
|
||||
&& !IsTerastallized(battlerAtkPartner))
|
||||
{
|
||||
RETURN_SCORE_PLUS(WEAK_EFFECT);
|
||||
}
|
||||
|
|
|
@ -448,14 +448,16 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes
|
|||
s32 dmg, moveType;
|
||||
uq4_12_t effectivenessMultiplier;
|
||||
bool32 isDamageMoveUnusable = FALSE;
|
||||
bool32 toggledDynamax = FALSE;
|
||||
bool32 toggledTera = FALSE;
|
||||
struct AiLogicData *aiData = AI_DATA;
|
||||
|
||||
SetBattlerData(battlerAtk);
|
||||
SetBattlerData(battlerDef);
|
||||
|
||||
// Temporarily enable Z-Moves for damage calcs
|
||||
if (considerZPower && IsViableZMove(battlerAtk, move))
|
||||
{
|
||||
//temporarily enable z moves for damage calcs
|
||||
gBattleStruct->zmove.baseMoves[battlerAtk] = move;
|
||||
gBattleStruct->zmove.active = TRUE;
|
||||
}
|
||||
|
@ -465,6 +467,18 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes
|
|||
if (gMovesInfo[move].effect == EFFECT_NATURE_POWER)
|
||||
move = GetNaturePowerMove();
|
||||
|
||||
// Temporarily enable other gimmicks for damage calcs if planned
|
||||
if (AI_DATA->shouldDynamax[battlerAtk])
|
||||
{
|
||||
toggledDynamax = TRUE;
|
||||
gBattleStruct->dynamax.dynamaxed[battlerAtk] = TRUE;
|
||||
}
|
||||
if (AI_DATA->shouldTerastal[battlerAtk])
|
||||
{
|
||||
toggledTera = TRUE;
|
||||
gBattleStruct->tera.isTerastallized[GetBattlerSide(battlerAtk)] |= gBitTable[gBattlerPartyIndexes[battlerAtk]];
|
||||
}
|
||||
|
||||
gBattleStruct->dynamicMoveType = 0;
|
||||
|
||||
|
||||
|
@ -582,6 +596,10 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes
|
|||
gBattleStruct->swapDamageCategory = FALSE;
|
||||
gBattleStruct->zmove.active = FALSE;
|
||||
gBattleStruct->zmove.baseMoves[battlerAtk] = MOVE_NONE;
|
||||
if (toggledDynamax)
|
||||
gBattleStruct->dynamax.dynamaxed[battlerAtk] = FALSE;
|
||||
if (toggledTera)
|
||||
gBattleStruct->tera.isTerastallized[GetBattlerSide(battlerAtk)] &= ~(gBitTable[gBattlerPartyIndexes[battlerAtk]]);
|
||||
|
||||
return dmg;
|
||||
}
|
||||
|
|
|
@ -544,13 +544,6 @@ static void OpponentHandleChooseMove(u32 battler)
|
|||
default:
|
||||
{
|
||||
u16 chosenMove = moveInfo->moves[chosenMoveId];
|
||||
bool32 isSecondTrainer = (GetBattlerPosition(battler) == B_POSITION_OPPONENT_RIGHT) && (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) && !BATTLE_TWO_VS_ONE_OPPONENT;
|
||||
u16 trainerId = isSecondTrainer ? gTrainerBattleOpponent_B : gTrainerBattleOpponent_A;
|
||||
const struct TrainerMon *party = GetTrainerPartyFromId(trainerId);
|
||||
bool32 shouldDynamax = FALSE;
|
||||
if (party != NULL)
|
||||
shouldDynamax = party[isSecondTrainer ? gBattlerPartyIndexes[battler] - MULTI_PARTY_SIZE : gBattlerPartyIndexes[battler]].shouldDynamax;
|
||||
|
||||
if (GetBattlerMoveTargetType(battler, chosenMove) & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER))
|
||||
gBattlerTarget = battler;
|
||||
if (GetBattlerMoveTargetType(battler, chosenMove) & MOVE_TARGET_BOTH)
|
||||
|
@ -568,8 +561,11 @@ static void OpponentHandleChooseMove(u32 battler)
|
|||
else if (CanUltraBurst(battler))
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_ULTRA_BURST) | (gBattlerTarget << 8));
|
||||
// If opponent can Dynamax and is allowed in the partydata, do it.
|
||||
else if (CanDynamax(battler) && shouldDynamax)
|
||||
else if (CanDynamax(battler) && AI_DATA->shouldDynamax[battler])
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_DYNAMAX) | (gBattlerTarget << 8));
|
||||
// If opponent can Terastal and is allowed in the partydata, do it.
|
||||
else if (CanTerastallize(battler) && AI_DATA->shouldTerastal[battler])
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_TERASTAL) | (gBattlerTarget << 8));
|
||||
else
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (gBattlerTarget << 8));
|
||||
}
|
||||
|
|
|
@ -454,6 +454,8 @@ static void HandleInputChooseTarget(u32 battler)
|
|||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_ULTRA_BURST | (gMultiUsePlayerCursor << 8));
|
||||
else if (gBattleStruct->dynamax.playerSelect)
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_DYNAMAX | (gMultiUsePlayerCursor << 8));
|
||||
else if (gBattleStruct->tera.playerSelect)
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_TERASTAL | (gMultiUsePlayerCursor << 8));
|
||||
else
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | (gMultiUsePlayerCursor << 8));
|
||||
EndBounceEffect(gMultiUsePlayerCursor, BOUNCE_HEALTHBOX);
|
||||
|
@ -616,6 +618,8 @@ static void HandleInputShowEntireFieldTargets(u32 battler)
|
|||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_ULTRA_BURST | (gMultiUsePlayerCursor << 8));
|
||||
else if (gBattleStruct->dynamax.playerSelect)
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_DYNAMAX | (gMultiUsePlayerCursor << 8));
|
||||
else if (gBattleStruct->tera.playerSelect)
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_TERASTAL | (gMultiUsePlayerCursor << 8));
|
||||
else
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | (gMultiUsePlayerCursor << 8));
|
||||
HideTriggerSprites();
|
||||
|
@ -648,6 +652,8 @@ static void HandleInputShowTargets(u32 battler)
|
|||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_ULTRA_BURST | (gMultiUsePlayerCursor << 8));
|
||||
else if (gBattleStruct->dynamax.playerSelect)
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_DYNAMAX | (gMultiUsePlayerCursor << 8));
|
||||
else if (gBattleStruct->tera.playerSelect)
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_TERASTAL | (gMultiUsePlayerCursor << 8));
|
||||
else
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | (gMultiUsePlayerCursor << 8));
|
||||
HideTriggerSprites();
|
||||
|
@ -772,6 +778,8 @@ static void HandleInputChooseMove(u32 battler)
|
|||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_ULTRA_BURST | (gMultiUsePlayerCursor << 8));
|
||||
else if (gBattleStruct->dynamax.playerSelect)
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_DYNAMAX | (gMultiUsePlayerCursor << 8));
|
||||
else if (gBattleStruct->tera.playerSelect)
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_TERASTAL | (gMultiUsePlayerCursor << 8));
|
||||
else
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | (gMultiUsePlayerCursor << 8));
|
||||
HideTriggerSprites();
|
||||
|
@ -810,6 +818,7 @@ static void HandleInputChooseMove(u32 battler)
|
|||
gBattleStruct->mega.playerSelect = FALSE;
|
||||
gBattleStruct->burst.playerSelect = FALSE;
|
||||
gBattleStruct->dynamax.playerSelect = FALSE;
|
||||
gBattleStruct->tera.playerSelect = FALSE;
|
||||
gBattleStruct->zmove.viable = FALSE;
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, 0xFFFF);
|
||||
HideTriggerSprites();
|
||||
|
@ -918,6 +927,12 @@ static void HandleInputChooseMove(u32 battler)
|
|||
ChangeDynamaxTriggerSprite(gBattleStruct->dynamax.triggerSpriteId, gBattleStruct->dynamax.playerSelect);
|
||||
PlaySE(SE_SELECT);
|
||||
}
|
||||
else if (CanTerastallize(battler))
|
||||
{
|
||||
gBattleStruct->tera.playerSelect ^= 1;
|
||||
ChangeTeraTriggerSprite(gBattleStruct->tera.triggerSpriteId, gBattleStruct->tera.playerSelect);
|
||||
PlaySE(SE_SELECT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -926,6 +941,7 @@ static void ReloadMoveNames(u32 battler)
|
|||
gBattleStruct->mega.playerSelect = FALSE;
|
||||
gBattleStruct->burst.playerSelect = FALSE;
|
||||
gBattleStruct->dynamax.playerSelect = FALSE;
|
||||
gBattleStruct->tera.playerSelect = FALSE;
|
||||
gBattleStruct->zmove.viewing = FALSE;
|
||||
MoveSelectionDestroyCursorAt(battler);
|
||||
MoveSelectionDisplayMoveNames(battler);
|
||||
|
@ -2059,6 +2075,7 @@ static void PlayerHandleChooseMove(u32 battler)
|
|||
gBattleStruct->mega.playerSelect = FALSE;
|
||||
gBattleStruct->burst.playerSelect = FALSE;
|
||||
gBattleStruct->dynamax.playerSelect = FALSE;
|
||||
gBattleStruct->tera.playerSelect = FALSE;
|
||||
if (!IsMegaTriggerSpriteActive())
|
||||
gBattleStruct->mega.triggerSpriteId = 0xFF;
|
||||
if (CanMegaEvolve(battler))
|
||||
|
@ -2073,6 +2090,10 @@ static void PlayerHandleChooseMove(u32 battler)
|
|||
CreateDynamaxTriggerSprite(battler, 0);
|
||||
if (!IsZMoveTriggerSpriteActive())
|
||||
gBattleStruct->zmove.triggerSpriteId = 0xFF;
|
||||
if (!IsTeraTriggerSpriteActive())
|
||||
gBattleStruct->tera.triggerSpriteId = 0xFF;
|
||||
if (CanTerastallize(battler))
|
||||
CreateTeraTriggerSprite(battler, 0);
|
||||
|
||||
GetUsableZMoves(battler, moveInfo->moves);
|
||||
gBattleStruct->zmove.viable = IsZMoveUsable(battler, gMoveSelectionCursor[battler]);
|
||||
|
|
|
@ -645,6 +645,13 @@ void BattleLoadMonSpriteGfx(struct Pokemon *mon, u32 battler)
|
|||
BlendPalette(paletteOffset, 16, 4, RGB(31, 0, 12));
|
||||
CpuCopy32(gPlttBufferFaded + paletteOffset, gPlttBufferUnfaded + paletteOffset, PLTT_SIZEOF(16));
|
||||
}
|
||||
|
||||
// Terastallization's tint
|
||||
if (IsTerastallized(battler))
|
||||
{
|
||||
BlendPalette(paletteOffset, 16, 8, GetTeraTypeRGB(GetBattlerTeraType(battler)));
|
||||
CpuCopy32(gPlttBufferFaded + paletteOffset, gPlttBufferUnfaded + paletteOffset, PLTT_SIZEOF(16));
|
||||
}
|
||||
}
|
||||
|
||||
void BattleGfxSfxDummy2(u16 species)
|
||||
|
@ -710,6 +717,7 @@ bool8 BattleLoadAllHealthBoxesGfx(u8 state)
|
|||
LoadSpritePalette(&sSpritePalettes_HealthBoxHealthBar[0]);
|
||||
LoadSpritePalette(&sSpritePalettes_HealthBoxHealthBar[1]);
|
||||
MegaIndicator_LoadSpritesGfx();
|
||||
TeraIndicator_LoadSpriteGfx();
|
||||
}
|
||||
else if (!IsDoubleBattle())
|
||||
{
|
||||
|
|
|
@ -854,6 +854,9 @@ u8 CreateBattlerHealthboxSprites(u8 battlerId)
|
|||
// Create mega indicator sprite.
|
||||
MegaIndicator_CreateSprite(battlerId, healthboxLeftSpriteId);
|
||||
|
||||
// Create tera indicator sprites.
|
||||
TeraIndicator_CreateSprite(battlerId, healthboxLeftSpriteId);
|
||||
|
||||
gBattleStruct->ballSpriteIds[0] = MAX_SPRITES;
|
||||
gBattleStruct->ballSpriteIds[1] = MAX_SPRITES;
|
||||
|
||||
|
@ -937,6 +940,7 @@ void SetHealthboxSpriteInvisible(u8 healthboxSpriteId)
|
|||
gSprites[gSprites[healthboxSpriteId].hMain_HealthBarSpriteId].invisible = TRUE;
|
||||
gSprites[gSprites[healthboxSpriteId].oam.affineParam].invisible = TRUE;
|
||||
MegaIndicator_SetVisibilities(healthboxSpriteId, TRUE);
|
||||
TeraIndicator_SetVisibilities(healthboxSpriteId, TRUE);
|
||||
}
|
||||
|
||||
void SetHealthboxSpriteVisible(u8 healthboxSpriteId)
|
||||
|
@ -945,6 +949,7 @@ void SetHealthboxSpriteVisible(u8 healthboxSpriteId)
|
|||
gSprites[gSprites[healthboxSpriteId].hMain_HealthBarSpriteId].invisible = FALSE;
|
||||
gSprites[gSprites[healthboxSpriteId].oam.affineParam].invisible = FALSE;
|
||||
MegaIndicator_SetVisibilities(healthboxSpriteId, FALSE);
|
||||
TeraIndicator_SetVisibilities(healthboxSpriteId, FALSE);
|
||||
}
|
||||
|
||||
static void UpdateSpritePos(u8 spriteId, s16 x, s16 y)
|
||||
|
@ -971,6 +976,7 @@ static void TryToggleHealboxVisibility(u32 priority, u32 healthboxLeftSpriteId,
|
|||
gSprites[healthboxRightSpriteId].invisible = invisible;
|
||||
gSprites[healthbarSpriteId].invisible = invisible;
|
||||
MegaIndicator_SetVisibilities(healthboxLeftSpriteId, invisible);
|
||||
TeraIndicator_SetVisibilities(healthboxLeftSpriteId, invisible);
|
||||
}
|
||||
|
||||
void UpdateOamPriorityInAllHealthboxes(u8 priority, bool32 hideHPBoxes)
|
||||
|
@ -988,6 +994,7 @@ void UpdateOamPriorityInAllHealthboxes(u8 priority, bool32 hideHPBoxes)
|
|||
gSprites[healthbarSpriteId].oam.priority = priority;
|
||||
|
||||
MegaIndicator_UpdateOamPriority(healthboxLeftSpriteId, priority);
|
||||
TeraIndicator_UpdateOamPriorities(healthboxLeftSpriteId, priority);
|
||||
|
||||
if (B_HIDE_HEALTHBOX_IN_ANIMS == TRUE && hideHPBoxes && IsBattlerAlive(i))
|
||||
TryToggleHealboxVisibility(priority, healthboxLeftSpriteId, healthboxRightSpriteId, healthbarSpriteId);
|
||||
|
@ -1050,6 +1057,13 @@ static void UpdateLvlInHealthbox(u8 healthboxSpriteId, u8 lvl)
|
|||
MegaIndicator_UpdateLevel(healthboxSpriteId, lvl);
|
||||
MegaIndicator_SetVisibilities(healthboxSpriteId, FALSE);
|
||||
}
|
||||
else if (IsTerastallized(battler))
|
||||
{
|
||||
objVram = ConvertIntToDecimalStringN(text, lvl, STR_CONV_MODE_LEFT_ALIGN, 3);
|
||||
xPos = 5 * (3 - (objVram - (text + 2))) - 1;
|
||||
TeraIndicator_UpdateLevel(healthboxSpriteId, lvl);
|
||||
TeraIndicator_SetVisibilities(healthboxSpriteId, FALSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
text[0] = CHAR_EXTRA_SYMBOL;
|
||||
|
@ -1473,6 +1487,7 @@ void HideTriggerSprites(void)
|
|||
HideBurstTriggerSprite();
|
||||
HideZMoveTriggerSprite();
|
||||
HideDynamaxTriggerSprite();
|
||||
HideTeraTriggerSprite();
|
||||
}
|
||||
|
||||
void DestroyMegaTriggerSprite(void)
|
||||
|
@ -2532,6 +2547,10 @@ void UpdateHealthboxAttribute(u8 healthboxSpriteId, struct Pokemon *mon, u8 elem
|
|||
u32 battlerId = gSprites[healthboxSpriteId].hMain_Battler;
|
||||
s32 maxHp = GetMonData(mon, MON_DATA_MAX_HP);
|
||||
s32 currHp = GetMonData(mon, MON_DATA_HP);
|
||||
|
||||
// This fixes a bug that should likely never happen involving switching between two Teras.
|
||||
if (elementId == HEALTHBOX_ALL)
|
||||
TeraIndicator_UpdateType(battlerId, healthboxSpriteId);
|
||||
|
||||
if (GetBattlerSide(battlerId) == B_SIDE_PLAYER)
|
||||
{
|
||||
|
|
|
@ -590,13 +590,15 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] =
|
|||
//.teraShard = ITEM_FAIRY_TERA_SHARD,
|
||||
//.arceusForm = SPECIES_ARCEUS_FAIRY,
|
||||
},
|
||||
/*
|
||||
[TYPE_STELLAR] =
|
||||
{
|
||||
.name = _("Stellar"),
|
||||
.teraShard = ITEM_STELLAR_TERA_SHARD,
|
||||
.name = _("Stellr"),
|
||||
.generic = _("a STELLAR move"),
|
||||
.palette = 15,
|
||||
.zMove = MOVE_BREAKNECK_BLITZ,
|
||||
.maxMove = MOVE_MAX_STRIKE,
|
||||
// .teraShard = ITEM_STELLAR_TERA_SHARD,
|
||||
},
|
||||
*/
|
||||
};
|
||||
|
||||
// extra args are money and ball
|
||||
|
@ -2325,6 +2327,11 @@ u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer
|
|||
u32 data = partyData[i].gigantamaxFactor;
|
||||
SetMonData(&party[i], MON_DATA_GIGANTAMAX_FACTOR, &data);
|
||||
}
|
||||
if (partyData[i].teraType > 0)
|
||||
{
|
||||
u32 data = partyData[i].teraType;
|
||||
SetMonData(&party[i], MON_DATA_TERA_TYPE, &data);
|
||||
}
|
||||
CalculateMonStats(&party[i]);
|
||||
|
||||
if (B_TRAINER_CLASS_POKE_BALLS >= GEN_7 && ball == -1)
|
||||
|
@ -4608,6 +4615,7 @@ static void HandleTurnActionSelectionState(void)
|
|||
gBattleStruct->mega.toEvolve &= ~(gBitTable[BATTLE_PARTNER(GetBattlerPosition(battler))]);
|
||||
gBattleStruct->burst.toBurst &= ~(gBitTable[BATTLE_PARTNER(GetBattlerPosition(battler))]);
|
||||
gBattleStruct->dynamax.toDynamax &= ~(gBitTable[BATTLE_PARTNER(GetBattlerPosition(battler))]);
|
||||
gBattleStruct->tera.toTera &= ~(gBitTable[BATTLE_PARTNER(GetBattlerPosition(battler))]);
|
||||
gBattleStruct->dynamax.usingMaxMove[BATTLE_PARTNER(GetBattlerPosition(battler))] = FALSE;
|
||||
gBattleStruct->zmove.toBeUsed[BATTLE_PARTNER(GetBattlerPosition(battler))] = MOVE_NONE;
|
||||
BtlController_EmitEndBounceEffect(battler, BUFFER_A);
|
||||
|
@ -4697,7 +4705,7 @@ static void HandleTurnActionSelectionState(void)
|
|||
}
|
||||
|
||||
// Get the chosen move position (and thus the chosen move) and target from the returned buffer.
|
||||
gBattleStruct->chosenMovePositions[battler] = gBattleResources->bufferB[battler][2] & ~(RET_MEGA_EVOLUTION | RET_ULTRA_BURST | RET_DYNAMAX);
|
||||
gBattleStruct->chosenMovePositions[battler] = gBattleResources->bufferB[battler][2] & ~(RET_MEGA_EVOLUTION | RET_ULTRA_BURST | RET_DYNAMAX | RET_TERASTAL);
|
||||
gChosenMoveByBattler[battler] = gBattleMons[battler].moves[gBattleStruct->chosenMovePositions[battler]];
|
||||
gBattleStruct->moveTarget[battler] = gBattleResources->bufferB[battler][3];
|
||||
|
||||
|
@ -4708,6 +4716,8 @@ static void HandleTurnActionSelectionState(void)
|
|||
gBattleStruct->burst.toBurst |= gBitTable[battler];
|
||||
else if (gBattleResources->bufferB[battler][2] & RET_DYNAMAX)
|
||||
gBattleStruct->dynamax.toDynamax |= gBitTable[battler];
|
||||
else if (gBattleResources->bufferB[battler][2] & RET_TERASTAL)
|
||||
gBattleStruct->tera.toTera |= gBitTable[battler];
|
||||
|
||||
// Max Move check
|
||||
if (ShouldUseMaxMove(battler, gChosenMoveByBattler[battler]))
|
||||
|
@ -5325,7 +5335,8 @@ static void PopulateArrayWithBattlers(u8 *battlers)
|
|||
static bool32 TryDoGimmicksBeforeMoves(void)
|
||||
{
|
||||
if (!(gHitMarker & HITMARKER_RUN)
|
||||
&& (gBattleStruct->mega.toEvolve || gBattleStruct->burst.toBurst || gBattleStruct->dynamax.toDynamax))
|
||||
&& (gBattleStruct->mega.toEvolve || gBattleStruct->burst.toBurst
|
||||
|| gBattleStruct->dynamax.toDynamax || gBattleStruct->tera.toTera))
|
||||
{
|
||||
u32 i, battler;
|
||||
u8 order[MAX_BATTLERS_COUNT];
|
||||
|
@ -5334,6 +5345,17 @@ static bool32 TryDoGimmicksBeforeMoves(void)
|
|||
SortBattlersBySpeed(order, FALSE);
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
// Tera Check
|
||||
if (gBattleStruct->tera.toTera & gBitTable[order[i]])
|
||||
{
|
||||
gBattlerAttacker = order[i];
|
||||
gBattleScripting.battler = gBattlerAttacker;
|
||||
gBattleStruct->tera.toTera &= ~(gBitTable[gBattlerAttacker]);
|
||||
PrepareBattlerForTera(gBattlerAttacker);
|
||||
PREPARE_TYPE_BUFFER(gBattleTextBuff1, GetBattlerTeraType(gBattlerAttacker));
|
||||
BattleScriptExecute(BattleScript_Terastallization);
|
||||
return TRUE;
|
||||
}
|
||||
// Dynamax Check
|
||||
if (gBattleStruct->dynamax.toDynamax & gBitTable[order[i]])
|
||||
{
|
||||
|
@ -5996,7 +6018,9 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk)
|
|||
}
|
||||
else if (gMovesInfo[move].effect == EFFECT_REVELATION_DANCE)
|
||||
{
|
||||
if (gBattleMons[battlerAtk].type1 != TYPE_MYSTERY)
|
||||
if (IsTerastallized(battlerAtk) && GetBattlerTeraType(battlerAtk) != TYPE_STELLAR)
|
||||
gBattleStruct->dynamicMoveType = GetBattlerTeraType(battlerAtk);
|
||||
else if (gBattleMons[battlerAtk].type1 != TYPE_MYSTERY)
|
||||
gBattleStruct->dynamicMoveType = gBattleMons[battlerAtk].type1 | F_DYNAMIC_TYPE_SET;
|
||||
else if (gBattleMons[battlerAtk].type2 != TYPE_MYSTERY)
|
||||
gBattleStruct->dynamicMoveType = gBattleMons[battlerAtk].type2 | F_DYNAMIC_TYPE_SET;
|
||||
|
@ -6038,6 +6062,10 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk)
|
|||
gBattleStruct->dynamicMoveType = TYPE_NORMAL | F_DYNAMIC_TYPE_SET;
|
||||
}
|
||||
}
|
||||
else if (gMovesInfo[move].effect == EFFECT_TERA_BLAST && IsTerastallized(battlerAtk))
|
||||
{
|
||||
gBattleStruct->dynamicMoveType = GetBattlerTeraType(battlerAtk) | F_DYNAMIC_TYPE_SET;
|
||||
}
|
||||
|
||||
attackerAbility = GetBattlerAbility(battlerAtk);
|
||||
|
||||
|
@ -6046,6 +6074,7 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk)
|
|||
&& gMovesInfo[move].effect != EFFECT_WEATHER_BALL
|
||||
&& gMovesInfo[move].effect != EFFECT_CHANGE_TYPE_ON_ITEM
|
||||
&& gMovesInfo[move].effect != EFFECT_NATURAL_GIFT
|
||||
&& !(gMovesInfo[move].effect == EFFECT_TERA_BLAST && IsTerastallized(battlerAtk))
|
||||
&& ((attackerAbility == ABILITY_PIXILATE && (ateType = TYPE_FAIRY))
|
||||
|| (attackerAbility == ABILITY_REFRIGERATE && (ateType = TYPE_ICE))
|
||||
|| (attackerAbility == ABILITY_AERILATE && (ateType = TYPE_FLYING))
|
||||
|
|
|
@ -846,11 +846,13 @@ static const u8 sText_ElectroShotCharging[] = _("{B_ATK_NAME_WITH_PREFIX} absorb
|
|||
static const u8 sText_ItemWasUsedUp[] = _("The {B_LAST_ITEM}\nwas used up...");
|
||||
static const u8 sText_AttackerLostItsType[] = _("{B_ATK_NAME_WITH_PREFIX} lost\nits {B_BUFF1} type!");
|
||||
static const u8 sText_ShedItsTail[] = _("{B_ATK_NAME_WITH_PREFIX} shed its tail\nto create a decoy!");
|
||||
static const u8 sText_PkmnTerastallizedInto[] = _("{B_ATK_NAME_WITH_PREFIX} terastallized\ninto the {B_BUFF1} type!");
|
||||
static const u8 sText_SupersweetAromaWafts[] = _("A supersweet aroma is wafting from\nthe syrup covering {B_ATK_NAME_WITH_PREFIX}!");
|
||||
static const u8 sText_TidyingUpComplete[] = _("Tidying up complete!");
|
||||
|
||||
const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] =
|
||||
{
|
||||
[STRINGID_PKMNTERASTALLIZEDINTO - BATTLESTRINGS_TABLE_START] = sText_PkmnTerastallizedInto,
|
||||
[STRINGID_TIDYINGUPCOMPLETE - BATTLESTRINGS_TABLE_START] = sText_TidyingUpComplete,
|
||||
[STRINGID_SUPERSWEETAROMAWAFTS - BATTLESTRINGS_TABLE_START] = sText_SupersweetAromaWafts,
|
||||
[STRINGID_SHEDITSTAIL - BATTLESTRINGS_TABLE_START] = sText_ShedItsTail,
|
||||
|
|
|
@ -1215,7 +1215,8 @@ bool32 ProteanTryChangeType(u32 battler, u32 ability, u32 move, u32 moveType)
|
|||
&& !gDisableStructs[gBattlerAttacker].usedProteanLibero
|
||||
&& (gBattleMons[battler].type1 != moveType || gBattleMons[battler].type2 != moveType
|
||||
|| (gBattleMons[battler].type3 != moveType && gBattleMons[battler].type3 != TYPE_MYSTERY))
|
||||
&& move != MOVE_STRUGGLE)
|
||||
&& move != MOVE_STRUGGLE
|
||||
&& !IsTerastallized(battler))
|
||||
{
|
||||
SET_BATTLER_TYPE(battler, moveType);
|
||||
return TRUE;
|
||||
|
@ -2085,12 +2086,12 @@ END:
|
|||
// of a move that is Super Effective against a Flying-type Pokémon.
|
||||
if (gBattleWeather & B_WEATHER_STRONG_WINDS)
|
||||
{
|
||||
if ((GetBattlerType(gBattlerTarget, 0) == TYPE_FLYING
|
||||
&& GetTypeModifier(moveType, GetBattlerType(gBattlerTarget, 0)) >= UQ_4_12(2.0))
|
||||
|| (GetBattlerType(gBattlerTarget, 1) == TYPE_FLYING
|
||||
&& GetTypeModifier(moveType, GetBattlerType(gBattlerTarget, 1)) >= UQ_4_12(2.0))
|
||||
|| (GetBattlerType(gBattlerTarget, 2) == TYPE_FLYING
|
||||
&& GetTypeModifier(moveType, GetBattlerType(gBattlerTarget, 2)) >= UQ_4_12(2.0)))
|
||||
if ((GetBattlerType(gBattlerTarget, 0, FALSE) == TYPE_FLYING
|
||||
&& GetTypeModifier(moveType, GetBattlerType(gBattlerTarget, 0, FALSE)) >= UQ_4_12(2.0))
|
||||
|| (GetBattlerType(gBattlerTarget, 1, FALSE) == TYPE_FLYING
|
||||
&& GetTypeModifier(moveType, GetBattlerType(gBattlerTarget, 1, FALSE)) >= UQ_4_12(2.0))
|
||||
|| (GetBattlerType(gBattlerTarget, 2, FALSE) == TYPE_FLYING
|
||||
&& GetTypeModifier(moveType, GetBattlerType(gBattlerTarget, 2, FALSE)) >= UQ_4_12(2.0)))
|
||||
{
|
||||
gBattlerAbility = gBattlerTarget;
|
||||
BattleScriptPushCursor();
|
||||
|
@ -3806,6 +3807,15 @@ void SetMoveEffect(bool32 primary, bool32 certain)
|
|||
gBattlescriptCurrInstr = BattleScript_EffectPsychicNoise;
|
||||
}
|
||||
break;
|
||||
case MOVE_EFFECT_TERA_BLAST:
|
||||
if (IsTerastallized(gEffectBattler)
|
||||
&& GetBattlerTeraType(gEffectBattler) == TYPE_STELLAR
|
||||
&& !NoAliveMonsForEitherParty())
|
||||
{
|
||||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||||
gBattlescriptCurrInstr = BattleScript_LowerAtkSpAtk;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9648,8 +9658,9 @@ static void Cmd_various(void)
|
|||
case VARIOUS_TRY_SOAK:
|
||||
{
|
||||
VARIOUS_ARGS(const u8 *failInstr);
|
||||
if (GetBattlerType(gBattlerTarget, 0) == gMovesInfo[gCurrentMove].type
|
||||
&& GetBattlerType(gBattlerTarget, 1) == gMovesInfo[gCurrentMove].type)
|
||||
if ((GetBattlerType(gBattlerTarget, 0, FALSE) == gMovesInfo[gCurrentMove].type
|
||||
&& GetBattlerType(gBattlerTarget, 1, FALSE) == gMovesInfo[gCurrentMove].type)
|
||||
|| IsTerastallized(gBattlerTarget))
|
||||
{
|
||||
gBattlescriptCurrInstr = cmd->failInstr;
|
||||
}
|
||||
|
@ -9982,7 +9993,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))
|
||||
if (IS_BATTLER_OF_TYPE(battler, gMovesInfo[gCurrentMove].argument) || IsTerastallized(battler))
|
||||
{
|
||||
gBattlescriptCurrInstr = cmd->failInstr;
|
||||
}
|
||||
|
@ -12049,6 +12060,12 @@ static void Cmd_tryconversiontypechange(void)
|
|||
u8 moveChecked = 0;
|
||||
u8 moveType = 0;
|
||||
|
||||
if (IsTerastallized(gBattlerAttacker))
|
||||
{
|
||||
gBattlescriptCurrInstr = cmd->failInstr;
|
||||
return;
|
||||
}
|
||||
|
||||
if (B_UPDATED_CONVERSION >= GEN_6)
|
||||
{
|
||||
// Changes user's type to its first move's type
|
||||
|
@ -12825,6 +12842,14 @@ static void Cmd_settypetorandomresistance(void)
|
|||
{
|
||||
gBattlescriptCurrInstr = cmd->failInstr;
|
||||
}
|
||||
else if (IsTerastallized(gBattlerAttacker))
|
||||
{
|
||||
gBattlescriptCurrInstr = cmd->failInstr;
|
||||
}
|
||||
else if (gLastHitByType[gBattlerAttacker] == TYPE_STELLAR)
|
||||
{
|
||||
gBattlescriptCurrInstr = cmd->failInstr;
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 i, resistTypes = 0;
|
||||
|
@ -14812,7 +14837,7 @@ static void Cmd_settypetoterrain(void)
|
|||
break;
|
||||
}
|
||||
|
||||
if (!IS_BATTLER_OF_TYPE(gBattlerAttacker, terrainType))
|
||||
if (!IS_BATTLER_OF_TYPE(gBattlerAttacker, terrainType) && !IsTerastallized(gBattlerAttacker))
|
||||
{
|
||||
SET_BATTLER_TYPE(gBattlerAttacker, terrainType);
|
||||
PREPARE_TYPE_BUFFER(gBattleTextBuff1, terrainType);
|
||||
|
@ -16378,14 +16403,18 @@ void BS_TryReflectType(void)
|
|||
{
|
||||
NATIVE_ARGS(const u8 *failInstr);
|
||||
u16 targetBaseSpecies = GET_BASE_SPECIES_ID(gBattleMons[gBattlerTarget].species);
|
||||
u8 targetType1 = GetBattlerType(gBattlerTarget, 0);
|
||||
u8 targetType2 = GetBattlerType(gBattlerTarget, 1);
|
||||
u8 targetType3 = GetBattlerType(gBattlerTarget, 2);
|
||||
u8 targetType1 = GetBattlerType(gBattlerTarget, 0, FALSE);
|
||||
u8 targetType2 = GetBattlerType(gBattlerTarget, 1, FALSE);
|
||||
u8 targetType3 = GetBattlerType(gBattlerTarget, 2, FALSE);
|
||||
|
||||
if (targetBaseSpecies == SPECIES_ARCEUS || targetBaseSpecies == SPECIES_SILVALLY)
|
||||
{
|
||||
gBattlescriptCurrInstr = cmd->failInstr;
|
||||
}
|
||||
else if (IsTerastallized(gBattlerAttacker))
|
||||
{
|
||||
gBattlescriptCurrInstr = cmd->failInstr;
|
||||
}
|
||||
else if (IS_BATTLER_TYPELESS(gBattlerTarget))
|
||||
{
|
||||
gBattlescriptCurrInstr = cmd->failInstr;
|
||||
|
@ -16746,7 +16775,8 @@ void BS_AllySwitchFailChance(void)
|
|||
void BS_SetPhotonGeyserCategory(void)
|
||||
{
|
||||
NATIVE_ARGS();
|
||||
gBattleStruct->swapDamageCategory = (GetCategoryBasedOnStats(gBattlerAttacker) == DAMAGE_CATEGORY_PHYSICAL);
|
||||
if (!(gMovesInfo[gCurrentMove].effect == EFFECT_TERA_BLAST && !IsTerastallized(gBattlerAttacker)))
|
||||
gBattleStruct->swapDamageCategory = (GetCategoryBasedOnStats(gBattlerAttacker) == DAMAGE_CATEGORY_PHYSICAL);
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
}
|
||||
|
||||
|
|
786
src/battle_terastal.c
Normal file
|
@ -0,0 +1,786 @@
|
|||
#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 "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);
|
||||
struct Pokemon *party = GetBattlerParty(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);
|
||||
}
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
// 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 (B_FLAG_TERA_ORB_CHARGED != 0
|
||||
&& (battler == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && battler == B_POSITION_PLAYER_RIGHT))
|
||||
&& !(CheckBagHasItem(ITEM_TERA_ORB, 1)
|
||||
&& FlagGet(B_FLAG_TERA_ORB_CHARGED)))
|
||||
{
|
||||
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)
|
||||
{
|
||||
struct Pokemon *mon = &GetBattlerParty(battler)[gBattlerPartyIndexes[battler]];
|
||||
return GetMonData(mon, 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);
|
||||
}
|
||||
}
|
||||
|
||||
// Most values pulled from the Tera type icon palette.
|
||||
const u16 sTeraTypeRGBValues[NUMBER_OF_MON_TYPES] = {
|
||||
[TYPE_NORMAL] = RGB_WHITE, // custom
|
||||
[TYPE_FIGHTING] = RGB(26, 8, 14),
|
||||
[TYPE_FLYING] = RGB(31, 26, 7),
|
||||
[TYPE_POISON] = RGB(26, 10, 25), // custom
|
||||
[TYPE_GROUND] = RGB(25, 23, 18),
|
||||
[TYPE_ROCK] = RGB(18, 16, 8), // custom
|
||||
[TYPE_BUG] = RGB(18, 24, 6),
|
||||
[TYPE_GHOST] = RGB(12, 10, 16),
|
||||
[TYPE_STEEL] = RGB(19, 19, 20),
|
||||
[TYPE_MYSTERY] = RGB_WHITE,
|
||||
[TYPE_FIRE] = RGB(31, 20, 11),
|
||||
[TYPE_WATER] = RGB(10, 18, 27),
|
||||
[TYPE_GRASS] = RGB(12, 24, 11),
|
||||
[TYPE_ELECTRIC] = RGB(30, 26, 7),
|
||||
[TYPE_PSYCHIC] = RGB(31, 14, 15),
|
||||
[TYPE_ICE] = RGB(14, 26, 25),
|
||||
[TYPE_DRAGON] = RGB(10, 18, 27),
|
||||
[TYPE_DARK] = RGB(6, 5, 8),
|
||||
[TYPE_FAIRY] = RGB(31, 15, 21),
|
||||
[TYPE_STELLAR] = RGB(10, 18, 27),
|
||||
};
|
||||
|
||||
u16 GetTeraTypeRGB(u32 type)
|
||||
{
|
||||
return sTeraTypeRGBValues[type];
|
||||
}
|
||||
|
||||
// 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},
|
||||
{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_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 (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
|
|
@ -707,7 +707,7 @@ void HandleAction_NothingIsFainted(void)
|
|||
|
||||
void HandleAction_ActionFinished(void)
|
||||
{
|
||||
u32 i, j;
|
||||
u32 i, j, moveType;
|
||||
bool32 afterYouActive = gSpecialStatuses[gBattlerByTurnOrder[gCurrentTurnActionNumber + 1]].afterYou;
|
||||
*(gBattleStruct->monToSwitchIntoId + gBattlerByTurnOrder[gCurrentTurnActionNumber]) = gSelectedMonPartyId = PARTY_SIZE;
|
||||
gCurrentTurnActionNumber++;
|
||||
|
@ -718,6 +718,16 @@ void HandleAction_ActionFinished(void)
|
|||
| HITMARKER_OBEYS | HITMARKER_WAKE_UP_CLEAR | HITMARKER_SYNCHRONISE_EFFECT
|
||||
| HITMARKER_CHARGING | HITMARKER_NEVER_SET | HITMARKER_IGNORE_DISGUISE);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
gCurrentMove = 0;
|
||||
gBattleMoveDamage = 0;
|
||||
gMoveResultFlags = 0;
|
||||
|
@ -898,34 +908,35 @@ static const uq4_12_t sPercentToModifier[] =
|
|||
|
||||
static const uq4_12_t sTypeEffectivenessTable[NUMBER_OF_MON_TYPES][NUMBER_OF_MON_TYPES] =
|
||||
{// Defender -->
|
||||
// Attacker Normal Fighting Flying Poison Ground Rock Bug Ghost Steel Mystery Fire Water Grass Electric Psychic Ice Dragon Dark Fairy
|
||||
[TYPE_NORMAL] = {______, ______, ______, ______, ______, X(0.5), ______, X(0.0), X(0.5), ______, ______, ______, ______, ______, ______, ______, ______, ______, ______},
|
||||
[TYPE_FIGHTING] = {X(2.0), ______, X(0.5), X(0.5), ______, X(2.0), X(0.5), X(0.0), X(2.0), ______, ______, ______, ______, ______, X(0.5), X(2.0), ______, X(2.0), X(0.5)},
|
||||
[TYPE_FLYING] = {______, X(2.0), ______, ______, ______, X(0.5), X(2.0), ______, X(0.5), ______, ______, ______, X(2.0), X(0.5), ______, ______, ______, ______, ______},
|
||||
[TYPE_POISON] = {______, ______, ______, X(0.5), X(0.5), X(0.5), ______, X(0.5), X(0.0), ______, ______, ______, X(2.0), ______, ______, ______, ______, ______, X(2.0)},
|
||||
[TYPE_GROUND] = {______, ______, X(0.0), X(2.0), ______, X(2.0), X(0.5), ______, X(2.0), ______, X(2.0), ______, X(0.5), X(2.0), ______, ______, ______, ______, ______},
|
||||
[TYPE_ROCK] = {______, X(0.5), X(2.0), ______, X(0.5), ______, X(2.0), ______, X(0.5), ______, X(2.0), ______, ______, ______, ______, X(2.0), ______, ______, ______},
|
||||
[TYPE_BUG] = {______, X(0.5), X(0.5), X(0.5), ______, ______, ______, X(0.5), X(0.5), ______, X(0.5), ______, X(2.0), ______, X(2.0), ______, ______, X(2.0), X(0.5)},
|
||||
// Attacker Normal Fighting Flying Poison Ground Rock Bug Ghost Steel Mystery Fire Water Grass Electric Psychic Ice Dragon Dark Fairy Stellar
|
||||
[TYPE_NORMAL] = {______, ______, ______, ______, ______, X(0.5), ______, X(0.0), X(0.5), ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______},
|
||||
[TYPE_FIGHTING] = {X(2.0), ______, X(0.5), X(0.5), ______, X(2.0), X(0.5), X(0.0), X(2.0), ______, ______, ______, ______, ______, X(0.5), X(2.0), ______, X(2.0), X(0.5), ______},
|
||||
[TYPE_FLYING] = {______, X(2.0), ______, ______, ______, X(0.5), X(2.0), ______, X(0.5), ______, ______, ______, X(2.0), X(0.5), ______, ______, ______, ______, ______, ______},
|
||||
[TYPE_POISON] = {______, ______, ______, X(0.5), X(0.5), X(0.5), ______, X(0.5), X(0.0), ______, ______, ______, X(2.0), ______, ______, ______, ______, ______, X(2.0), ______},
|
||||
[TYPE_GROUND] = {______, ______, X(0.0), X(2.0), ______, X(2.0), X(0.5), ______, X(2.0), ______, X(2.0), ______, X(0.5), X(2.0), ______, ______, ______, ______, ______, ______},
|
||||
[TYPE_ROCK] = {______, X(0.5), X(2.0), ______, X(0.5), ______, X(2.0), ______, X(0.5), ______, X(2.0), ______, ______, ______, ______, X(2.0), ______, ______, ______, ______},
|
||||
[TYPE_BUG] = {______, X(0.5), X(0.5), X(0.5), ______, ______, ______, X(0.5), X(0.5), ______, X(0.5), ______, X(2.0), ______, X(2.0), ______, ______, X(2.0), X(0.5), ______},
|
||||
#if B_STEEL_RESISTANCES >= GEN_6
|
||||
[TYPE_GHOST] = {X(0.0), ______, ______, ______, ______, ______, ______, X(2.0), ______, ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), ______},
|
||||
[TYPE_GHOST] = {X(0.0), ______, ______, ______, ______, ______, ______, X(2.0), ______, ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), ______, ______},
|
||||
#else
|
||||
[TYPE_GHOST] = {X(0.0), ______, ______, ______, ______, ______, ______, X(2.0), X(0.5), ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), ______},
|
||||
[TYPE_GHOST] = {X(0.0), ______, ______, ______, ______, ______, ______, X(2.0), X(0.5), ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), ______, ______},
|
||||
#endif
|
||||
[TYPE_STEEL] = {______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), ______, X(0.5), X(0.5), ______, X(0.5), ______, X(2.0), ______, ______, X(2.0)},
|
||||
[TYPE_MYSTERY] = {______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______},
|
||||
[TYPE_FIRE] = {______, ______, ______, ______, ______, X(0.5), X(2.0), ______, X(2.0), ______, X(0.5), X(0.5), X(2.0), ______, ______, X(2.0), X(0.5), ______, ______},
|
||||
[TYPE_WATER] = {______, ______, ______, ______, X(2.0), X(2.0), ______, ______, ______, ______, X(2.0), X(0.5), X(0.5), ______, ______, ______, X(0.5), ______, ______},
|
||||
[TYPE_GRASS] = {______, ______, X(0.5), X(0.5), X(2.0), X(2.0), X(0.5), ______, X(0.5), ______, X(0.5), X(2.0), X(0.5), ______, ______, ______, X(0.5), ______, ______},
|
||||
[TYPE_ELECTRIC] = {______, ______, X(2.0), ______, X(0.0), ______, ______, ______, ______, ______, ______, X(2.0), X(0.5), X(0.5), ______, ______, X(0.5), ______, ______},
|
||||
[TYPE_PSYCHIC] = {______, X(2.0), ______, X(2.0), ______, ______, ______, ______, X(0.5), ______, ______, ______, ______, ______, X(0.5), ______, ______, X(0.0), ______},
|
||||
[TYPE_ICE] = {______, ______, X(2.0), ______, X(2.0), ______, ______, ______, X(0.5), ______, X(0.5), X(0.5), X(2.0), ______, ______, X(0.5), X(2.0), ______, ______},
|
||||
[TYPE_DRAGON] = {______, ______, ______, ______, ______, ______, ______, ______, X(0.5), ______, ______, ______, ______, ______, ______, ______, X(2.0), ______, X(0.0)},
|
||||
[TYPE_STEEL] = {______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), ______, X(0.5), X(0.5), ______, X(0.5), ______, X(2.0), ______, ______, X(2.0), ______},
|
||||
[TYPE_MYSTERY] = {______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______},
|
||||
[TYPE_FIRE] = {______, ______, ______, ______, ______, X(0.5), X(2.0), ______, X(2.0), ______, X(0.5), X(0.5), X(2.0), ______, ______, X(2.0), X(0.5), ______, ______, ______},
|
||||
[TYPE_WATER] = {______, ______, ______, ______, X(2.0), X(2.0), ______, ______, ______, ______, X(2.0), X(0.5), X(0.5), ______, ______, ______, X(0.5), ______, ______, ______},
|
||||
[TYPE_GRASS] = {______, ______, X(0.5), X(0.5), X(2.0), X(2.0), X(0.5), ______, X(0.5), ______, X(0.5), X(2.0), X(0.5), ______, ______, ______, X(0.5), ______, ______, ______},
|
||||
[TYPE_ELECTRIC] = {______, ______, X(2.0), ______, X(0.0), ______, ______, ______, ______, ______, ______, X(2.0), X(0.5), X(0.5), ______, ______, X(0.5), ______, ______, ______},
|
||||
[TYPE_PSYCHIC] = {______, X(2.0), ______, X(2.0), ______, ______, ______, ______, X(0.5), ______, ______, ______, ______, ______, X(0.5), ______, ______, X(0.0), ______, ______},
|
||||
[TYPE_ICE] = {______, ______, X(2.0), ______, X(2.0), ______, ______, ______, X(0.5), ______, X(0.5), X(0.5), X(2.0), ______, ______, X(0.5), X(2.0), ______, ______, ______},
|
||||
[TYPE_DRAGON] = {______, ______, ______, ______, ______, ______, ______, ______, X(0.5), ______, ______, ______, ______, ______, ______, ______, X(2.0), ______, X(0.0), ______},
|
||||
#if B_STEEL_RESISTANCES >= GEN_6
|
||||
[TYPE_DARK] = {______, X(0.5), ______, ______, ______, ______, ______, X(2.0), ______, ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), X(0.5)},
|
||||
[TYPE_DARK] = {______, X(0.5), ______, ______, ______, ______, ______, X(2.0), ______, ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), X(0.5), ______},
|
||||
#else
|
||||
[TYPE_DARK] = {______, X(0.5), ______, ______, ______, ______, ______, X(2.0), X(0.5), ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), X(0.5)},
|
||||
[TYPE_DARK] = {______, X(0.5), ______, ______, ______, ______, ______, X(2.0), X(0.5), ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), X(0.5), ______},
|
||||
#endif
|
||||
[TYPE_FAIRY] = {______, X(2.0), ______, X(0.5), ______, ______, ______, ______, X(0.5), ______, X(0.5), ______, ______, ______, ______, ______, X(2.0), X(2.0), ______},
|
||||
[TYPE_FAIRY] = {______, X(2.0), ______, X(0.5), ______, ______, ______, ______, X(0.5), ______, X(0.5), ______, ______, ______, ______, ______, X(2.0), X(2.0), ______, ______},
|
||||
[TYPE_STELLAR] = {______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______},
|
||||
};
|
||||
|
||||
#undef ______
|
||||
|
@ -1276,7 +1287,7 @@ static 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);
|
||||
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];
|
||||
|
@ -5349,6 +5360,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
|
|||
&& gMovesInfo[move].power != 0
|
||||
&& TARGET_TURN_DAMAGED
|
||||
&& !IS_BATTLER_OF_TYPE(battler, moveType)
|
||||
&& moveType != TYPE_STELLAR
|
||||
&& gBattleMons[battler].hp != 0)
|
||||
{
|
||||
SET_BATTLER_TYPE(battler, moveType);
|
||||
|
@ -8842,6 +8854,10 @@ static inline u32 CalcMoveBasePower(u32 move, u32 battlerAtk, u32 battlerDef, u3
|
|||
if (RandomPercentage(RNG_FICKLE_BEAM, 30))
|
||||
basePower *= 2;
|
||||
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;
|
||||
|
@ -9210,6 +9226,19 @@ static inline u32 CalcMoveBasePowerAfterModifiers(u32 move, u32 battlerAtk, u32
|
|||
modifier = uq4_12_multiply(modifier, UQ_4_12(1.1));
|
||||
break;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
return uq4_12_multiply_by_int_half_down(modifier, basePower);
|
||||
}
|
||||
|
||||
|
@ -9912,7 +9941,10 @@ static inline s32 DoMoveDamageCalcVars(u32 move, u32 battlerAtk, u32 battlerDef,
|
|||
dmg /= 100;
|
||||
}
|
||||
|
||||
DAMAGE_APPLY_MODIFIER(GetSameTypeAttackBonusModifier(battlerAtk, moveType, move, abilityAtk));
|
||||
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));
|
||||
DAMAGE_APPLY_MODIFIER(GetZMaxMoveAgainstProtectionModifier(battlerDef, move));
|
||||
|
@ -10119,12 +10151,12 @@ static inline uq4_12_t CalcTypeEffectivenessMultiplierInternal(u32 move, u32 mov
|
|||
{
|
||||
u32 illusionSpecies;
|
||||
|
||||
MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, GetBattlerType(battlerDef, 0), battlerAtk, recordAbilities);
|
||||
if (GetBattlerType(battlerDef, 1) != GetBattlerType(battlerDef, 0))
|
||||
MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, GetBattlerType(battlerDef, 1), battlerAtk, recordAbilities);
|
||||
if (GetBattlerType(battlerDef, 2) != TYPE_MYSTERY && GetBattlerType(battlerDef, 2) != GetBattlerType(battlerDef, 1)
|
||||
&& GetBattlerType(battlerDef, 2) != GetBattlerType(battlerDef, 0))
|
||||
MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, GetBattlerType(battlerDef, 2), battlerAtk, recordAbilities);
|
||||
MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, GetBattlerType(battlerDef, 0, FALSE), battlerAtk, recordAbilities);
|
||||
if (GetBattlerType(battlerDef, 1, FALSE) != GetBattlerType(battlerDef, 0, FALSE))
|
||||
MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, GetBattlerType(battlerDef, 1, FALSE), battlerAtk, recordAbilities);
|
||||
if (GetBattlerType(battlerDef, 2, FALSE) != TYPE_MYSTERY && GetBattlerType(battlerDef, 2, FALSE) != GetBattlerType(battlerDef, 1, FALSE)
|
||||
&& GetBattlerType(battlerDef, 2, FALSE) != GetBattlerType(battlerDef, 0, FALSE))
|
||||
MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, GetBattlerType(battlerDef, 2, FALSE), battlerAtk, recordAbilities);
|
||||
|
||||
if (recordAbilities && (illusionSpecies = GetIllusionMonSpecies(battlerDef)))
|
||||
TryNoticeIllusionInTypeEffectiveness(move, moveType, battlerAtk, battlerDef, modifier, illusionSpecies);
|
||||
|
@ -10185,12 +10217,16 @@ uq4_12_t CalcTypeEffectivenessMultiplier(u32 move, u32 moveType, u32 battlerAtk,
|
|||
{
|
||||
uq4_12_t modifier = UQ_4_12(1.0);
|
||||
|
||||
if (move != MOVE_STRUGGLE && moveType != TYPE_MYSTERY)
|
||||
if (move != MOVE_STRUGGLE && moveType != TYPE_MYSTERY && moveType != TYPE_STELLAR)
|
||||
{
|
||||
modifier = CalcTypeEffectivenessMultiplierInternal(move, moveType, battlerAtk, battlerDef, recordAbilities, modifier, defAbility);
|
||||
if (gMovesInfo[move].effect == EFFECT_TWO_TYPED_MOVE)
|
||||
modifier = CalcTypeEffectivenessMultiplierInternal(move, gMovesInfo[move].argument, battlerAtk, battlerDef, recordAbilities, modifier, defAbility);
|
||||
}
|
||||
else if (moveType == TYPE_STELLAR)
|
||||
{
|
||||
modifier = IsTerastallized(battlerDef) ? UQ_4_12(2.0) : UQ_4_12(1.0);
|
||||
}
|
||||
|
||||
if (recordAbilities)
|
||||
UpdateMoveResultFlags(modifier);
|
||||
|
@ -10640,8 +10676,8 @@ bool32 TryBattleFormChange(u32 battler, u16 method)
|
|||
bool32 DoBattlersShareType(u32 battler1, u32 battler2)
|
||||
{
|
||||
s32 i;
|
||||
u8 types1[3] = {GetBattlerType(battler1, 0), GetBattlerType(battler1, 1), GetBattlerType(battler1, 2)};
|
||||
u8 types2[3] = {GetBattlerType(battler2, 0), GetBattlerType(battler2, 1), GetBattlerType(battler2, 2)};
|
||||
u8 types1[3] = {GetBattlerType(battler1, 0, FALSE), GetBattlerType(battler1, 1, FALSE), GetBattlerType(battler1, 2, FALSE)};
|
||||
u8 types2[3] = {GetBattlerType(battler2, 0, FALSE), GetBattlerType(battler2, 1, FALSE), GetBattlerType(battler2, 2, FALSE)};
|
||||
|
||||
if (types1[2] == TYPE_MYSTERY)
|
||||
types1[2] = types1[0];
|
||||
|
@ -10823,7 +10859,7 @@ static bool32 IsUnnerveAbilityOnOpposingSide(u32 battler)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
// Photon geyser & light that burns the sky
|
||||
// Photon Geyser, Light That Burns the Sky, Tera Blast
|
||||
u8 GetCategoryBasedOnStats(u32 battler)
|
||||
{
|
||||
u32 attack = gBattleMons[battler].attack;
|
||||
|
@ -11381,17 +11417,23 @@ bool8 IsMonBannedFromSkyBattles(u16 species)
|
|||
}
|
||||
}
|
||||
|
||||
u8 GetBattlerType(u32 battler, u8 typeIndex)
|
||||
u8 GetBattlerType(u32 battler, u8 typeIndex, bool32 ignoreTera)
|
||||
{
|
||||
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
|
||||
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)
|
||||
if (gBattleResources->flags->flags[battler] & RESOURCE_FLAG_ROOST
|
||||
&& !IsTerastallized(battler))
|
||||
{
|
||||
if (types[0] == TYPE_FLYING && types[1] == TYPE_FLYING)
|
||||
return B_ROOST_PURE_FLYING >= GEN_5 ? TYPE_NORMAL : TYPE_MYSTERY;
|
||||
|
@ -11406,6 +11448,8 @@ u8 GetBattlerType(u32 battler, u8 typeIndex)
|
|||
void RemoveBattlerType(u32 battler, u8 type)
|
||||
{
|
||||
u32 i;
|
||||
if (IsTerastallized(battler)) // don't remove type if Terastallized
|
||||
return;
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
if (*(u8 *)(&gBattleMons[battler].type1 + i) == type)
|
||||
|
|
|
@ -2230,4 +2230,10 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
|
|||
.battleTvScore = 0, // TODO: Assign points
|
||||
.encourageEncore = TRUE,
|
||||
},
|
||||
|
||||
[EFFECT_TERA_BLAST] =
|
||||
{
|
||||
.battleScript = BattleScript_EffectPhotonGeyser,
|
||||
.battleTvScore = 0, // TODO: Assign points
|
||||
},
|
||||
};
|
||||
|
|
|
@ -18440,7 +18440,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
|
|||
.description = COMPOUND_STRING(
|
||||
"If the user's Terastallized,\n"
|
||||
"it hits with its Tera-type."),
|
||||
.effect = EFFECT_PLACEHOLDER, // EFFECT_TERA_BLAST,
|
||||
.effect = EFFECT_TERA_BLAST,
|
||||
.power = 80,
|
||||
.type = TYPE_NORMAL,
|
||||
.accuracy = 100,
|
||||
|
@ -18449,6 +18449,10 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
|
|||
.priority = 0,
|
||||
.category = DAMAGE_CATEGORY_SPECIAL,
|
||||
.forcePressure = TRUE,
|
||||
.additionalEffects = ADDITIONAL_EFFECTS({
|
||||
.moveEffect = MOVE_EFFECT_TERA_BLAST,
|
||||
.self = TRUE,
|
||||
}),
|
||||
},
|
||||
|
||||
[MOVE_SILK_TRAP] =
|
||||
|
|
|
@ -891,6 +891,10 @@ static const union AnimCmd sSpriteAnim_TypeFairy[] = {
|
|||
ANIMCMD_FRAME(TYPE_FAIRY * 8, 0, FALSE, FALSE),
|
||||
ANIMCMD_END
|
||||
};
|
||||
static const union AnimCmd sSpriteAnim_TypeStellar[] = {
|
||||
ANIMCMD_FRAME(TYPE_STELLAR * 8, 0, FALSE, FALSE),
|
||||
ANIMCMD_END
|
||||
};
|
||||
static const union AnimCmd sSpriteAnim_CategoryCool[] = {
|
||||
ANIMCMD_FRAME((CONTEST_CATEGORY_COOL + NUMBER_OF_MON_TYPES) * 8, 0, FALSE, FALSE),
|
||||
ANIMCMD_END
|
||||
|
@ -931,6 +935,7 @@ static const union AnimCmd *const sSpriteAnimTable_MoveTypes[NUMBER_OF_MON_TYPES
|
|||
sSpriteAnim_TypeDragon,
|
||||
sSpriteAnim_TypeDark,
|
||||
sSpriteAnim_TypeFairy,
|
||||
sSpriteAnim_TypeStellar,
|
||||
sSpriteAnim_CategoryCool,
|
||||
sSpriteAnim_CategoryBeauty,
|
||||
sSpriteAnim_CategoryCute,
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "decompress.h"
|
||||
#include "event_data.h"
|
||||
#include "international_string_util.h"
|
||||
#include "item.h"
|
||||
#include "link.h"
|
||||
#include "link_rfu.h"
|
||||
#include "main.h"
|
||||
|
@ -38,6 +39,10 @@ void HealPlayerParty(void)
|
|||
HealPokemon(&gPlayerParty[i]);
|
||||
if (OW_PC_HEAL >= GEN_8)
|
||||
HealPlayerBoxes();
|
||||
|
||||
// Recharge Tera Orb, if possible.
|
||||
if (B_FLAG_TERA_ORB_CHARGED != 0 && CheckBagHasItem(ITEM_TERA_ORB, 1))
|
||||
FlagSet(B_FLAG_TERA_ORB_CHARGED);
|
||||
}
|
||||
|
||||
static void HealPlayerBoxes(void)
|
||||
|
|
802
test/battle/gimmick/terastal.c
Normal file
|
@ -0,0 +1,802 @@
|
|||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
// Base Power and STAB Checks
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Terastallizing into a different type preserves other STAB boosts", s16 damage1, s16 damage2)
|
||||
{
|
||||
bool32 tera;
|
||||
PARAMETRIZE { tera = FALSE; }
|
||||
PARAMETRIZE { tera = TRUE; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_BULBASAUR) { TeraType(TYPE_NORMAL); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_VINE_WHIP, tera: tera); }
|
||||
TURN { MOVE(player, MOVE_SLUDGE_BOMB); }
|
||||
} SCENE {
|
||||
MESSAGE("Bulbasaur used Vine Whip!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_VINE_WHIP, player);
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage1);
|
||||
MESSAGE("Bulbasaur used Sludge Bomb!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SLUDGE_BOMB, player);
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage2);
|
||||
} FINALLY {
|
||||
EXPECT_EQ(results[0].damage1, results[1].damage1);
|
||||
EXPECT_EQ(results[0].damage2, results[1].damage2);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Terastallizing does not affect the power of non-STAB moves", s16 damage)
|
||||
{
|
||||
bool32 tera;
|
||||
PARAMETRIZE { tera = FALSE; }
|
||||
PARAMETRIZE { tera = TRUE; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_PSYCHIC); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_HEADBUTT, tera: tera); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used Headbutt!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_HEADBUTT, player);
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
EXPECT_EQ(results[0].damage, results[1].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Terastallizing into a different type gives that type 1.5x STAB", s16 damage)
|
||||
{
|
||||
bool32 tera;
|
||||
PARAMETRIZE { tera = FALSE; }
|
||||
PARAMETRIZE { tera = TRUE; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_NORMAL); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_HEADBUTT, tera: tera); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used Headbutt!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_HEADBUTT, player);
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
// The jump from no STAB to 1.5x STAB is a 1.5x boost.
|
||||
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Terastallizing into the same type gives that type 2x STAB", s16 damage)
|
||||
{
|
||||
bool32 tera;
|
||||
PARAMETRIZE { tera = FALSE; }
|
||||
PARAMETRIZE { tera = TRUE; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_PSYCHIC); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_PSYCHIC, tera: tera); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used Psychic!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYCHIC, player);
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
// The jump from 1.5x STAB to 2.0x STAB is a 1.33x boost.
|
||||
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.33), results[1].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Terastallizing into a different type with Adaptability gives 2.0x STAB", s16 damage)
|
||||
{
|
||||
bool32 tera;
|
||||
PARAMETRIZE { tera = FALSE; }
|
||||
PARAMETRIZE { tera = TRUE; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_CRAWDAUNT) { Ability(ABILITY_ADAPTABILITY); TeraType(TYPE_NORMAL); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_HEADBUTT, tera: tera); }
|
||||
} SCENE {
|
||||
MESSAGE("Crawdaunt used Headbutt!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_HEADBUTT, player);
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
// The jump from no STAB to 2.0x STAB is a 2.0x boost.
|
||||
EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[1].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Terastallizing into the same type with Adaptability gives 2.25x STAB", s16 damage)
|
||||
{
|
||||
bool32 tera;
|
||||
PARAMETRIZE { tera = FALSE; }
|
||||
PARAMETRIZE { tera = TRUE; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_CRAWDAUNT) { Ability(ABILITY_ADAPTABILITY); TeraType(TYPE_WATER); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_WATER_PULSE, tera: tera); }
|
||||
} SCENE {
|
||||
MESSAGE("Crawdaunt used Water Pulse!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_PULSE, player);
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
// The jump from 2x STAB to 2.25x STAB is a 1.125x boost.
|
||||
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.125), results[1].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Terastallizing boosts moves of the same type to 60 BP", s16 damage)
|
||||
{
|
||||
bool32 tera;
|
||||
PARAMETRIZE { tera = FALSE; }
|
||||
PARAMETRIZE { tera = TRUE; }
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_ABSORB].power == 20);
|
||||
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_GRASS); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_ABSORB, tera: tera); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used Absorb!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ABSORB, player);
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
// The jump from 20 BP to 90 BP (60 * 1.5x) is a 4.5x boost.
|
||||
EXPECT_MUL_EQ(results[0].damage, Q_4_12(4.5), results[1].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Terastallization's 60 BP floor occurs after Technician", s16 damage)
|
||||
{
|
||||
bool32 tera;
|
||||
PARAMETRIZE { tera = FALSE; }
|
||||
PARAMETRIZE { tera = TRUE; }
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_MEGA_DRAIN].power == 40);
|
||||
PLAYER(SPECIES_MR_MIME) { Ability(ABILITY_TECHNICIAN); TeraType(TYPE_GRASS); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_MEGA_DRAIN, tera: tera); }
|
||||
} SCENE {
|
||||
MESSAGE("Mr. Mime used Mega Drain!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_MEGA_DRAIN, player);
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
// This should be the same as a normal Tera boost.
|
||||
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Terastallization's 60 BP floor occurs after Technician", s16 damage)
|
||||
{
|
||||
bool32 tera;
|
||||
PARAMETRIZE { tera = FALSE; }
|
||||
PARAMETRIZE { tera = TRUE; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_MR_MIME) { Ability(ABILITY_TECHNICIAN); TeraType(TYPE_PSYCHIC); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_STORED_POWER, tera: tera); }
|
||||
} SCENE {
|
||||
MESSAGE("Mr. Mime used Stored Power!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_STORED_POWER, player);
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
// The jump from 45 BP (20 * 1.5x * 1.5x) to 120 BP (60 * 2.0x) is a 2.667x boost.
|
||||
EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.667), results[1].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Terastallization's 60 BP floor does not apply to multi-hit moves", s16 damage)
|
||||
{
|
||||
bool32 tera;
|
||||
PARAMETRIZE { tera = FALSE; }
|
||||
PARAMETRIZE { tera = TRUE; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_NORMAL); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_FURY_SWIPES, tera: tera); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used Fury Swipes!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FURY_SWIPES, player);
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Terastallization's 60 BP floor does not apply to priority moves", s16 damage)
|
||||
{
|
||||
bool32 tera;
|
||||
PARAMETRIZE { tera = FALSE; }
|
||||
PARAMETRIZE { tera = TRUE; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_NORMAL); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_QUICK_ATTACK, tera: tera); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used Quick Attack!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, player);
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);
|
||||
}
|
||||
}
|
||||
|
||||
// Defensive Type Checks
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Terastallization changes type effectiveness", s16 damage)
|
||||
{
|
||||
bool32 tera;
|
||||
PARAMETRIZE { tera = FALSE; }
|
||||
PARAMETRIZE { tera = TRUE; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_GRASS); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_CELEBRATE, tera: tera); MOVE(opponent, MOVE_WATER_GUN); }
|
||||
} SCENE {
|
||||
MESSAGE("Foe Wobbuffet used Water Gun!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, opponent);
|
||||
HP_BAR(player, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Terastallization changes type effectiveness")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_FLYING); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_CELEBRATE, tera: TRUE); MOVE(opponent, MOVE_EARTHQUAKE); }
|
||||
} SCENE {
|
||||
MESSAGE("Foe Wobbuffet used Earthquake!");
|
||||
MESSAGE("It doesn't affect Wobbuffet…");
|
||||
NOT { HP_BAR(player); }
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Terastallization persists across switches")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_FLYING); }
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_CELEBRATE, tera: TRUE); MOVE(opponent, MOVE_EARTHQUAKE); }
|
||||
TURN { SWITCH(player, 1); }
|
||||
TURN { SWITCH(player, 0); }
|
||||
TURN { MOVE(opponent, MOVE_EARTHQUAKE); }
|
||||
} SCENE {
|
||||
// turn 1
|
||||
MESSAGE("Foe Wobbuffet used Earthquake!");
|
||||
MESSAGE("It doesn't affect Wobbuffet…");
|
||||
NOT { HP_BAR(player); }
|
||||
// turn 4
|
||||
MESSAGE("Foe Wobbuffet used Earthquake!");
|
||||
MESSAGE("It doesn't affect Wobbuffet…");
|
||||
NOT { HP_BAR(player); }
|
||||
}
|
||||
}
|
||||
|
||||
// Other Type Checks
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Terastallization changes the effect of Curse")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_GHOST); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_CURSE, tera: TRUE); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used Curse!");
|
||||
HP_BAR(player);
|
||||
MESSAGE("Wobbuffet cut its own HP and laid a CURSE on Foe Wobbuffet!");
|
||||
NOT { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); }
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Roost does not remove the user's Flying type while Terastallized")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_ZAPDOS) { HP(1); TeraType(TYPE_FLYING); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_ROOST, tera: TRUE); MOVE(opponent, MOVE_ICE_BEAM); }
|
||||
} SCENE {
|
||||
MESSAGE("Zapdos used Roost!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROOST, player);
|
||||
MESSAGE("Foe Wobbuffet used Ice Beam!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ICE_BEAM, opponent);
|
||||
MESSAGE("It's super effective!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Type-changing moves fail against a Terastallized Pokemon")
|
||||
{
|
||||
u16 move;
|
||||
PARAMETRIZE { move = MOVE_SOAK; }
|
||||
PARAMETRIZE { move = MOVE_FORESTS_CURSE; }
|
||||
PARAMETRIZE { move = MOVE_TRICK_OR_TREAT; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_PSYCHIC); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_CELEBRATE, tera: TRUE); MOVE(opponent, move); }
|
||||
} SCENE {
|
||||
if (move != MOVE_SOAK)
|
||||
NOT { ANIMATION(ANIM_TYPE_MOVE, move, opponent); }
|
||||
MESSAGE("But it failed!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Reflect Type fails if used by a Terastallized Pokemon")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_PSYCHIC); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_REFLECT_TYPE, tera: TRUE); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used Reflect Type!");
|
||||
MESSAGE("But it failed!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Conversion fails if used by a Terastallized Pokemon")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_PSYCHIC); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_CONVERSION, tera: TRUE); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used Conversion!");
|
||||
MESSAGE("But it failed!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Conversion2 fails if used by a Terastallized Pokemon")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_PSYCHIC); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_TACKLE); }
|
||||
TURN { MOVE(player, MOVE_CONVERSION_2, tera: TRUE); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used Conversion 2!");
|
||||
MESSAGE("But it failed!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Reflect Type copies a Terastallized Pokemon's Tera Type")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_GHOST); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_CELEBRATE, tera: TRUE); }
|
||||
TURN { MOVE(opponent, MOVE_REFLECT_TYPE); }
|
||||
TURN { MOVE(player, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
// turn 2
|
||||
MESSAGE("Foe Wobbuffet used Reflect Type!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_REFLECT_TYPE, opponent);
|
||||
// turn 3
|
||||
MESSAGE("Wobbuffet used Tackle!");
|
||||
MESSAGE("It doesn't affect Foe Wobbuffet…");
|
||||
NOT { HP_BAR(opponent); }
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Synchronoise uses a Terastallized Pokemon's Tera Type")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_GHOST); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { TeraType(TYPE_GHOST); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_SYNCHRONOISE); MOVE(player, MOVE_CELEBRATE, tera: TRUE); }
|
||||
TURN { MOVE(opponent, MOVE_SYNCHRONOISE, tera: TRUE); }
|
||||
} SCENE {
|
||||
// turn 1
|
||||
MESSAGE("Foe Wobbuffet used Synchronoise!");
|
||||
MESSAGE("It had no effect on Wobbuffet!");
|
||||
// turn 2
|
||||
MESSAGE("Foe Wobbuffet used Synchronoise!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SYNCHRONOISE, opponent);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Revelation Dance uses a Terastallized Pokemon's Tera Type")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(P_GEN_7_POKEMON);
|
||||
PLAYER(SPECIES_ORICORIO) { TeraType(TYPE_NORMAL); }
|
||||
OPPONENT(SPECIES_GENGAR);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_REVELATION_DANCE, tera: TRUE); }
|
||||
} SCENE {
|
||||
#if B_EXPANDED_MOVE_NAMES == TRUE
|
||||
MESSAGE("Oricorio used Revelation Dance!");
|
||||
#else
|
||||
MESSAGE("Oricorio used RvlationDnce!");
|
||||
#endif
|
||||
MESSAGE("It doesn't affect Foe Gengar…");
|
||||
NOT { HP_BAR(opponent); }
|
||||
}
|
||||
}
|
||||
|
||||
// This tests that Tera STAB modifiers depend on the user's original types, too.
|
||||
SINGLE_BATTLE_TEST("(TERA) Double Shock does not remove the user's Electric type while Terastallized, and changes STAB modifier depending on when it is used")
|
||||
{
|
||||
s16 damage[4];
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_DOUBLE_SHOCK].effect == EFFECT_FAIL_IF_NOT_ARG_TYPE);
|
||||
PLAYER(SPECIES_PICHU) { TeraType(TYPE_ELECTRIC); }
|
||||
PLAYER(SPECIES_WOBBUFFET)
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_DOUBLE_SHOCK); MOVE(opponent, MOVE_RECOVER); }
|
||||
TURN { MOVE(player, MOVE_DOUBLE_SHOCK, tera: TRUE); MOVE(opponent, MOVE_RECOVER); }
|
||||
TURN { MOVE(player, MOVE_DOUBLE_SHOCK); MOVE(opponent, MOVE_RECOVER); }
|
||||
TURN { SWITCH(player, 1); MOVE(opponent, MOVE_RECOVER); }
|
||||
TURN { SWITCH(player, 0); MOVE(opponent, MOVE_RECOVER); }
|
||||
TURN { MOVE(player, MOVE_DOUBLE_SHOCK); MOVE(opponent, MOVE_RECOVER); }
|
||||
TURN { MOVE(player, MOVE_DOUBLE_SHOCK); }
|
||||
} SCENE {
|
||||
// turn 1 - regular STAB
|
||||
MESSAGE("Pichu used Double Shock!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SHOCK, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[0]);
|
||||
// turn 2 - lost Electric type, gained back from Tera
|
||||
MESSAGE("Pichu used Double Shock!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SHOCK, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[1]);
|
||||
// turn 3 - retained Electric type
|
||||
MESSAGE("Pichu used Double Shock!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SHOCK, player);
|
||||
// turn 6 - original type reset, regular STAB + Tera boost
|
||||
MESSAGE("Pichu used Double Shock!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SHOCK, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[2]);
|
||||
// turn 7 - regular STAB + Tera boost stays
|
||||
MESSAGE("Pichu used Double Shock!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SHOCK, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[3]);
|
||||
} THEN {
|
||||
EXPECT_EQ(damage[0], damage[1]);
|
||||
EXPECT_MUL_EQ(damage[0], Q_4_12(1.333), damage[2]);
|
||||
EXPECT_EQ(damage[2], damage[3]);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Transform does not copy the target's Tera Type, and if the user is Terastallized it keeps its own Tera Type")
|
||||
{
|
||||
KNOWN_FAILING; // Transform seems to be bugged in tests.
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_TACKLE, MOVE_EARTHQUAKE); TeraType(TYPE_GHOST); }
|
||||
OPPONENT(SPECIES_DITTO) { TeraType(TYPE_FLYING); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_CELEBRATE, tera: TRUE); MOVE(opponent, MOVE_TRANSFORM); }
|
||||
TURN { MOVE(player, MOVE_EARTHQUAKE); }
|
||||
// TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_TACKLE, target: player, tera: TRUE); }
|
||||
} SCENE {
|
||||
// turn 2
|
||||
MESSAGE("Wobbuffet used Earthquake!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_EARTHQUAKE, player);
|
||||
HP_BAR(opponent);
|
||||
// turn 3
|
||||
MESSAGE("Wobbuffet used Tackle!");
|
||||
MESSAGE("It doesn't affect Ditto…");
|
||||
NOT { HP_BAR(opponent); }
|
||||
}
|
||||
}
|
||||
|
||||
// Stellar Type checks
|
||||
SINGLE_BATTLE_TEST("(TERA) Stellar type does not change the user's defensive profile", s16 damage)
|
||||
{
|
||||
bool32 tera;
|
||||
PARAMETRIZE { tera = FALSE; }
|
||||
PARAMETRIZE { tera = TRUE; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_CELEBRATE, tera: tera); MOVE(opponent, MOVE_PSYCHIC); }
|
||||
} SCENE {
|
||||
MESSAGE("Foe Wobbuffet used Psychic!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYCHIC, opponent);
|
||||
HP_BAR(player, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
EXPECT_EQ(results[0].damage, results[1].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Reflect Type copies a Stellar-type Pokemon's base type")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_BANETTE) { TeraType(TYPE_STELLAR); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_CELEBRATE, tera: TRUE); }
|
||||
TURN { MOVE(opponent, MOVE_REFLECT_TYPE); }
|
||||
TURN { MOVE(player, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
// turn 2
|
||||
MESSAGE("Foe Wobbuffet used Reflect Type!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_REFLECT_TYPE, opponent);
|
||||
// turn 3
|
||||
MESSAGE("Banette used Tackle!");
|
||||
MESSAGE("It doesn't affect Foe Wobbuffet…");
|
||||
NOT { HP_BAR(opponent); }
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Revelation Dance uses a Stellar-type Pokemon's base type")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(P_GEN_7_POKEMON);
|
||||
PLAYER(SPECIES_ORICORIO_SENSU) { TeraType(TYPE_STELLAR); }
|
||||
OPPONENT(SPECIES_GUMSHOOS);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_REVELATION_DANCE, tera: TRUE); }
|
||||
} SCENE {
|
||||
#if B_EXPANDED_MOVE_NAMES == TRUE
|
||||
MESSAGE("Oricorio used Revelation Dance!");
|
||||
#else
|
||||
MESSAGE("Oricorio used RvlationDnce!");
|
||||
#endif
|
||||
MESSAGE("It doesn't affect Foe Gumshoos…");
|
||||
NOT { HP_BAR(opponent); }
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Conversion2 fails if last hit by a Stellar-type move")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TERA_BLAST, tera: TRUE); }
|
||||
TURN { MOVE(opponent, MOVE_CONVERSION_2); }
|
||||
} SCENE {
|
||||
// turn 1
|
||||
MESSAGE("Wobbuffet used Tera Blast!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player);
|
||||
// turn 2
|
||||
MESSAGE("Foe Wobbuffet used Conversion 2!");
|
||||
MESSAGE("But it failed!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Roost does not remove Flying-type ground immunity when Terastallized into the Stellar type")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_ZAPDOS) { HP(1); TeraType(TYPE_STELLAR); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_ROOST, tera: TRUE); MOVE(opponent, MOVE_ICE_BEAM); }
|
||||
} SCENE {
|
||||
MESSAGE("Zapdos used Roost!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROOST, player);
|
||||
MESSAGE("Foe Wobbuffet used Ice Beam!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ICE_BEAM, opponent);
|
||||
MESSAGE("It's super effective!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Terastallizing into the Stellar-type provides a one-time 2.0x boost to STAB moves")
|
||||
{
|
||||
s16 damage[3];
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_EXTRASENSORY); }
|
||||
TURN { MOVE(player, MOVE_EXTRASENSORY, tera: TRUE); }
|
||||
TURN { MOVE(player, MOVE_EXTRASENSORY); }
|
||||
} SCENE {
|
||||
// turn 1
|
||||
MESSAGE("Wobbuffet used Extrasensory!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_EXTRASENSORY, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[0]);
|
||||
// turn 2
|
||||
MESSAGE("Wobbuffet used Extrasensory!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_EXTRASENSORY, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[1]);
|
||||
// turn 3
|
||||
MESSAGE("Wobbuffet used Extrasensory!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_EXTRASENSORY, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[2]);
|
||||
} THEN {
|
||||
// Extrasensory goes from a 50% boost to a 100% boost for a 1.33x total multiplier
|
||||
EXPECT_MUL_EQ(damage[0], UQ_4_12(1.33), damage[1]);
|
||||
EXPECT_EQ(damage[0], damage[2]);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Terastallizing into the Stellar-type provides a one-time 1.2x boost to non-STAB moves")
|
||||
{
|
||||
s16 damage[3];
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TAKE_DOWN); }
|
||||
TURN { MOVE(player, MOVE_TAKE_DOWN, tera: TRUE); }
|
||||
TURN { MOVE(player, MOVE_TAKE_DOWN); }
|
||||
} SCENE {
|
||||
// turn 1
|
||||
MESSAGE("Wobbuffet used Take Down!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TAKE_DOWN, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[0]);
|
||||
// turn 2
|
||||
MESSAGE("Wobbuffet used Take Down!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TAKE_DOWN, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[1]);
|
||||
// turn 3
|
||||
MESSAGE("Wobbuffet used Take Down!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TAKE_DOWN, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[2]);
|
||||
} THEN {
|
||||
EXPECT_MUL_EQ(damage[0], UQ_4_12(1.2), damage[1]);
|
||||
EXPECT_EQ(damage[0], damage[2]);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Terastallizing into the Stellar type boosts all moves up to 60 BP once per type")
|
||||
{
|
||||
s16 damage[4];
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_MEGA_DRAIN].power == 40);
|
||||
ASSUME(gMovesInfo[MOVE_BUBBLE].power == 40);
|
||||
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_MEGA_DRAIN); }
|
||||
TURN { MOVE(player, MOVE_MEGA_DRAIN, tera: TRUE); }
|
||||
TURN { MOVE(player, MOVE_MEGA_DRAIN); }
|
||||
TURN { MOVE(player, MOVE_BUBBLE); }
|
||||
} SCENE {
|
||||
// turn 1
|
||||
MESSAGE("Wobbuffet used Mega Drain!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_MEGA_DRAIN, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[0]);
|
||||
// turn 2
|
||||
MESSAGE("Wobbuffet used Mega Drain!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_MEGA_DRAIN, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[1]);
|
||||
// turn 3
|
||||
MESSAGE("Wobbuffet used Mega Drain!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_MEGA_DRAIN, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[2]);
|
||||
// turn 4
|
||||
MESSAGE("Wobbuffet used Bubble!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BUBBLE, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[3]);
|
||||
} THEN {
|
||||
// The jump from 40 BP to 72 BP (60 * 1.2x) is a 1.8x boost.
|
||||
EXPECT_MUL_EQ(damage[0], Q_4_12(1.8), damage[1]);
|
||||
EXPECT_EQ(damage[0], damage[2]);
|
||||
EXPECT_EQ(damage[1], damage[3]);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Protean cannot change the type of a Terastallized Pokemon")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_GRENINJA) { Ability(ABILITY_PROTEAN); TeraType(TYPE_GRASS); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_BUBBLE, tera: TRUE);
|
||||
MOVE(opponent, MOVE_EMBER); }
|
||||
} SCENE {
|
||||
MESSAGE("Greninja used Bubble!");
|
||||
MESSAGE("Foe Wobbuffet used Ember!");
|
||||
MESSAGE("It's super effective!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Status moves don't expend Stellar's one-time type boost")
|
||||
{
|
||||
s16 damage[2];
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_GROWL, tera: TRUE); }
|
||||
TURN { MOVE(player, MOVE_TAKE_DOWN); }
|
||||
TURN { MOVE(player, MOVE_TAKE_DOWN); }
|
||||
} SCENE {
|
||||
// turn 1
|
||||
MESSAGE("Wobbuffet used Growl!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_GROWL, player);
|
||||
// turn 2
|
||||
MESSAGE("Wobbuffet used Take Down!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TAKE_DOWN, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[0]);
|
||||
// turn 3
|
||||
MESSAGE("Wobbuffet used Take Down!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TAKE_DOWN, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[1]);
|
||||
} THEN {
|
||||
EXPECT_MUL_EQ(damage[1], UQ_4_12(1.20), damage[0]);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Stellar type's one-time boost factors in dynamically-typed moves")
|
||||
{
|
||||
s16 damage[4];
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_WEATHER_BALL].type == TYPE_NORMAL);
|
||||
PLAYER(SPECIES_PELIPPER) { Ability(ABILITY_DRIZZLE); TeraType(TYPE_STELLAR); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_WEATHER_BALL, tera: TRUE); MOVE(opponent, MOVE_RECOVER); }
|
||||
TURN { MOVE(player, MOVE_TAKE_DOWN); MOVE(opponent, MOVE_RECOVER); }
|
||||
TURN { MOVE(player, MOVE_TAKE_DOWN); MOVE(opponent, MOVE_RECOVER); }
|
||||
TURN { MOVE(player, MOVE_WATER_PULSE); MOVE(opponent, MOVE_RECOVER); }
|
||||
TURN { MOVE(player, MOVE_WATER_PULSE); MOVE(opponent, MOVE_RECOVER); }
|
||||
} SCENE {
|
||||
MESSAGE("Pelipper used Weather Ball!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_WEATHER_BALL, player);
|
||||
// turn 2
|
||||
MESSAGE("Pelipper used Take Down!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TAKE_DOWN, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[0]);
|
||||
// turn 3
|
||||
MESSAGE("Pelipper used Take Down!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TAKE_DOWN, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[1]);
|
||||
// turn 4
|
||||
MESSAGE("Pelipper used Water Pulse!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_PULSE, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[2]);
|
||||
// turn 5
|
||||
MESSAGE("Pelipper used Water Pulse!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_PULSE, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[3]);
|
||||
} THEN {
|
||||
// Take Down should have a Normal type boost applied
|
||||
EXPECT_MUL_EQ(damage[1], UQ_4_12(1.20), damage[0]);
|
||||
// Water Pulse should not have a Water type boost applied
|
||||
EXPECT_EQ(damage[3], damage[2]);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) All type indicators function correctly")
|
||||
{
|
||||
u32 type;
|
||||
PARAMETRIZE { type = TYPE_NORMAL; }
|
||||
PARAMETRIZE { type = TYPE_FIGHTING; }
|
||||
PARAMETRIZE { type = TYPE_FLYING; }
|
||||
PARAMETRIZE { type = TYPE_POISON; }
|
||||
PARAMETRIZE { type = TYPE_GROUND; }
|
||||
PARAMETRIZE { type = TYPE_ROCK; }
|
||||
PARAMETRIZE { type = TYPE_BUG; }
|
||||
PARAMETRIZE { type = TYPE_GHOST; }
|
||||
PARAMETRIZE { type = TYPE_STEEL; }
|
||||
PARAMETRIZE { type = TYPE_MYSTERY; }
|
||||
PARAMETRIZE { type = TYPE_FIRE; }
|
||||
PARAMETRIZE { type = TYPE_WATER; }
|
||||
PARAMETRIZE { type = TYPE_GRASS; }
|
||||
PARAMETRIZE { type = TYPE_ELECTRIC; }
|
||||
PARAMETRIZE { type = TYPE_PSYCHIC; }
|
||||
PARAMETRIZE { type = TYPE_ICE; }
|
||||
PARAMETRIZE { type = TYPE_DRAGON; }
|
||||
PARAMETRIZE { type = TYPE_DARK; }
|
||||
PARAMETRIZE { type = TYPE_FAIRY; }
|
||||
PARAMETRIZE { type = TYPE_STELLAR; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { TeraType(type); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_CELEBRATE, tera: TRUE); }
|
||||
}
|
||||
}
|
137
test/battle/move_effect/tera_blast.c
Normal file
|
@ -0,0 +1,137 @@
|
|||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_TERA_BLAST].effect == EFFECT_TERA_BLAST);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Tera Blast changes from Normal-type to the user's Tera Type")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_TERA_BLAST].type == TYPE_NORMAL);
|
||||
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_DARK); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TERA_BLAST, tera: TRUE); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used Tera Blast!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player);
|
||||
MESSAGE("It's super effective!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Tera Blast becomes a physical move if the user is Terastallized and has a higher Attack stat", s16 damage)
|
||||
{
|
||||
bool32 tera;
|
||||
PARAMETRIZE { tera = FALSE; }
|
||||
PARAMETRIZE { tera = TRUE; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_NORMAL); Attack(100); SpAttack(50); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Defense(200); SpDefense(200); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TERA_BLAST, tera: tera); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used Tera Blast!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player);
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
// Since Wobbuffett has equal defenses, Tera Blast should do 1.5x more damage
|
||||
// from gaining STAB and an additional 2.0x damage from using its highest
|
||||
// attacking stat.
|
||||
EXPECT_MUL_EQ(results[0].damage, UQ_4_12(3.0), results[1].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Stellar-type Tera Blast lowers both offensive stats")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TERA_BLAST, tera: TRUE); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used Tera Blast!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
|
||||
MESSAGE("Wobbuffet's Attack fell!");
|
||||
MESSAGE("Wobbuffet's Sp. Atk fell!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SINGLE_BATTLE_TEST("Stellar-type Tera Blast has 100 BP and a one-time 1.2x boost")
|
||||
{
|
||||
s16 damage[3];
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TERA_BLAST); MOVE(opponent, MOVE_RECOVER); }
|
||||
TURN { MOVE(player, MOVE_TERA_BLAST, tera: TRUE); }
|
||||
TURN { MOVE(player, MOVE_WORK_UP); }
|
||||
TURN { MOVE(player, MOVE_TERA_BLAST); }
|
||||
} SCENE {
|
||||
// turn 1
|
||||
MESSAGE("Wobbuffet used Tera Blast!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[0]);
|
||||
// turn 2
|
||||
MESSAGE("Wobbuffet used Tera Blast!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[1]);
|
||||
// turn 4
|
||||
MESSAGE("Wobbuffet used Tera Blast!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[2]);
|
||||
} THEN {
|
||||
// 80 BP to 120 BP (100 * 1.2) boost upon Terastallizing
|
||||
EXPECT_MUL_EQ(damage[0], UQ_4_12(1.50), damage[1]);
|
||||
// 120 BP to 100 BP after Stellar boost expended
|
||||
EXPECT_MUL_EQ(damage[2], UQ_4_12(1.20), damage[1]);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Stellar-type Tera Blast is super-effective on Stellar-type Pokemon")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TERA_BLAST, tera: TRUE); MOVE(opponent, MOVE_CELEBRATE, tera: TRUE); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used Tera Blast!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player);
|
||||
MESSAGE("It's super effective!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Stellar-type Tera Blast activates a Stellar-type Pokemon's Weakness Policy")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_WEAKNESS_POLICY); TeraType(TYPE_NORMAL); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TERA_BLAST, tera: TRUE); MOVE(opponent, MOVE_CELEBRATE, tera: TRUE); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used Tera Blast!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player);
|
||||
MESSAGE("It's super effective!");
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Flying-type Tera Blast does not have its priority boosted by Gale Wings")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_TALONFLAME) { Ability(ABILITY_GALE_WINGS); TeraType(TYPE_FLYING); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TERA_BLAST, tera: TRUE); MOVE(opponent, MOVE_QUICK_ATTACK); }
|
||||
} SCENE {
|
||||
MESSAGE("Foe Wobbuffet used Quick Attack!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, opponent);
|
||||
MESSAGE("Talonflame used Tera Blast!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player);
|
||||
}
|
||||
}
|
|
@ -2075,6 +2075,9 @@ void MoveGetIdAndSlot(s32 battlerId, struct MoveContext *ctx, u32 *moveId, u32 *
|
|||
|
||||
if (ctx->explicitDynamax && ctx->dynamax)
|
||||
*moveSlot |= RET_DYNAMAX;
|
||||
|
||||
if (ctx->explicitTera && ctx->tera)
|
||||
*moveSlot |= RET_TERASTAL;
|
||||
}
|
||||
|
||||
void Move(u32 sourceLine, struct BattlePokemon *battler, struct MoveContext ctx)
|
||||
|
|