Gimmick Refactor (#4449)

* consolidated gimmick checks, triggers, communication, and activation; updated test runner

* fixed improper use of .usableGimmick

* cleaning up battle_dynamax.c, changing function args to u32s

* fixed '#ifdef TESTING' causing errors

* updated z-moves to use gimmick interface, pared down redundancies; no AI/tests

* added support for z-moves in tests, consolidated gimmick fields

* removed ShouldUseMaxMove and .usingMaxMove

* renamed TryChangeZIndicator, updated z move display

* added several z-move tests and fixed various z-move interactions; fixed z-move category calc

* fixed useless battler arg in GetTypeBasedZMove

* added basic test check for bad Z-Move or Mega usage

* reworked test runner gimmick functionality; added support for Ultra Burst + Z-Move to test Light That Burns the Sky

* fixed gimmick test logic; fixed damage category override

* fixed mega rayquaza test fail

* consolidated gimmick indicator logic; added graphics to gGimmicksInfo

* removed TeraData struct

* reimplemented AI logic for Z-Moves; no changes

* updated Z-Move and Ultra Burst trigger gfx

* added testrunner check for multiple gimmick use

* fixed duplicate z-move call in test

* reorganized data/graphics/gimmicks.h

* added signature Z-Move ability tests; implemented Guardian of Alola

* fixed bad test update

* fixed Thousand Arrows not affecting Tera Flying; clean-up

* fixed -ate tests

* fixed tera tests

* fixed tera tests really

* fixed last batch of tests

* fixed -ate mega test again

* code review

* code review pt.2

* tweaked CanTera again

* dynamax flag only required for player
This commit is contained in:
AgustinGDLV 2024-06-22 13:25:40 -07:00 committed by GitHub
parent 2640bcd053
commit 9797640dff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
53 changed files with 2207 additions and 2657 deletions

View File

@ -1672,6 +1672,10 @@
callnative BS_ApplyTerastallization
.endm
.macro damagetoquartertargethp
callnative BS_DamageToQuarterTargetHP
.endm
@ various command changed to more readable macros
.macro cancelmultiturnmoves battler:req
various \battler, VARIOUS_CANCEL_MULTI_TURN_MOVES

View File

@ -20,6 +20,16 @@
.section script_data, "aw", %progbits
BattleScript_DamageToQuarterTargetHP::
attackcanceler
accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE
attackstring
ppreduce
typecalc
bichalfword gMoveResultFlags, MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE
damagetoquartertargethp
goto BattleScript_HitFromAtkAnimation
BattleScript_Terastallization::
@ TODO: no string prints in S/V, but right now this helps with clarity
printstring STRINGID_PKMNSTORINGENERGY
@ -6528,6 +6538,9 @@ BattleScript_PerishSongCountGoesDown::
waitmessage B_WAIT_TIME_LONG
end2
BattleScript_AllStatsUpZMove::
printfromtable gZEffectStringIds
waitmessage B_WAIT_TIME_LONG
BattleScript_AllStatsUp::
jumpifstat BS_ATTACKER, CMP_LESS_THAN, STAT_ATK, MAX_STAT_STAGE, BattleScript_AllStatsUpAtk
jumpifstat BS_ATTACKER, CMP_LESS_THAN, STAT_DEF, MAX_STAT_STAGE, BattleScript_AllStatsUpAtk
@ -6986,13 +6999,13 @@ BattleScript_MegaEvolution::
BattleScript_MegaEvolutionAfterString:
waitmessage B_WAIT_TIME_LONG
setbyte gIsCriticalHit, 0
handlemegaevo BS_ATTACKER, 0
playanimation BS_ATTACKER, B_ANIM_MEGA_EVOLUTION
handlemegaevo BS_SCRIPTING, 0
playanimation BS_SCRIPTING, B_ANIM_MEGA_EVOLUTION
waitanimation
handlemegaevo BS_ATTACKER, 1
handlemegaevo BS_SCRIPTING, 1
printstring STRINGID_MEGAEVOEVOLVED
waitmessage B_WAIT_TIME_LONG
switchinabilities BS_ATTACKER
switchinabilities BS_SCRIPTING
end3
BattleScript_WishMegaEvolution::
@ -7029,13 +7042,13 @@ BattleScript_UltraBurst::
printstring STRINGID_ULTRABURSTREACTING
waitmessage B_WAIT_TIME_LONG
setbyte gIsCriticalHit, 0
handleultraburst BS_ATTACKER, 0
playanimation BS_ATTACKER, B_ANIM_ULTRA_BURST
handleultraburst BS_SCRIPTING, 0
playanimation BS_SCRIPTING, B_ANIM_ULTRA_BURST
waitanimation
handleultraburst BS_ATTACKER, 1
handleultraburst BS_SCRIPTING, 1
printstring STRINGID_ULTRABURSTCOMPLETED
waitmessage B_WAIT_TIME_LONG
switchinabilities BS_ATTACKER
switchinabilities BS_SCRIPTING
end3
BattleScript_GulpMissileFormChange::

Binary file not shown.

Before

Width:  |  Height:  |  Size: 314 B

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 B

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@ -16,6 +16,7 @@
#include "battle_debug.h"
#include "battle_dynamax.h"
#include "battle_terastal.h"
#include "battle_gimmick.h"
#include "random.h" // for rng_value_t
// Helper for accessing command arguments and advancing gBattlescriptCurrInstr.
@ -370,8 +371,6 @@ 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
@ -558,24 +557,6 @@ struct LinkBattlerHeader
struct BattleEnigmaBerry battleEnigmaBerry;
};
struct MegaEvolutionData
{
u8 toEvolve; // As flags using gBitTable.
bool8 alreadyEvolved[4]; // Array id is used for mon position.
u8 battlerId;
bool8 playerSelect;
u8 triggerSpriteId;
};
struct UltraBurstData
{
u8 toBurst; // As flags using gBitTable.
bool8 alreadyBursted[4]; // Array id is used for mon position.
u8 battlerId;
bool8 playerSelect;
u8 triggerSpriteId;
};
struct Illusion
{
u8 on;
@ -587,48 +568,30 @@ struct Illusion
struct ZMoveData
{
u8 viable:1; // current move can become a z move
u8 viable:1; // current move can become a z move
u8 viewing:1; // if player is viewing the z move name instead of regular moves
u8 active:1; // is z move being used this turn
u8 zStatusActive:1;
u8 healReplacement:1;
u8 activeCategory:2; // active z move category
u8 zUnused:1;
u8 triggerSpriteId;
u8 healReplacement:6;
u8 possibleZMoves[MAX_BATTLERS_COUNT];
u16 chosenZMove; // z move of move cursor is on
u8 effect;
u8 used[MAX_BATTLERS_COUNT]; //one per bank for multi-battles
u16 toBeUsed[MAX_BATTLERS_COUNT]; // z moves per battler to be used
u16 baseMoves[MAX_BATTLERS_COUNT];
u8 categories[MAX_BATTLERS_COUNT];
};
struct DynamaxData
{
bool8 playerSelect;
u8 triggerSpriteId;
u8 toDynamax; // flags using gBitTable
bool8 alreadyDynamaxed[NUM_BATTLE_SIDES];
bool8 dynamaxed[MAX_BATTLERS_COUNT];
u8 dynamaxTurns[MAX_BATTLERS_COUNT];
u8 usingMaxMove[MAX_BATTLERS_COUNT];
u8 activeCategory;
u8 categories[MAX_BATTLERS_COUNT];
u16 baseMove[MAX_BATTLERS_COUNT]; // base move of Max Move
u16 baseMoves[MAX_BATTLERS_COUNT]; // base move of Max Move
u16 lastUsedBaseMove;
u16 levelUpHP;
};
struct TeraData
struct BattleGimmickData
{
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 usableGimmick[MAX_BATTLERS_COUNT]; // first usable gimmick that can be selected for each battler
bool8 playerSelect; // used to toggle trigger and update battle UI
u8 triggerSpriteId;
u8 indicatorSpriteId[MAX_BATTLERS_COUNT];
u8 toActivate; // stores whether a battler should transform at start of turn as bitfield
u8 activeGimmick[NUM_BATTLE_SIDES][PARTY_SIZE]; // stores the active gimmick for each party member
bool8 activated[MAX_BATTLERS_COUNT][GIMMICKS_COUNT]; // stores whether a trainer has used gimmick
};
struct LostItem
@ -753,11 +716,9 @@ struct BattleStruct
u8 activeAbilityPopUps; // as bits for each battler
u8 abilityPopUpSpriteIds[MAX_BATTLERS_COUNT][2]; // two per battler
bool8 throwingPokeBall;
struct MegaEvolutionData mega;
struct UltraBurstData burst;
struct ZMoveData zmove;
struct DynamaxData dynamax;
struct TeraData tera;
struct BattleGimmickData gimmick;
const u8 *trainerSlideMsg;
bool8 trainerSlideLowHpMsgDone;
u8 introState;
@ -832,6 +793,8 @@ struct BattleStruct
u8 shellSideArmCategory[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT];
u8 boosterEnergyActivates;
u8 distortedTypeMatchups;
u8 categoryOverride; // for Z-Moves and Max Moves
u32 stellarBoostFlags[NUM_BATTLE_SIDES]; // stored as a bitfield of flags for all types for each side
};
// The palaceFlags member of struct BattleStruct contains 1 flag per move to indicate which moves the AI should consider,

View File

@ -98,10 +98,7 @@ enum {
// Special return values in gBattleBufferB from Battle Controller functions.
#define RET_VALUE_LEVELED_UP 11
#define RET_MEGA_EVOLUTION (1 << 7)
#define RET_ULTRA_BURST (1 << 6)
#define RET_DYNAMAX (1 << 5)
#define RET_TERASTAL (1 << 4)
#define RET_GIMMICK (1 << 7)
struct UnusedControllerStruct
{
@ -131,8 +128,6 @@ struct ChooseMoveStruct
u8 monType1;
u8 monType2;
u8 monType3;
struct MegaEvolutionData mega;
struct UltraBurstData burst;
struct ZMoveData zmove;
};

View File

@ -56,21 +56,19 @@ enum MaxMoveEffect
MAX_EFFECT_BYPASS_PROTECT,
};
bool32 IsDynamaxed(u16 battlerId);
bool32 CanDynamax(u16 battlerId);
bool32 IsGigantamaxed(u16 battlerId);
bool32 CanDynamax(u32 battler);
bool32 IsGigantamaxed(u32 battler);
void ApplyDynamaxHPMultiplier(u32 battler, struct Pokemon* mon);
void PrepareBattlerForDynamax(u16 battlerId);
u16 GetNonDynamaxHP(u16 battlerId);
u16 GetNonDynamaxMaxHP(u32 battlerId);
void UndoDynamax(u16 battlerId);
bool32 IsMoveBlockedByMaxGuard(u16 move);
bool32 IsMoveBlockedByDynamax(u16 move);
void ActivateDynamax(u32 battler);
u16 GetNonDynamaxHP(u32 battler);
u16 GetNonDynamaxMaxHP(u32 battler);
void UndoDynamax(u32 battler);
bool32 IsMoveBlockedByMaxGuard(u32 move);
bool32 IsMoveBlockedByDynamax(u32 move);
bool32 ShouldUseMaxMove(u16 battlerId, u16 baseMove);
u16 GetMaxMove(u16 battlerId, u16 baseMove);
u8 GetMaxMovePower(u16 move);
bool32 IsMaxMove(u16 move);
u16 GetMaxMove(u32 battler, u32 baseMove);
u8 GetMaxMovePower(u32 move);
bool32 IsMaxMove(u32 move);
void ChooseDamageNonTypesString(u8 type);
void BS_UpdateDynamax(void);
@ -83,10 +81,4 @@ void BS_HealOneSixth(void);
void BS_TryRecycleBerry(void);
void BS_JumpIfDynamaxed(void);
void ChangeDynamaxTriggerSprite(u8 spriteId, u8 animId);
void CreateDynamaxTriggerSprite(u8, bool8);
void HideDynamaxTriggerSprite(void);
bool32 IsDynamaxTriggerSpriteActive(void);
void DestroyDynamaxTriggerSprite(void);
#endif

51
include/battle_gimmick.h Normal file
View File

@ -0,0 +1,51 @@
#ifndef GUARD_BATTLE_GIMMICK_H
#define GUARD_BATTLE_GIMMICK_H
enum Gimmick
{
GIMMICK_NONE,
GIMMICK_MEGA,
GIMMICK_ULTRA_BURST,
GIMMICK_Z_MOVE,
GIMMICK_DYNAMAX,
GIMMICK_TERA,
GIMMICKS_COUNT,
};
struct GimmickInfo
{
const struct SpritePalette *triggerPal; // trigger gfx data
const struct SpriteSheet *triggerSheet;
const struct SpriteTemplate *triggerTemplate;
const struct SpritePalette *indicatorPal; // indicator gfx data
const struct SpriteSheet *indicatorSheet;
bool32 (*CanActivate)(u32 battler);
void (*ActivateGimmick)(u32 battler);
};
void AssignUsableGimmicks(void);
bool32 CanActivateGimmick(u32 battler, enum Gimmick gimmick);
bool32 IsGimmickSelected(u32 battler, enum Gimmick gimmick);
void SetActiveGimmick(u32 battler, enum Gimmick gimmick);
enum Gimmick GetActiveGimmick(u32 battler);
bool32 ShouldTrainerBattlerUseGimmick(u32 battler, enum Gimmick gimmick);
bool32 HasTrainerUsedGimmick(u32 battler, enum Gimmick gimmick);
void SetGimmickAsActivated(u32 battler, enum Gimmick gimmick);
void ChangeGimmickTriggerSprite(u32 spriteId, u32 animId);
void CreateGimmickTriggerSprite(u32 battler);
bool32 IsGimmickTriggerSpriteActive(void);
void HideGimmickTriggerSprite(void);
void DestroyGimmickTriggerSprite(void);
void LoadIndicatorSpritesGfx(void);
u32 GetIndicatorTileTag(u32 battler);
u32 GetIndicatorPalTag(u32 battler);
void UpdateIndicatorVisibilityAndType(u32 healthboxId, bool32 invisible);
void UpdateIndicatorOamPriority(u32 healthboxId, u32 oamPriority);
void UpdateIndicatorLevelData(u32 healthboxId, u32 level);
void CreateIndicatorSprite(u32 battler);
extern const struct GimmickInfo gGimmicksInfo[];
#endif

View File

@ -48,45 +48,38 @@ enum
#define TAG_HEALTHBAR_PAL TAG_HEALTHBAR_PLAYER1_TILE
#define TAG_HEALTHBOX_PAL TAG_HEALTHBOX_PLAYER1_TILE
#define TAG_MEGA_TRIGGER_TILE 0xD777
#define TAG_GIMMICK_TRIGGER_TILE 0xD777
#define TAG_MEGA_INDICATOR_TILE 0xD778
#define TAG_ALPHA_INDICATOR_TILE 0xD779
#define TAG_OMEGA_INDICATOR_TILE 0xD77A
#define TAG_ZMOVE_TRIGGER_TILE 0xD77B
#define TAG_BURST_TRIGGER_TILE 0xD77C
#define TAG_DYNAMAX_TRIGGER_TILE 0xD77D
#define TAG_DYNAMAX_INDICATOR_TILE 0xD77E
#define TAG_DYNAMAX_INDICATOR_TILE 0xD77B
#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
#define TAG_NORMAL_INDICATOR_TILE 0xD77C
#define TAG_FIGHTING_INDICATOR_TILE 0xD77D
#define TAG_FLYING_INDICATOR_TILE 0xD77E
#define TAG_POISON_INDICATOR_TILE 0xD77F
#define TAG_GROUND_INDICATOR_TILE 0xD780
#define TAG_ROCK_INDICATOR_TILE 0xD781
#define TAG_BUG_INDICATOR_TILE 0xD782
#define TAG_GHOST_INDICATOR_TILE 0xD783
#define TAG_STEEL_INDICATOR_TILE 0xD784
// 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_FIRE_INDICATOR_TILE 0xD786
#define TAG_WATER_INDICATOR_TILE 0xD787
#define TAG_GRASS_INDICATOR_TILE 0xD788
#define TAG_ELECTRIC_INDICATOR_TILE 0xD789
#define TAG_PSYCHIC_INDICATOR_TILE 0xD78A
#define TAG_ICE_INDICATOR_TILE 0xD78B
#define TAG_DRAGON_INDICATOR_TILE 0xD78C
#define TAG_DARK_INDICATOR_TILE 0xD78D
#define TAG_FAIRY_INDICATOR_TILE 0xD78E
#define TAG_STELLAR_INDICATOR_TILE 0xD78F
#define TAG_TERA_TRIGGER_TILE 0xD790
#define TAG_MEGA_TRIGGER_PAL 0xD777
#define TAG_GIMMICK_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
#define TAG_TERA_INDICATOR_PAL 0xD77A
enum
{
@ -116,18 +109,6 @@ void InitBattlerHealthboxCoords(u8 battler);
void GetBattlerHealthboxCoords(u8 battler, s16 *x, s16 *y);
void UpdateHpTextInHealthbox(u32 healthboxSpriteId, u32 maxOrCurrent, s16 currHp, s16 maxHp);
void SwapHpBarsWithHpText(void);
void ChangeMegaTriggerSprite(u8 spriteId, u8 animId);
void CreateMegaTriggerSprite(u8 battlerId, u8 palId);
bool32 IsMegaTriggerSpriteActive(void);
void HideMegaTriggerSprite(void);
void DestroyMegaTriggerSprite(void);
void ChangeBurstTriggerSprite(u8 spriteId, u8 animId);
void CreateBurstTriggerSprite(u8 battlerId, u8 palId);
bool32 IsBurstTriggerSpriteActive(void);
void HideBurstTriggerSprite(void);
void DestroyBurstTriggerSprite(void);
void MegaIndicator_LoadSpritesGfx(void);
void MegaIndicator_SetVisibilities(u32 healthboxId, bool32 invisible);
u8 CreatePartyStatusSummarySprites(u8 battler, struct HpAndStatus *partyInfo, bool8 skipPlayer, bool8 isBattleStart);
void Task_HidePartyStatusSummary(u8 taskId);
void UpdateHealthboxAttribute(u8 healthboxSpriteId, struct Pokemon *mon, u8 elementId);
@ -136,7 +117,6 @@ u8 GetScaledHPFraction(s16 hp, s16 maxhp, u8 scale);
u8 GetHPBarLevel(s16 hp, s16 maxhp);
void CreateAbilityPopUp(u8 battlerId, u32 ability, bool32 isDoubleBattle);
void DestroyAbilityPopUp(u8 battlerId);
void HideTriggerSprites(void);
bool32 CanThrowLastUsedBall(void);
void TryHideLastUsedBall(void);
void TryRestoreLastUsedBall(void);

View File

@ -47,7 +47,7 @@ bool32 IsShieldsDownProtected(u32 battler);
u32 IsAbilityStatusProtected(u32 battler);
bool32 TryResetBattlerStatChanges(u8 battler);
bool32 CanCamouflage(u8 battlerId);
u16 GetNaturePowerMove(void);
u32 GetNaturePowerMove(u32 battler);
void StealTargetItem(u8 battlerStealer, u8 battlerItem);
u8 GetCatchingBattler(void);
u32 GetHighestStatId(u32 battlerId);

View File

@ -84,6 +84,7 @@ extern const u8 BattleScript_DmgHazardsOnBattlerScripting[];
extern const u8 BattleScript_DmgHazardsOnFaintedBattler[];
extern const u8 BattleScript_PerishSongTakesLife[];
extern const u8 BattleScript_PerishSongCountGoesDown[];
extern const u8 BattleScript_AllStatsUpZMove[];
extern const u8 BattleScript_AllStatsUp[];
extern const u8 BattleScript_RapidSpinAway[];
extern const u8 BattleScript_WrapFree[];
@ -838,5 +839,6 @@ extern const u8 BattleScript_EffectShedTail[];
extern const u8 BattleScript_EffectUpperHand[];
extern const u8 BattleScript_EffectTidyUp[];
extern const u8 BattleScript_EffectSpicyExtract[];
extern const u8 BattleScript_DamageToQuarterTargetHP[];
#endif // GUARD_BATTLE_SCRIPTS_H

View File

@ -1,31 +1,14 @@
#ifndef GUARD_BATTLE_TERASTAL_H
#define GUARD_BATTLE_TERASTAL_H
void PrepareBattlerForTera(u32 battler);
void ActivateTera(u32 battler);
void ApplyBattlerVisualsForTeraAnim(u32 battler);
bool32 CanTerastallize(u32 battler);
u32 GetBattlerTeraType(u32 battler);
bool32 IsTerastallized(u32 battler);
void ExpendTypeStellarBoost(u32 battler, u32 type);
bool32 IsTypeStellarBoosted(u32 battler, u32 type);
uq4_12_t GetTeraMultiplier(u32 battler, u32 type);
u16 GetTeraTypeRGB(u32 type);
void ChangeTeraTriggerSprite(u8 spriteId, u8 animId);
void CreateTeraTriggerSprite(u8 battler, u8 palId);
bool32 IsTeraTriggerSpriteActive(void);
void HideTeraTriggerSprite(void);
void DestroyTeraTriggerSprite(void);
void TeraIndicator_LoadSpriteGfx(void);
bool32 TeraIndicator_ShouldBeInvisible(u32 battler);
u8 TeraIndicator_GetSpriteId(u32 healthboxSpriteId);
void TeraIndicator_SetVisibilities(u32 healthboxId, bool32 invisible);
void TeraIndicator_UpdateOamPriorities(u32 healthboxId, u32 oamPriority);
void TeraIndicator_UpdateLevel(u32 healthboxId, u32 level);
void TeraIndicator_CreateSprite(u32 battler, u32 healthboxSpriteId);
void TeraIndicator_DestroySprite(u32 healthboxSpriteId);
void TeraIndicator_UpdateType(u32 battler, u32 healthboxSpriteId);
#endif

View File

@ -196,6 +196,8 @@ s32 GetStealthHazardDamage(u8 hazardType, u32 battler);
s32 GetStealthHazardDamageByTypesAndHP(u8 hazardType, u8 type1, u8 type2, u32 maxHp);
bool32 CanMegaEvolve(u32 battler);
bool32 CanUltraBurst(u32 battler);
void ActivateMegaEvolution(u32 battler);
void ActivateUltraBurst(u32 battler);
bool32 IsBattlerMegaEvolved(u32 battler);
bool32 IsBattlerPrimalReverted(u32 battler);
bool32 IsBattlerUltraBursted(u32 battler);

View File

@ -13,19 +13,17 @@ struct SignatureZMove
u16 zmove;
};
bool8 IsZMove(u16 move);
void QueueZMove(u8 battler, u16 baseMove);
bool32 IsViableZMove(u8 battler, u16 move);
bool32 TryChangeZIndicator(u8 battler, u8 moveIndex);
void CreateZMoveTriggerSprite(u8, bool8);
void HideZMoveTriggerSprite(void);
bool32 IsZMoveTriggerSpriteActive(void);
void DestroyZMoveTriggerSprite(void);
u16 GetTypeBasedZMove(u16 move, u8 battler);
bool32 IsZMove(u32 move);
bool32 CanUseZMove(u32 battler);
u32 GetUsableZMove(u32 battler, u32 move);
void ActivateZMove(u32 battler);
bool32 IsViableZMove(u32 battler, u32 move);
bool32 TryChangeZTrigger(u32 battler, u32 moveIndex);
u32 GetTypeBasedZMove(u32 move);
u32 GetSignatureZMove(u32 move, u32 species, u32 item);
bool32 MoveSelectionDisplayZMove(u16 zmove, u32 battler);
void SetZEffect(void);
bool32 IsZMoveUsable(u8 battler, u16 moveIndex);
void GetUsableZMoves(u8 battler, u16 *moves);
u16 GetZMovePower(u16 move);
void AssignUsableZMoves(u32 battler, u16 *moves);
u32 GetZMovePower(u32 move);
#endif // GUARD_BATTLE_Z_MOVE_H

View File

@ -352,6 +352,7 @@ enum {
EFFECT_TERA_BLAST,
EFFECT_TERA_STARSTORM,
EFFECT_DRAGON_DARTS,
EFFECT_GUARDIAN_OF_ALOLA,
NUM_BATTLE_MOVE_EFFECTS,
};

View File

@ -70,11 +70,11 @@ struct TrainerMon
u8 nature:5;
bool8 gender:2;
bool8 isShiny:1;
u8 useGimmick:4;
u8 dynamaxLevel:4;
u8 teraType:5;
bool8 gigantamaxFactor:1;
bool8 shouldDynamax:1;
bool8 shouldTerastal:1;
u8 padding:2;
};
#define TRAINER_PARTY(partyArray) partyArray, .partySize = ARRAY_COUNT(partyArray)

View File

@ -326,14 +326,14 @@
* The inference process is naive, if your test contains anything that
* modifies the speed of a battler you should specify them explicitly.
*
* MOVE(battler, move | moveSlot:, [megaEvolve:], [hit:], [criticalHit:], [target:], [allowed:], [WITH_RNG(tag, value])
* MOVE(battler, move | moveSlot:, [gimmick:], [hit:], [criticalHit:], [target:], [allowed:], [WITH_RNG(tag, value])
* Used when the battler chooses Fight. Either the move ID or move slot
* must be specified. megaEvolve: TRUE causes the battler to Mega Evolve
* if able, hit: FALSE causes the move to miss, criticalHit: TRUE causes
* the move to land a critical hit, target: is used in double battles to
* choose the target (when necessary), and allowed: FALSE is used to
* reject an illegal move e.g. a Disabled one. WITH_RNG allows the move
* to specify an explicit outcome for an RNG tag.
* must be specified. gimmick: GIMMICK_MEGA causes the battler to Mega
* Evolve if able, hit: FALSE causes the move to miss, criticalHit: TRUE
* causes the move to land a critical hit, target: is used in double
* battles to choose the target (when necessary), and allowed: FALSE is
* used to reject an illegal move e.g. a Disabled one. WITH_RNG allows
* the move to specify an explicit outcome for an RNG tag.
* MOVE(playerLeft, MOVE_TACKLE, target: opponentRight);
* If the battler does not have an explicit Moves specified the moveset
* will be populated based on the MOVEs it uses.
@ -662,6 +662,7 @@ struct BattleTestData
u8 gender;
u8 nature;
u16 forcedAbilities[NUM_BATTLE_SIDES][PARTY_SIZE];
u8 chosenGimmick[NUM_BATTLE_SIDES][PARTY_SIZE];
u8 currentMonIndexes[MAX_BATTLERS_COUNT];
u8 turnState;
@ -941,15 +942,8 @@ struct MoveContext
u16 explicitCriticalHit:1;
u16 secondaryEffect:1;
u16 explicitSecondaryEffect:1;
u16 megaEvolve:1;
u16 explicitMegaEvolve:1;
u16 ultraBurst:1;
u16 explicitUltraBurst:1;
// TODO: u8 zMove:1;
u16 dynamax:1;
u16 explicitDynamax:1;
u16 tera:1;
u16 explicitTera:1;
u16 gimmick:4;
u16 explicitGimmick:1;
u16 allowed:1;
u16 explicitAllowed:1;
u16 notExpected:1; // Has effect only with EXPECT_MOVE

View File

@ -24,6 +24,7 @@ void TestRunner_Battle_InvalidNoHPMon(u32 battlerId, u32 partyIndex);
void TestRunner_Battle_CheckBattleRecordActionType(u32 battlerId, u32 recordIndex, u32 actionType);
u32 TestRunner_Battle_GetForcedAbility(u32 side, u32 partyIndex);
u32 TestRunner_Battle_GetChosenGimmick(u32 side, u32 partyIndex);
#else
@ -45,6 +46,8 @@ u32 TestRunner_Battle_GetForcedAbility(u32 side, u32 partyIndex);
#define TestRunner_Battle_GetForcedAbility(...) (u32)0
#define TestRunner_Battle_GetChosenGimmick(...) (u32)0
#endif
#endif

View File

@ -399,23 +399,6 @@ 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;
@ -495,7 +478,6 @@ void SetAiLogicDataForTurn(struct AiLogicData *aiData)
continue;
SetBattlerAiData(battlerAtk, aiData);
SetBattlerAiGimmickData(battlerAtk, aiData);
SetBattlerAiMovesData(aiData, battlerAtk, battlersCount);
}
}
@ -1458,7 +1440,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(-10);
else if (aiData->abilities[battlerDef] == ABILITY_SUCTION_CUPS)
ADJUST_SCORE(-10);
else if (IsDynamaxed(battlerDef))
else if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
ADJUST_SCORE(-10);
break;
case EFFECT_TOXIC_THREAD:
@ -1491,7 +1473,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
return 0;
if (!ShouldTryOHKO(battlerAtk, battlerDef, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], move))
ADJUST_SCORE(-10);
else if (IsDynamaxed(battlerDef))
else if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
ADJUST_SCORE(-10);
break;
case EFFECT_MIST:
@ -1530,7 +1512,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(-3);
break;
case EFFECT_DISABLE:
if (IsDynamaxed(battlerDef))
if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
ADJUST_SCORE(-10);
else if (gDisableStructs[battlerDef].disableTimer == 0
&& (B_MENTAL_HERB < GEN_5 || aiData->holdEffects[battlerDef] != HOLD_EFFECT_MENTAL_HERB)
@ -1552,7 +1534,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
}
break;
case EFFECT_ENCORE:
if (IsDynamaxed(battlerDef))
if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
ADJUST_SCORE(-10);
else if (gDisableStructs[battlerDef].encoreTimer == 0
&& (B_MENTAL_HERB < GEN_5 || aiData->holdEffects[battlerDef] != HOLD_EFFECT_MENTAL_HERB)
@ -1768,7 +1750,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
}
break;
case EFFECT_TORMENT:
if (IsDynamaxed(battlerDef))
if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
ADJUST_SCORE(-10);
else if (gBattleMons[battlerDef].status2 & STATUS2_TORMENT
|| DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove))
@ -1974,7 +1956,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
case EFFECT_DESTINY_BOND:
if (gBattleMons[battlerDef].status2 & STATUS2_DESTINY_BOND)
ADJUST_SCORE(-10);
else if (IsDynamaxed(battlerDef))
else if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
ADJUST_SCORE(-10);
break;
case EFFECT_HEAL_BELL:
@ -2128,7 +2110,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(-10);
break;
case EFFECT_NATURE_POWER:
return AI_CheckBadMove(battlerAtk, battlerDef, GetNaturePowerMove(), score);
return AI_CheckBadMove(battlerAtk, battlerDef, GetNaturePowerMove(battlerAtk), score);
case EFFECT_TAUNT:
if (gDisableStructs[battlerDef].tauntTimer > 0
|| DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove))
@ -2172,7 +2154,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|| gAbilitiesInfo[aiData->abilities[battlerDef]].cantBeSwapped
|| aiData->holdEffects[battlerDef] == HOLD_EFFECT_ABILITY_SHIELD)
ADJUST_SCORE(-10);
else if (IsDynamaxed(battlerDef))
else if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
ADJUST_SCORE(-10);
break;
case EFFECT_WORRY_SEED:
@ -2192,7 +2174,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|| gAbilitiesInfo[aiData->abilities[battlerDef]].cantBeOverwritten
|| aiData->holdEffects[battlerAtk] == HOLD_EFFECT_ABILITY_SHIELD)
ADJUST_SCORE(-10);
else if (IsDynamaxed(battlerDef))
else if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
ADJUST_SCORE(-10);
break;
case EFFECT_SIMPLE_BEAM:
@ -2448,11 +2430,11 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
switch (move)
{
case MOVE_TRICK_OR_TREAT:
if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GHOST) || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove) || IsTerastallized(battlerDef))
if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GHOST) || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove) || GetActiveGimmick(battlerDef) == GIMMICK_TERA)
ADJUST_SCORE(-10);
break;
case MOVE_FORESTS_CURSE:
if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS) || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove) || IsTerastallized(battlerDef))
if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS) || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove) || GetActiveGimmick(battlerDef) == GIMMICK_TERA)
ADJUST_SCORE(-10);
break;
}
@ -2520,7 +2502,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
{
ADJUST_SCORE(-10);
}
else if (IsDynamaxed(battlerDef))
else if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
ADJUST_SCORE(-10);
else if (isDoubleBattle)
{
@ -3013,8 +2995,8 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
break;
case EFFECT_SOAK:
if (atkPartnerAbility == ABILITY_WONDER_GUARD
&& IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_WATER)
&& !IsTerastallized(battlerAtkPartner))
&& !IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_WATER)
&& GetActiveGimmick(battlerAtkPartner) != GIMMICK_TERA)
{
RETURN_SCORE_PLUS(WEAK_EFFECT);
}
@ -3236,7 +3218,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
u32 i;
// The AI should understand that while Dynamaxed, status moves function like Protect.
if (IsDynamaxed(battlerAtk) && gMovesInfo[move].category == DAMAGE_CATEGORY_STATUS)
if (GetActiveGimmick(battlerAtk) == GIMMICK_DYNAMAX && gMovesInfo[move].category == DAMAGE_CATEGORY_STATUS)
moveEffect = EFFECT_PROTECT;
// check status move preference
@ -3431,7 +3413,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
if ((gMovesInfo[move].soundMove && aiData->abilities[battlerDef] == ABILITY_SOUNDPROOF)
|| aiData->abilities[battlerDef] == ABILITY_SUCTION_CUPS)
break;
else if (IsDynamaxed(battlerDef))
else if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
break;
score += AI_TryToClearStats(battlerAtk, battlerDef, isDoubleBattle);
break;
@ -3516,7 +3498,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
}
break;
case EFFECT_OHKO:
if (IsDynamaxed(battlerDef))
if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
break;
else if (gStatuses3[battlerAtk] & STATUS3_ALWAYS_HITS)
ADJUST_SCORE(BEST_EFFECT);
@ -3613,7 +3595,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(BEST_EFFECT);
break;
case EFFECT_DISABLE:
if (IsDynamaxed(battlerDef))
if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
break;
else if (gDisableStructs[battlerDef].disableTimer == 0
&& (gLastMoves[battlerDef] != MOVE_NONE)
@ -3626,7 +3608,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
}
break;
case EFFECT_ENCORE:
if (IsDynamaxed(battlerDef))
if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
break;
else if (gDisableStructs[battlerDef].encoreTimer == 0
&& (B_MENTAL_HERB < GEN_5 || aiData->holdEffects[battlerDef] != HOLD_EFFECT_MENTAL_HERB)
@ -3645,7 +3627,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(GOOD_EFFECT);
break;
case EFFECT_DESTINY_BOND:
if (IsDynamaxed(battlerDef))
if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
break;
else if (AI_IsFaster(battlerAtk, battlerDef, move) && CanTargetFaintAi(battlerDef, battlerAtk))
ADJUST_SCORE(GOOD_EFFECT);
@ -4012,7 +3994,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
}
break;
case HOLD_EFFECT_EJECT_BUTTON:
//if (!IsRaidBattle() && IsDynamaxed(battlerDef) && gNewBS->dynamaxData.timer[battlerDef] > 1 &&
//if (!IsRaidBattle() && GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX && gNewBS->dynamaxData.timer[battlerDef] > 1 &&
if (HasDamagingMove(battlerAtk)
|| (isDoubleBattle && IsBattlerAlive(BATTLE_PARTNER(battlerAtk)) && HasDamagingMove(BATTLE_PARTNER(battlerAtk))))
ADJUST_SCORE(DECENT_EFFECT); // Force 'em out next turn
@ -4094,7 +4076,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(DECENT_EFFECT);
break;
case EFFECT_SKILL_SWAP:
if (IsDynamaxed(battlerDef))
if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
break;
else if (gAbilitiesInfo[aiData->abilities[battlerDef]].aiRating > gAbilitiesInfo[aiData->abilities[battlerAtk]].aiRating)
ADJUST_SCORE(DECENT_EFFECT);
@ -4106,7 +4088,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(DECENT_EFFECT);
break;
case EFFECT_ENTRAINMENT:
if (IsDynamaxed(battlerDef))
if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
break;
else if ((IsAbilityOfRating(aiData->abilities[battlerDef], 5) || gAbilitiesInfo[aiData->abilities[battlerAtk]].aiRating <= 0)
&& (aiData->abilities[battlerDef] != aiData->abilities[battlerAtk] && !(gStatuses3[battlerDef] & STATUS3_GASTRO_ACID)))

View File

@ -468,7 +468,7 @@ bool32 IsDamageMoveUnusable(u32 move, u32 battlerAtk, u32 battlerDef)
break;
case EFFECT_LOW_KICK:
case EFFECT_HEAT_CRASH:
if (IsDynamaxed(battlerDef))
if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
return TRUE;
break;
case EFFECT_FAIL_IF_NOT_ARG_TYPE:
@ -508,35 +508,31 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
s32 moveType;
uq4_12_t effectivenessMultiplier;
bool32 isDamageMoveUnusable = FALSE;
bool32 toggledDynamax = FALSE;
bool32 toggledTera = FALSE;
bool32 toggledGimmick = FALSE;
struct AiLogicData *aiData = AI_DATA;
gBattleStruct->aiCalcInProgress = TRUE;
// Temporarily enable Z-Moves for damage calcs
if (considerZPower && IsViableZMove(battlerAtk, move))
SetBattlerData(battlerAtk);
SetBattlerData(battlerDef);
// Temporarily enable gimmicks for damage calcs if planned
if (gBattleStruct->gimmick.usableGimmick[battlerAtk] && GetActiveGimmick(battlerAtk) == GIMMICK_NONE
&& !(gBattleStruct->gimmick.usableGimmick[battlerAtk] == GIMMICK_Z_MOVE && !considerZPower))
{
gBattleStruct->zmove.baseMoves[battlerAtk] = move;
gBattleStruct->zmove.active = TRUE;
// Set Z-Move variables if needed
if (gBattleStruct->gimmick.usableGimmick[battlerAtk] == GIMMICK_Z_MOVE && IsViableZMove(battlerAtk, move))
gBattleStruct->zmove.baseMoves[battlerAtk] = move;
toggledGimmick = TRUE;
SetActiveGimmick(battlerAtk, gBattleStruct->gimmick.usableGimmick[battlerAtk]);
}
else if (gMovesInfo[move].effect == EFFECT_PHOTON_GEYSER)
if (gMovesInfo[move].effect == EFFECT_PHOTON_GEYSER)
gBattleStruct->swapDamageCategory = (GetCategoryBasedOnStats(gBattlerAttacker) == DAMAGE_CATEGORY_PHYSICAL);
else if (move == MOVE_SHELL_SIDE_ARM && gBattleStruct->shellSideArmCategory[battlerAtk][battlerDef] == DAMAGE_CATEGORY_PHYSICAL)
gBattleStruct->swapDamageCategory = TRUE;
else 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]];
}
move = GetNaturePowerMove(battlerAtk);
gBattleStruct->dynamicMoveType = 0;
@ -608,7 +604,7 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
simDamage.minimum = LowestRollDmg(nonCritDmg);
}
if (!gBattleStruct->zmove.active)
if (GetActiveGimmick(battlerAtk) != GIMMICK_Z_MOVE)
{
// Handle dynamic move damage
switch (gMovesInfo[move].effect)
@ -697,14 +693,12 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
// convert multiper to AI_EFFECTIVENESS_xX
*typeEffectiveness = AI_GetEffectiveness(effectivenessMultiplier);
// Undo temporary settings
gBattleStruct->aiCalcInProgress = FALSE;
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]]);
if (toggledGimmick)
SetActiveGimmick(battlerAtk, GIMMICK_NONE);
return simDamage;
}
@ -3812,24 +3806,28 @@ bool32 ShouldUseZMove(u32 battlerAtk, u32 battlerDef, u32 chosenMove)
{
// simple logic. just upgrades chosen move to z move if possible, unless regular move would kill opponent
if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && battlerDef == BATTLE_PARTNER(battlerAtk))
return FALSE; //don't use z move on partner
if (gBattleStruct->zmove.used[battlerAtk])
return FALSE; //cant use z move twice
return FALSE; // don't use z move on partner
if (HasTrainerUsedGimmick(battlerAtk, GIMMICK_Z_MOVE))
return FALSE; // can't use z move twice
if (IsViableZMove(battlerAtk, chosenMove))
{
u8 effectiveness;
u32 zMove = GetUsableZMove(battlerAtk, chosenMove);
struct SimulatedDamage dmg;
if (gBattleMons[battlerDef].ability == ABILITY_DISGUISE
&& !gMovesInfo[zMove].ignoresTargetAbility
&& (gBattleMons[battlerDef].species == SPECIES_MIMIKYU_DISGUISED || gBattleMons[battlerDef].species == SPECIES_MIMIKYU_TOTEM_DISGUISED))
return FALSE; // Don't waste a Z-Move busting disguise
if (gBattleMons[battlerDef].ability == ABILITY_ICE_FACE && gBattleMons[battlerDef].species == SPECIES_EISCUE_ICE_FACE && IS_MOVE_PHYSICAL(chosenMove))
if (gBattleMons[battlerDef].ability == ABILITY_ICE_FACE
&& !gMovesInfo[zMove].ignoresTargetAbility
&& gBattleMons[battlerDef].species == SPECIES_EISCUE_ICE_FACE && IS_MOVE_PHYSICAL(chosenMove))
return FALSE; // Don't waste a Z-Move busting Ice Face
if (IS_MOVE_STATUS(chosenMove) && !IS_MOVE_STATUS(gBattleStruct->zmove.chosenZMove))
if (IS_MOVE_STATUS(chosenMove) && !IS_MOVE_STATUS(zMove))
return FALSE;
else if (!IS_MOVE_STATUS(chosenMove) && IS_MOVE_STATUS(gBattleStruct->zmove.chosenZMove))
else if (!IS_MOVE_STATUS(chosenMove) && IS_MOVE_STATUS(zMove))
return FALSE;
dmg = AI_CalcDamageSaveBattlers(chosenMove, battlerAtk, battlerDef, &effectiveness, FALSE, DMG_ROLL_DEFAULT);

View File

@ -6557,8 +6557,8 @@ static void ReloadBattlerSprites(u32 battler, struct Pokemon *party)
BattleLoadMonSpriteGfx(mon, battler);
CreateBattlerSprite(battler);
UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], mon, HEALTHBOX_ALL);
// If battler is mega evolved / primal reversed, hide the sprite until the move animation finishes.
MegaIndicator_SetVisibilities(gHealthboxSpriteIds[battler], TRUE);
// If battler has an indicator for a gimmick, hide the sprite until the move animation finishes.
UpdateIndicatorVisibilityAndType(gHealthboxSpriteIds[battler], TRUE);
// Try to recreate shadow sprite
if (gBattleSpritesDataPtr->healthBoxesData[battler].shadowSpriteId < MAX_SPRITES)

View File

@ -552,22 +552,17 @@ static void OpponentHandleChooseMove(u32 battler)
if (gAbsentBattlerFlags & gBitTable[gBattlerTarget])
gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT);
}
if (ShouldUseZMove(battler, gBattlerTarget, chosenMove))
QueueZMove(battler, chosenMove);
// If opponent can Mega Evolve, do it.
if (CanMegaEvolve(battler))
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_MEGA_EVOLUTION) | (gBattlerTarget << 8));
// If opponent can Ultra Burst, do it.
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) && 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));
// If opponent can and should use a gimmick (considering trainer data), do it
if (gBattleStruct->gimmick.usableGimmick[battler] != GIMMICK_NONE
&& !(gBattleStruct->gimmick.usableGimmick[battler] == GIMMICK_Z_MOVE
&& !ShouldUseZMove(battler, gBattlerTarget, moveInfo->moves[chosenMoveId])))
{
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_GIMMICK) | (gBattlerTarget << 8));
}
else
{
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (gBattlerTarget << 8));
}
}
break;
}

View File

@ -9,6 +9,7 @@
#include "battle_setup.h"
#include "battle_tv.h"
#include "battle_z_move.h"
#include "battle_gimmick.h"
#include "bg.h"
#include "data.h"
#include "item.h"
@ -448,19 +449,13 @@ static void HandleInputChooseTarget(u32 battler)
{
PlaySE(SE_SELECT);
gSprites[gBattlerSpriteIds[gMultiUsePlayerCursor]].callback = SpriteCB_HideAsMoveTarget;
if (gBattleStruct->mega.playerSelect)
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_MEGA_EVOLUTION | (gMultiUsePlayerCursor << 8));
else if (gBattleStruct->burst.playerSelect)
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));
if (gBattleStruct->gimmick.playerSelect)
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_GIMMICK | (gMultiUsePlayerCursor << 8));
else
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | (gMultiUsePlayerCursor << 8));
EndBounceEffect(gMultiUsePlayerCursor, BOUNCE_HEALTHBOX);
TryHideLastUsedBall();
HideTriggerSprites();
HideGimmickTriggerSprite();
PlayerBufferExecCompleted(battler);
}
else if (JOY_NEW(B_BUTTON) || gPlayerDpadHoldFrames > 59)
@ -612,17 +607,11 @@ static void HandleInputShowEntireFieldTargets(u32 battler)
{
PlaySE(SE_SELECT);
HideAllTargets();
if (gBattleStruct->mega.playerSelect)
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_MEGA_EVOLUTION | (gMultiUsePlayerCursor << 8));
else if (gBattleStruct->burst.playerSelect)
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));
if (gBattleStruct->gimmick.playerSelect)
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_GIMMICK | (gMultiUsePlayerCursor << 8));
else
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | (gMultiUsePlayerCursor << 8));
HideTriggerSprites();
HideGimmickTriggerSprite();
PlayerBufferExecCompleted(battler);
}
else if (JOY_NEW(B_BUTTON) || gPlayerDpadHoldFrames > 59)
@ -646,17 +635,11 @@ static void HandleInputShowTargets(u32 battler)
{
PlaySE(SE_SELECT);
HideShownTargets(battler);
if (gBattleStruct->mega.playerSelect)
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_MEGA_EVOLUTION | (gMultiUsePlayerCursor << 8));
else if (gBattleStruct->burst.playerSelect)
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));
if (gBattleStruct->gimmick.playerSelect)
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_GIMMICK | (gMultiUsePlayerCursor << 8));
else
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | (gMultiUsePlayerCursor << 8));
HideTriggerSprites();
HideGimmickTriggerSprite();
TryHideLastUsedBall();
PlayerBufferExecCompleted(battler);
}
@ -698,16 +681,13 @@ static void HandleInputChooseMove(u32 battler)
if (gBattleStruct->zmove.viewing)
{
u16 chosenMove = moveInfo->moves[gMoveSelectionCursor[battler]];
QueueZMove(battler, chosenMove);
gBattleStruct->zmove.viewing = FALSE;
if (gMovesInfo[moveInfo->moves[gMoveSelectionCursor[battler]]].category != DAMAGE_CATEGORY_STATUS)
moveTarget = MOVE_TARGET_SELECTED; //damaging z moves always have selected target
}
// Status moves turn into Max Guard when Dynamaxed, targets user.
if ((IsDynamaxed(battler) || gBattleStruct->dynamax.playerSelect))
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX || IsGimmickSelected(battler, GIMMICK_DYNAMAX))
moveTarget = gMovesInfo[GetMaxMove(battler, moveInfo->moves[gMoveSelectionCursor[battler]])].target;
if (moveTarget & MOVE_TARGET_USER)
@ -763,17 +743,11 @@ static void HandleInputChooseMove(u32 battler)
{
case 0:
default:
if (gBattleStruct->mega.playerSelect)
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_MEGA_EVOLUTION | (gMultiUsePlayerCursor << 8));
else if (gBattleStruct->burst.playerSelect)
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));
if (gBattleStruct->gimmick.playerSelect)
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_GIMMICK | (gMultiUsePlayerCursor << 8));
else
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | (gMultiUsePlayerCursor << 8));
HideTriggerSprites();
HideGimmickTriggerSprite();
TryHideLastUsedBall();
PlayerBufferExecCompleted(battler);
break;
@ -800,19 +774,15 @@ static void HandleInputChooseMove(u32 battler)
else if ((JOY_NEW(B_BUTTON) || gPlayerDpadHoldFrames > 59) && !gBattleStruct->descriptionSubmenu)
{
PlaySE(SE_SELECT);
gBattleStruct->gimmick.playerSelect = FALSE;
if (gBattleStruct->zmove.viewing)
{
ReloadMoveNames(battler);
}
else
{
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();
HideGimmickTriggerSprite();
PlayerBufferExecCompleted(battler);
}
}
@ -828,7 +798,7 @@ static void HandleInputChooseMove(u32 battler)
MoveSelectionDisplayMoveType(battler);
if (gBattleStruct->descriptionSubmenu)
MoveSelectionDisplayMoveDescription(battler);
TryChangeZIndicator(battler, gMoveSelectionCursor[battler]);
TryChangeZTrigger(battler, gMoveSelectionCursor[battler]);
}
}
else if (JOY_NEW(DPAD_RIGHT) && !gBattleStruct->zmove.viewing)
@ -844,7 +814,7 @@ static void HandleInputChooseMove(u32 battler)
MoveSelectionDisplayMoveType(battler);
if (gBattleStruct->descriptionSubmenu)
MoveSelectionDisplayMoveDescription(battler);
TryChangeZIndicator(battler, gMoveSelectionCursor[battler]);
TryChangeZTrigger(battler, gMoveSelectionCursor[battler]);
}
}
else if (JOY_NEW(DPAD_UP) && !gBattleStruct->zmove.viewing)
@ -859,7 +829,7 @@ static void HandleInputChooseMove(u32 battler)
MoveSelectionDisplayMoveType(battler);
if (gBattleStruct->descriptionSubmenu)
MoveSelectionDisplayMoveDescription(battler);
TryChangeZIndicator(battler, gMoveSelectionCursor[battler]);
TryChangeZTrigger(battler, gMoveSelectionCursor[battler]);
}
}
else if (JOY_NEW(DPAD_DOWN) && !gBattleStruct->zmove.viewing)
@ -875,7 +845,7 @@ static void HandleInputChooseMove(u32 battler)
MoveSelectionDisplayMoveType(battler);
if (gBattleStruct->descriptionSubmenu)
MoveSelectionDisplayMoveDescription(battler);
TryChangeZIndicator(battler, gMoveSelectionCursor[battler]);
TryChangeZTrigger(battler, gMoveSelectionCursor[battler]);
}
}
else if (JOY_NEW(SELECT_BUTTON) && !gBattleStruct->zmove.viewing && !gBattleStruct->descriptionSubmenu)
@ -920,41 +890,11 @@ static void HandleInputChooseMove(u32 battler)
}
else if (JOY_NEW(START_BUTTON))
{
if (CanMegaEvolve(battler))
if (gBattleStruct->gimmick.usableGimmick[battler] != GIMMICK_NONE)
{
gBattleStruct->mega.playerSelect ^= 1;
ChangeMegaTriggerSprite(gBattleStruct->mega.triggerSpriteId, gBattleStruct->mega.playerSelect);
PlaySE(SE_SELECT);
}
else if (CanUltraBurst(battler))
{
gBattleStruct->burst.playerSelect ^= 1;
ChangeBurstTriggerSprite(gBattleStruct->burst.triggerSpriteId, gBattleStruct->burst.playerSelect);
PlaySE(SE_SELECT);
}
else if (gBattleStruct->zmove.viable)
{
// show z move name / info
//TODO: brighten z move symbol
PlaySE(SE_SELECT);
if (!gBattleStruct->zmove.viewing)
MoveSelectionDisplayZMove(gBattleStruct->zmove.chosenZMove, battler);
else
ReloadMoveNames(battler);
}
else if (CanDynamax(battler))
{
gBattleStruct->dynamax.playerSelect ^= 1;
MoveSelectionDisplayMoveNames(battler);
MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0);
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);
MoveSelectionDisplayMoveType(battler); // For Tera Blast / Tera Starstorm
gBattleStruct->gimmick.playerSelect ^= 1;
ReloadMoveNames(battler);
ChangeGimmickTriggerSprite(gBattleStruct->gimmick.triggerSpriteId, gBattleStruct->gimmick.playerSelect);
PlaySE(SE_SELECT);
}
}
@ -962,16 +902,20 @@ static void HandleInputChooseMove(u32 battler)
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);
MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0);
MoveSelectionDisplayPpNumber(battler);
MoveSelectionDisplayMoveType(battler);
if (gBattleStruct->zmove.viable && !gBattleStruct->zmove.viewing)
{
struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]);
MoveSelectionDisplayZMove(GetUsableZMove(battler, moveInfo->moves[gMoveSelectionCursor[battler]]), battler);
}
else
{
gBattleStruct->zmove.viewing = FALSE;
MoveSelectionDestroyCursorAt(battler);
MoveSelectionDisplayMoveNames(battler);
MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0);
MoveSelectionDisplayPpNumber(battler);
MoveSelectionDisplayMoveType(battler);
}
}
static u32 UNUSED HandleMoveInputUnused(u32 battler)
@ -1124,7 +1068,7 @@ static void HandleMoveSwitching(u32 battler)
MoveSelectionDisplayPpString(battler);
MoveSelectionDisplayPpNumber(battler);
MoveSelectionDisplayMoveType(battler);
GetUsableZMoves(battler, moveInfo->moves);
AssignUsableZMoves(battler, moveInfo->moves);
}
else if (JOY_NEW(B_BUTTON | SELECT_BUTTON))
{
@ -1495,7 +1439,7 @@ static void Task_GiveExpToMon(u8 taskId)
CalculateMonStats(mon);
// Reapply Dynamax HP multiplier after stats are recalculated.
if (IsDynamaxed(battler) && monId == gBattlerPartyIndexes[battler])
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX && monId == gBattlerPartyIndexes[battler])
{
ApplyDynamaxHPMultiplier(battler, mon);
gBattleMons[battler].hp = gBattleStruct->dynamax.levelUpHP;
@ -1579,7 +1523,7 @@ static void Task_GiveExpWithExpBar(u8 taskId)
CalculateMonStats(&gPlayerParty[monId]);
// Reapply Dynamax HP multiplier after stats are recalculated.
if (IsDynamaxed(battler) && monId == gBattlerPartyIndexes[battler])
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX && monId == gBattlerPartyIndexes[battler])
{
ApplyDynamaxHPMultiplier(battler, &gPlayerParty[monId]);
gBattleMons[battler].hp = gBattleStruct->dynamax.levelUpHP;
@ -1732,8 +1676,7 @@ static void MoveSelectionDisplayMoveNames(u32 battler)
for (i = 0; i < MAX_MON_MOVES; i++)
{
MoveSelectionDestroyCursorAt(i);
if ((gBattleStruct->dynamax.playerSelect && CanDynamax(battler))
|| IsDynamaxed(battler))
if (IsGimmickSelected(battler, GIMMICK_DYNAMAX) || GetActiveGimmick(battler) == GIMMICK_DYNAMAX)
StringCopy(gDisplayedStringBattle, GetMoveName(GetMaxMove(battler, moveInfo->moves[i])));
else
StringCopy(gDisplayedStringBattle, GetMoveName(moveInfo->moves[i]));
@ -1780,7 +1723,7 @@ static void MoveSelectionDisplayMoveType(u32 battler)
if (moveInfo->moves[gMoveSelectionCursor[battler]] == MOVE_TERA_BLAST)
{
if (gBattleStruct->tera.playerSelect || IsTerastallized(battler))
if (IsGimmickSelected(battler, GIMMICK_TERA) || GetActiveGimmick(battler) == GIMMICK_TERA)
type = GetBattlerTeraType(battler);
}
else if (moveInfo->moves[gMoveSelectionCursor[battler]] == MOVE_IVY_CUDGEL)
@ -1792,10 +1735,16 @@ static void MoveSelectionDisplayMoveType(u32 battler)
|| speciesId == SPECIES_OGERPON_CORNERSTONE_MASK || speciesId == SPECIES_OGERPON_CORNERSTONE_MASK_TERA)
type = gBattleMons[battler].type2;
}
// Max Guard is a Normal-type move
else if (gMovesInfo[moveInfo->moves[gMoveSelectionCursor[battler]]].category == DAMAGE_CATEGORY_STATUS
&& (GetActiveGimmick(battler) == GIMMICK_DYNAMAX || IsGimmickSelected(battler, GIMMICK_DYNAMAX)))
{
type = TYPE_NORMAL;
}
else if (moveInfo->moves[gMoveSelectionCursor[battler]] == MOVE_TERA_STARSTORM)
{
if (gBattleMons[battler].species == SPECIES_TERAPAGOS_STELLAR
|| (gBattleStruct->tera.playerSelect && gBattleMons[battler].species == SPECIES_TERAPAGOS_TERASTAL))
|| (IsGimmickSelected(battler, GIMMICK_TERA) && gBattleMons[battler].species == SPECIES_TERAPAGOS_TERASTAL))
type = TYPE_STELLAR;
}
@ -2149,32 +2098,16 @@ static void PlayerHandleChooseMove(u32 battler)
struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]);
InitMoveSelectionsVarsAndStrings(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))
CreateMegaTriggerSprite(battler, 0);
if (!IsBurstTriggerSpriteActive())
gBattleStruct->burst.triggerSpriteId = 0xFF;
if (CanUltraBurst(battler))
CreateBurstTriggerSprite(battler, 0);
if (!IsDynamaxTriggerSpriteActive())
gBattleStruct->dynamax.triggerSpriteId = 0xFF;
if (CanDynamax(battler))
CreateDynamaxTriggerSprite(battler, 0);
if (!IsZMoveTriggerSpriteActive())
gBattleStruct->zmove.triggerSpriteId = 0xFF;
if (!IsTeraTriggerSpriteActive())
gBattleStruct->tera.triggerSpriteId = 0xFF;
if (CanTerastallize(battler))
CreateTeraTriggerSprite(battler, 0);
gBattleStruct->gimmick.playerSelect = FALSE;
AssignUsableZMoves(battler, moveInfo->moves);
gBattleStruct->zmove.viable = (gBattleStruct->zmove.possibleZMoves[battler] & gBitTable[gMoveSelectionCursor[battler]]) != 0;
if (!IsGimmickTriggerSpriteActive())
gBattleStruct->gimmick.triggerSpriteId = 0xFF;
if (!(gBattleStruct->gimmick.usableGimmick[battler] == GIMMICK_Z_MOVE && !gBattleStruct->zmove.viable))
CreateGimmickTriggerSprite(battler);
GetUsableZMoves(battler, moveInfo->moves);
gBattleStruct->zmove.viable = IsZMoveUsable(battler, gMoveSelectionCursor[battler]);
CreateZMoveTriggerSprite(battler, gBattleStruct->zmove.viable);
gBattlerControllerFuncs[battler] = HandleChooseMoveAfterDma3;
}
}

View File

@ -368,17 +368,17 @@ static void PlayerPartnerHandleChooseMove(u32 battler)
if (gAbsentBattlerFlags & gBitTable[gBattlerTarget])
gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
}
if (ShouldUseZMove(battler, gBattlerTarget, moveInfo->moves[chosenMoveId]))
QueueZMove(battler, moveInfo->moves[chosenMoveId]);
// If partner can mega evolve, do it.
if (CanMegaEvolve(battler))
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_MEGA_EVOLUTION) | (gBattlerTarget << 8));
else if (CanUltraBurst(battler))
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_ULTRA_BURST) | (gBattlerTarget << 8));
// If partner can and should use a gimmick (considering trainer data), do it
if (gBattleStruct->gimmick.usableGimmick[battler] != GIMMICK_NONE
&& !(gBattleStruct->gimmick.usableGimmick[battler] == GIMMICK_Z_MOVE
&& !ShouldUseZMove(battler, gBattlerTarget, moveInfo->moves[chosenMoveId])))
{
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_GIMMICK) | (gBattlerTarget << 8));
}
else
{
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (gBattlerTarget << 8));
}
}
PlayerPartnerBufferExecCompleted(battler);

View File

@ -5,6 +5,7 @@
#include "battle_interface.h"
#include "battle_scripts.h"
#include "battle_script_commands.h"
#include "battle_gimmick.h"
#include "data.h"
#include "event_data.h"
#include "graphics.h"
@ -22,7 +23,7 @@
#include "constants/items.h"
#include "constants/moves.h"
static u8 GetMaxPowerTier(u16 move);
static u8 GetMaxPowerTier(u32 move);
struct GMaxMove
{
@ -69,35 +70,21 @@ static const struct GMaxMove sGMaxMoveTable[] =
{SPECIES_URSHIFU_RAPID_STRIKE_STYLE_GIGANTAMAX, TYPE_WATER, MOVE_G_MAX_RAPID_FLOW},
};
// forward declarations
static void SpriteCb_DynamaxTrigger(struct Sprite *);
// Returns whether a battler is Dynamaxed.
bool32 IsDynamaxed(u16 battlerId)
{
if (gBattleStruct->dynamax.dynamaxed[battlerId]
/*|| IsRaidBoss(battlerId)*/)
return TRUE;
return FALSE;
}
// Returns whether a battler can Dynamax.
bool32 CanDynamax(u16 battlerId)
bool32 CanDynamax(u32 battler)
{
u16 species = gBattleMons[battlerId].species;
u16 holdEffect = ItemId_GetHoldEffect(gBattleMons[battlerId].item);
// Check if Dynamax battle flag is set. This needs to be defined in include/config/battle.h
#if B_FLAG_DYNAMAX_BATTLE != 0
if (!FlagGet(B_FLAG_DYNAMAX_BATTLE))
#endif
return FALSE;
u16 species = gBattleMons[battler].species;
u16 holdEffect = GetBattlerHoldEffect(battler, FALSE);
// Check if Player has a Dynamax Band.
if ((GetBattlerPosition(battlerId) == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && GetBattlerPosition(battlerId) == B_POSITION_PLAYER_RIGHT))
&& !CheckBagHasItem(ITEM_DYNAMAX_BAND, 1))
return FALSE;
if (!TESTING && (GetBattlerPosition(battler) == B_POSITION_PLAYER_LEFT
|| (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && GetBattlerPosition(battler) == B_POSITION_PLAYER_RIGHT)))
{
if (!CheckBagHasItem(ITEM_DYNAMAX_BAND, 1))
return FALSE;
if (B_FLAG_DYNAMAX_BATTLE == 0 || (B_FLAG_DYNAMAX_BATTLE != 0 && !FlagGet(B_FLAG_DYNAMAX_BATTLE)))
return FALSE;
}
// Check if species isn't allowed to Dynamax.
if (GET_BASE_SPECIES_ID(species) == SPECIES_ZACIAN
@ -105,18 +92,24 @@ bool32 CanDynamax(u16 battlerId)
|| GET_BASE_SPECIES_ID(species) == SPECIES_ETERNATUS)
return FALSE;
// Cannot Dynamax if you can Mega Evolve or use a Z-Move
if (holdEffect == HOLD_EFFECT_MEGA_STONE || holdEffect == HOLD_EFFECT_Z_CRYSTAL)
// Check if Trainer has already Dynamaxed.
if (HasTrainerUsedGimmick(battler, GIMMICK_DYNAMAX))
return FALSE;
// Cannot Dynamax if your side has already or will Dynamax.
if (gBattleStruct->dynamax.alreadyDynamaxed[GetBattlerSide(battlerId)]
|| gBattleStruct->dynamax.dynamaxed[BATTLE_PARTNER(battlerId)]
|| gBattleStruct->dynamax.toDynamax & gBitTable[BATTLE_PARTNER(battlerId)])
// Check if AI battler is intended to Dynamaxed.
if (!ShouldTrainerBattlerUseGimmick(battler, GIMMICK_DYNAMAX))
return FALSE;
// Check if battler has another gimmick active.
if (GetActiveGimmick(battler) != GIMMICK_NONE)
return FALSE;
// Check if battler is holding a Z-Crystal or Mega Stone.
if (!TESTING && (holdEffect == HOLD_EFFECT_Z_CRYSTAL || holdEffect == HOLD_EFFECT_MEGA_STONE)) // tests make this check already
return FALSE;
// TODO: Cannot Dynamax in a Max Raid if you don't have Dynamax Energy.
// if (gBattleTypeFlags & BATTLE_TYPE_RAID && gBattleStruct->raid.dynamaxEnergy != battlerId)
// if (gBattleTypeFlags & BATTLE_TYPE_RAID && gBattleStruct->raid.dynamaxEnergy != battler)
// return FALSE;
// No checks failed, all set!
@ -124,10 +117,10 @@ bool32 CanDynamax(u16 battlerId)
}
// Returns whether a battler is transformed into a Gigantamax form.
bool32 IsGigantamaxed(u16 battlerId)
bool32 IsGigantamaxed(u32 battler)
{
struct Pokemon *mon = &GetSideParty(GetBattlerSide(battlerId))[gBattlerPartyIndexes[battlerId]];
if ((gSpeciesInfo[gBattleMons[battlerId].species].isGigantamax) && GetMonData(mon, MON_DATA_GIGANTAMAX_FACTOR))
struct Pokemon *mon = &GetSideParty(GetBattlerSide(battler))[gBattlerPartyIndexes[battler]];
if ((gSpeciesInfo[gBattleMons[battler].species].isGigantamax) && GetMonData(mon, MON_DATA_GIGANTAMAX_FACTOR))
return TRUE;
return FALSE;
}
@ -148,79 +141,80 @@ void ApplyDynamaxHPMultiplier(u32 battler, struct Pokemon* mon)
}
// Returns the non-Dynamax HP of a Pokemon.
u16 GetNonDynamaxHP(u16 battlerId)
u16 GetNonDynamaxHP(u32 battler)
{
if (!IsDynamaxed(battlerId) || gBattleMons[battlerId].species == SPECIES_SHEDINJA)
return gBattleMons[battlerId].hp;
if (GetActiveGimmick(battler) != GIMMICK_DYNAMAX || gBattleMons[battler].species == SPECIES_SHEDINJA)
return gBattleMons[battler].hp;
else
{
u16 mult = UQ_4_12(1.0/1.5); // placeholder
u16 hp = UQ_4_12_TO_INT((gBattleMons[battlerId].hp * mult) + UQ_4_12_ROUND);
u16 hp = UQ_4_12_TO_INT((gBattleMons[battler].hp * mult) + UQ_4_12_ROUND);
return hp;
}
}
// Returns the non-Dynamax Max HP of a Pokemon.
u16 GetNonDynamaxMaxHP(u32 battlerId)
u16 GetNonDynamaxMaxHP(u32 battler)
{
if (!IsDynamaxed(battlerId) || gBattleMons[battlerId].species == SPECIES_SHEDINJA)
return gBattleMons[battlerId].maxHP;
if (GetActiveGimmick(battler) != GIMMICK_DYNAMAX || gBattleMons[battler].species == SPECIES_SHEDINJA)
return gBattleMons[battler].maxHP;
else
{
u16 mult = UQ_4_12(1.0/1.5); // placeholder
u16 maxHP = UQ_4_12_TO_INT((gBattleMons[battlerId].maxHP * mult) + UQ_4_12_ROUND);
u16 maxHP = UQ_4_12_TO_INT((gBattleMons[battler].maxHP * mult) + UQ_4_12_ROUND);
return maxHP;
}
}
// Sets flags used for Dynamaxing and checks Gigantamax forms.
void PrepareBattlerForDynamax(u16 battlerId)
void ActivateDynamax(u32 battler)
{
u8 side = GetBattlerSide(battlerId);
gBattleStruct->dynamax.alreadyDynamaxed[side] = TRUE;
gBattleStruct->dynamax.dynamaxed[battlerId] = TRUE;
gBattleStruct->dynamax.dynamaxTurns[battlerId] = DYNAMAX_TURNS_COUNT;
// Set appropriate use flags.
SetActiveGimmick(battler, GIMMICK_DYNAMAX);
SetGimmickAsActivated(battler, GIMMICK_DYNAMAX);
gBattleStruct->dynamax.dynamaxTurns[battler] = DYNAMAX_TURNS_COUNT;
// Substitute is removed upon Dynamaxing.
gBattleMons[battlerId].status2 &= ~STATUS2_SUBSTITUTE;
ClearBehindSubstituteBit(battlerId);
gBattleMons[battler].status2 &= ~STATUS2_SUBSTITUTE;
ClearBehindSubstituteBit(battler);
// Choiced Moves are reset upon Dynamaxing.
gBattleStruct->choicedMove[battlerId] = MOVE_NONE;
gBattleStruct->choicedMove[battler] = MOVE_NONE;
// Try Gigantamax form change.
if (!(gBattleMons[battlerId].status2 & STATUS2_TRANSFORMED)) // Ditto cannot Gigantamax.
TryBattleFormChange(battlerId, FORM_CHANGE_BATTLE_GIGANTAMAX);
if (!(gBattleMons[battler].status2 & STATUS2_TRANSFORMED)) // Ditto cannot Gigantamax.
TryBattleFormChange(battler, FORM_CHANGE_BATTLE_GIGANTAMAX);
BattleScriptExecute(BattleScript_DynamaxBegins);
}
// Unsets the flags used for Dynamaxing and reverts max HP if needed.
void UndoDynamax(u16 battlerId)
void UndoDynamax(u32 battler)
{
u8 side = GetBattlerSide(battlerId);
u8 monId = gBattlerPartyIndexes[battlerId];
u8 side = GetBattlerSide(battler);
u8 monId = gBattlerPartyIndexes[battler];
// Revert HP if battler is still Dynamaxed.
if (IsDynamaxed(battlerId))
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX)
{
struct Pokemon *mon = (side == B_SIDE_PLAYER) ? &gPlayerParty[monId] : &gEnemyParty[monId];
u16 mult = UQ_4_12(1.0/1.5); // placeholder
gBattleMons[battlerId].hp = UQ_4_12_TO_INT((GetMonData(mon, MON_DATA_HP) * mult + 1) + UQ_4_12_ROUND); // round up
SetMonData(mon, MON_DATA_HP, &gBattleMons[battlerId].hp);
gBattleMons[battler].hp = UQ_4_12_TO_INT((GetMonData(mon, MON_DATA_HP) * mult + 1) + UQ_4_12_ROUND); // round up
SetMonData(mon, MON_DATA_HP, &gBattleMons[battler].hp);
CalculateMonStats(mon);
}
// Makes sure there are no Dynamax flags set, including on switch / faint.
gBattleStruct->dynamax.dynamaxed[battlerId] = FALSE;
gBattleStruct->dynamax.dynamaxTurns[battlerId] = 0;
SetActiveGimmick(battler, GIMMICK_NONE);
gBattleStruct->dynamax.dynamaxTurns[battler] = 0;
// Undo form change if needed.
if (IsGigantamaxed(battlerId))
TryBattleFormChange(battlerId, FORM_CHANGE_END_BATTLE);
if (IsGigantamaxed(battler))
TryBattleFormChange(battler, FORM_CHANGE_END_BATTLE);
}
// Certain moves are blocked by Max Guard that normally ignore protection.
bool32 IsMoveBlockedByMaxGuard(u16 move)
bool32 IsMoveBlockedByMaxGuard(u32 move)
{
switch (move)
{
@ -239,7 +233,7 @@ bool32 IsMoveBlockedByMaxGuard(u16 move)
}
// Weight-based moves (and some other moves in Raids) are blocked by Dynamax.
bool32 IsMoveBlockedByDynamax(u16 move)
bool32 IsMoveBlockedByDynamax(u32 move)
{
// TODO: Certain moves are banned in raids.
switch (gMovesInfo[move].effect)
@ -251,24 +245,15 @@ bool32 IsMoveBlockedByDynamax(u16 move)
return FALSE;
}
// Returns whether a move should be converted into a Max Move.
bool32 ShouldUseMaxMove(u16 battlerId, u16 baseMove)
{
// TODO: Raid bosses do not always use Max Moves.
// if (IsRaidBoss(battlerId))
// return !IsRaidBossUsingRegularMove(battlerId, baseMove);
return IsDynamaxed(battlerId) || gBattleStruct->dynamax.toDynamax & gBitTable[battlerId];
}
static u16 GetTypeBasedMaxMove(u16 battlerId, u16 type)
static u16 GetTypeBasedMaxMove(u32 battler, u32 type)
{
// Gigantamax check
u32 i;
u16 species = gBattleMons[battlerId].species;
u16 targetSpecies = SPECIES_NONE;
u32 species = gBattleMons[battler].species;
u32 targetSpecies = SPECIES_NONE;
if (!gSpeciesInfo[species].isGigantamax)
targetSpecies = GetBattleFormChangeTargetSpecies(battlerId, FORM_CHANGE_BATTLE_GIGANTAMAX);
targetSpecies = GetBattleFormChangeTargetSpecies(battler, FORM_CHANGE_BATTLE_GIGANTAMAX);
if (targetSpecies != SPECIES_NONE)
species = targetSpecies;
@ -289,9 +274,9 @@ static u16 GetTypeBasedMaxMove(u16 battlerId, u16 type)
}
// Returns the appropriate Max Move or G-Max Move for a battler to use.
u16 GetMaxMove(u16 battlerId, u16 baseMove)
u16 GetMaxMove(u32 battler, u32 baseMove)
{
u16 move = baseMove;
u32 move = baseMove;
if (baseMove == MOVE_NONE) // for move display
{
return MOVE_NONE;
@ -306,13 +291,11 @@ u16 GetMaxMove(u16 battlerId, u16 baseMove)
}
else if (gBattleStruct->dynamicMoveType)
{
move = GetTypeBasedMaxMove(battlerId, gBattleStruct->dynamicMoveType & DYNAMIC_TYPE_MASK);
gBattleStruct->dynamax.categories[battlerId] = gMovesInfo[baseMove].category;
move = GetTypeBasedMaxMove(battler, gBattleStruct->dynamicMoveType & DYNAMIC_TYPE_MASK);
}
else
{
move = GetTypeBasedMaxMove(battlerId, gMovesInfo[baseMove].type);
gBattleStruct->dynamax.categories[battlerId] = gMovesInfo[baseMove].category;
move = GetTypeBasedMaxMove(battler, gMovesInfo[baseMove].type);
}
return move;
@ -332,7 +315,7 @@ enum
};
// Gets the base power of a Max Move.
u8 GetMaxMovePower(u16 move)
u8 GetMaxMovePower(u32 move)
{
u8 tier;
// G-Max Drum Solo, G-Max Hydrosnipe, and G-Max Fireball always have 160 base power.
@ -383,7 +366,7 @@ u8 GetMaxMovePower(u16 move)
}
}
static u8 GetMaxPowerTier(u16 move)
static u8 GetMaxPowerTier(u32 move)
{
if (gMovesInfo[move].strikeCount >= 2 && gMovesInfo[move].strikeCount <= 5)
{
@ -459,7 +442,7 @@ static u8 GetMaxPowerTier(u16 move)
}
// Returns whether a move is a Max Move or not.
bool32 IsMaxMove(u16 move)
bool32 IsMaxMove(u32 move)
{
return move >= FIRST_MAX_MOVE && move <= LAST_MAX_MOVE;
}
@ -485,7 +468,7 @@ void ChooseDamageNonTypesString(u8 type)
}
// Returns the status effect that should be applied by a G-Max Move.
static u32 GetMaxMoveStatusEffect(u16 move)
static u32 GetMaxMoveStatusEffect(u32 move)
{
u8 maxEffect = gMovesInfo[move].argument;
switch (maxEffect)
@ -524,7 +507,7 @@ static u32 GetMaxMoveStatusEffect(u16 move)
void BS_UpdateDynamax(void)
{
NATIVE_ARGS();
u16 battler = gBattleScripting.battler;
u32 battler = gBattleScripting.battler;
struct Pokemon *mon = &GetSideParty(GetBattlerSide(battler))[gBattlerPartyIndexes[battler]];
if (!IsGigantamaxed(battler)) // RecalcBattlerStats will get called on form change.
@ -1049,201 +1032,8 @@ void BS_TryRecycleBerry(void)
void BS_JumpIfDynamaxed(void)
{
NATIVE_ARGS(const u8 *jumpInstr);
if (IsDynamaxed(gBattlerTarget))
if ((GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX))
gBattlescriptCurrInstr = cmd->jumpInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;
}
// DYNAMAX TRIGGER:
static const u8 ALIGNED(4) sDynamaxTriggerGfx[] = INCBIN_U8("graphics/battle_interface/dynamax_trigger.4bpp");
static const u16 sDynamaxTriggerPal[] = INCBIN_U16("graphics/battle_interface/dynamax_trigger.gbapal");
static const struct SpriteSheet sSpriteSheet_DynamaxTrigger =
{
sDynamaxTriggerGfx, sizeof(sDynamaxTriggerGfx), TAG_DYNAMAX_TRIGGER_TILE
};
static const struct SpritePalette sSpritePalette_DynamaxTrigger =
{
sDynamaxTriggerPal, TAG_DYNAMAX_TRIGGER_PAL
};
static const struct OamData sOamData_DynamaxTrigger =
{
.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_DynamaxTriggerOff[] =
{
ANIMCMD_FRAME(0, 0),
ANIMCMD_END
};
static const union AnimCmd sSpriteAnim_DynamaxTriggerOn[] =
{
ANIMCMD_FRAME(16, 0),
ANIMCMD_END
};
static const union AnimCmd *const sSpriteAnimTable_DynamaxTrigger[] =
{
sSpriteAnim_DynamaxTriggerOff,
sSpriteAnim_DynamaxTriggerOn,
};
static const struct SpriteTemplate sSpriteTemplate_DynamaxTrigger =
{
.tileTag = TAG_DYNAMAX_TRIGGER_TILE,
.paletteTag = TAG_DYNAMAX_TRIGGER_PAL,
.oam = &sOamData_DynamaxTrigger,
.anims = sSpriteAnimTable_DynamaxTrigger,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_DynamaxTrigger
};
// Dynamax Evolution Trigger icon functions.
void ChangeDynamaxTriggerSprite(u8 spriteId, u8 animId)
{
StartSpriteAnim(&gSprites[spriteId], animId);
}
#define SINGLES_DYNAMAX_TRIGGER_POS_X_OPTIMAL (30)
#define SINGLES_DYNAMAX_TRIGGER_POS_X_PRIORITY (31)
#define SINGLES_DYNAMAX_TRIGGER_POS_X_SLIDE (15)
#define SINGLES_DYNAMAX_TRIGGER_POS_Y_DIFF (-11)
#define DOUBLES_DYNAMAX_TRIGGER_POS_X_OPTIMAL (30)
#define DOUBLES_DYNAMAX_TRIGGER_POS_X_PRIORITY (31)
#define DOUBLES_DYNAMAX_TRIGGER_POS_X_SLIDE (15)
#define DOUBLES_DYNAMAX_TRIGGER_POS_Y_DIFF (-4)
#define tBattler data[0]
#define tHide data[1]
void CreateDynamaxTriggerSprite(u8 battlerId, u8 palId)
{
LoadSpritePalette(&sSpritePalette_DynamaxTrigger);
if (GetSpriteTileStartByTag(TAG_DYNAMAX_TRIGGER_TILE) == 0xFFFF)
LoadSpriteSheet(&sSpriteSheet_DynamaxTrigger);
if (gBattleStruct->dynamax.triggerSpriteId == 0xFF)
{
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
gBattleStruct->dynamax.triggerSpriteId = CreateSprite(&sSpriteTemplate_DynamaxTrigger,
gSprites[gHealthboxSpriteIds[battlerId]].x - DOUBLES_DYNAMAX_TRIGGER_POS_X_SLIDE,
gSprites[gHealthboxSpriteIds[battlerId]].y - DOUBLES_DYNAMAX_TRIGGER_POS_Y_DIFF, 0);
else
gBattleStruct->dynamax.triggerSpriteId = CreateSprite(&sSpriteTemplate_DynamaxTrigger,
gSprites[gHealthboxSpriteIds[battlerId]].x - SINGLES_DYNAMAX_TRIGGER_POS_X_SLIDE,
gSprites[gHealthboxSpriteIds[battlerId]].y - SINGLES_DYNAMAX_TRIGGER_POS_Y_DIFF, 0);
}
gSprites[gBattleStruct->dynamax.triggerSpriteId].tBattler = battlerId;
gSprites[gBattleStruct->dynamax.triggerSpriteId].tHide = FALSE;
ChangeDynamaxTriggerSprite(gBattleStruct->dynamax.triggerSpriteId, palId);
}
static void SpriteCb_DynamaxTrigger(struct Sprite *sprite)
{
s32 xSlide, xPriority, xOptimal;
s32 yDiff;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
{
xSlide = DOUBLES_DYNAMAX_TRIGGER_POS_X_SLIDE;
xPriority = DOUBLES_DYNAMAX_TRIGGER_POS_X_PRIORITY;
xOptimal = DOUBLES_DYNAMAX_TRIGGER_POS_X_OPTIMAL;
yDiff = DOUBLES_DYNAMAX_TRIGGER_POS_Y_DIFF;
}
else
{
xSlide = SINGLES_DYNAMAX_TRIGGER_POS_X_SLIDE;
xPriority = SINGLES_DYNAMAX_TRIGGER_POS_X_PRIORITY;
xOptimal = SINGLES_DYNAMAX_TRIGGER_POS_X_OPTIMAL;
yDiff = SINGLES_DYNAMAX_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)
DestroyDynamaxTriggerSprite();
}
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 IsDynamaxTriggerSpriteActive(void)
{
if (GetSpriteTileStartByTag(TAG_DYNAMAX_TRIGGER_TILE) == 0xFFFF)
return FALSE;
else if (IndexOfSpritePaletteTag(TAG_DYNAMAX_TRIGGER_PAL) != 0xFF)
return TRUE;
else
return FALSE;
}
void HideDynamaxTriggerSprite(void)
{
if (gBattleStruct->dynamax.triggerSpriteId >= MAX_SPRITES)
return;
ChangeDynamaxTriggerSprite(gBattleStruct->dynamax.triggerSpriteId, 0);
gSprites[gBattleStruct->dynamax.triggerSpriteId].tHide = TRUE;
}
void DestroyDynamaxTriggerSprite(void)
{
FreeSpritePaletteByTag(TAG_DYNAMAX_TRIGGER_PAL);
FreeSpriteTilesByTag(TAG_DYNAMAX_TRIGGER_TILE);
if (gBattleStruct->dynamax.triggerSpriteId != 0xFF)
DestroySprite(&gSprites[gBattleStruct->dynamax.triggerSpriteId]);
gBattleStruct->dynamax.triggerSpriteId = 0xFF;
}
#undef tBattler
#undef tHide
// data fields for healthboxMain
// oam.affineParam holds healthboxRight spriteId
#define hMain_DynamaxIndicatorId 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]

View File

@ -626,7 +626,7 @@ void BattleLoadMonSpriteGfx(struct Pokemon *mon, u32 battler)
}
// dynamax tint
if (IsDynamaxed(battler))
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX)
{
// Calyrex and its forms have a blue dynamax aura instead of red.
if (GET_BASE_SPECIES_ID(species) == SPECIES_CALYREX)
@ -637,7 +637,7 @@ void BattleLoadMonSpriteGfx(struct Pokemon *mon, u32 battler)
}
// Terastallization's tint
if (IsTerastallized(battler))
if (GetActiveGimmick(battler) == GIMMICK_TERA)
{
BlendPalette(paletteOffset, 16, 8, GetTeraTypeRGB(GetBattlerTeraType(battler)));
CpuCopy32(gPlttBufferFaded + paletteOffset, gPlttBufferUnfaded + paletteOffset, PLTT_SIZEOF(16));
@ -706,8 +706,7 @@ bool8 BattleLoadAllHealthBoxesGfx(u8 state)
{
LoadSpritePalette(&sSpritePalettes_HealthBoxHealthBar[0]);
LoadSpritePalette(&sSpritePalettes_HealthBoxHealthBar[1]);
MegaIndicator_LoadSpritesGfx();
TeraIndicator_LoadSpriteGfx();
LoadIndicatorSpritesGfx();
CategoryIcons_LoadSpritesGfx();
}
else if (!IsDoubleBattle())

392
src/battle_gimmick.c Normal file
View File

@ -0,0 +1,392 @@
#include "global.h"
#include "battle.h"
#include "battle_anim.h"
#include "battle_controllers.h"
#include "battle_interface.h"
#include "battle_gimmick.h"
#include "battle_z_move.h"
#include "battle_setup.h"
#include "battle_util.h"
#include "item.h"
#include "palette.h"
#include "pokemon.h"
#include "sprite.h"
#include "util.h"
#include "test_runner.h"
#include "data/gimmicks.h"
// Populates gBattleStruct->gimmick.usableGimmick for each battler.
void AssignUsableGimmicks(void)
{
u32 battler, gimmick;
for (battler = 0; battler < gBattlersCount; ++battler)
{
gBattleStruct->gimmick.usableGimmick[battler] = GIMMICK_NONE;
for (gimmick = 0; gimmick < GIMMICKS_COUNT; ++gimmick)
{
if (CanActivateGimmick(battler, gimmick))
{
gBattleStruct->gimmick.usableGimmick[battler] = gimmick;
break;
}
}
}
}
// Returns whether a battler is able to use a gimmick. Checks consumption and gimmick specific functions.
bool32 CanActivateGimmick(u32 battler, enum Gimmick gimmick)
{
return gGimmicksInfo[gimmick].CanActivate != NULL && gGimmicksInfo[gimmick].CanActivate(battler);
}
// Returns whether the player has a gimmick selected while in the move selection menu.
bool32 IsGimmickSelected(u32 battler, enum Gimmick gimmick)
{
// There's no player select in tests, but some gimmicks need to test choice before they are fully activated.
if (TESTING)
return (gBattleStruct->gimmick.toActivate & gBitTable[battler]) && gBattleStruct->gimmick.usableGimmick[battler] == gimmick;
else
return gBattleStruct->gimmick.usableGimmick[battler] == gimmick && gBattleStruct->gimmick.playerSelect;
}
// Sets a battler as having a gimmick active using their party index.
void SetActiveGimmick(u32 battler, enum Gimmick gimmick)
{
gBattleStruct->gimmick.activeGimmick[GetBattlerSide(battler)][gBattlerPartyIndexes[battler]] = gimmick;
}
// Returns a battler's active gimmick, if any.
enum Gimmick GetActiveGimmick(u32 battler)
{
return gBattleStruct->gimmick.activeGimmick[GetBattlerSide(battler)][gBattlerPartyIndexes[battler]];
}
// Returns whether a trainer mon is intended to use an unrestrictive gimmick via .useGimmick (i.e Tera).
bool32 ShouldTrainerBattlerUseGimmick(u32 battler, enum Gimmick gimmick)
{
// There are no trainer party settings in battles, but the AI needs to know which gimmick to use.
if (TESTING)
{
return gimmick == TestRunner_Battle_GetChosenGimmick(GetBattlerSide(battler), gBattlerPartyIndexes[battler]);
}
// The player can bypass these checks because they can choose through the controller.
else if (GetBattlerSide(battler) == B_SIDE_PLAYER
&& !((gBattleTypeFlags & BATTLE_TYPE_MULTI) && battler == B_POSITION_PLAYER_RIGHT))
{
return TRUE;
}
// Check the trainer party data to see if a gimmick is intended.
else
{
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 *mon = &GetTrainerPartyFromId(trainerId)[isSecondTrainer ? gBattlerPartyIndexes[battler] - MULTI_PARTY_SIZE : gBattlerPartyIndexes[battler]];
return mon->useGimmick == gimmick;
}
}
// Returns whether a trainer has used a gimmick during a battle.
bool32 HasTrainerUsedGimmick(u32 battler, enum Gimmick gimmick)
{
// Check whether partner battler has used gimmick or plans to during turn.
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE
&& IsPartnerMonFromSameTrainer(battler)
&& (gBattleStruct->gimmick.activated[BATTLE_PARTNER(battler)][gimmick]
|| ((gBattleStruct->gimmick.toActivate & gBitTable[BATTLE_PARTNER(battler)]
&& gBattleStruct->gimmick.usableGimmick[BATTLE_PARTNER(battler)] == gimmick))))
{
return TRUE;
}
// Otherwise, return whether current battler has used gimmick.
else
{
return gBattleStruct->gimmick.activated[battler][gimmick];
}
}
// Sets a gimmick as used by a trainer with checks for Multi Battles.
void SetGimmickAsActivated(u32 battler, enum Gimmick gimmick)
{
gBattleStruct->gimmick.activated[battler][gimmick] = TRUE;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && IsPartnerMonFromSameTrainer(battler))
gBattleStruct->gimmick.activated[BATTLE_PARTNER(battler)][gimmick] = TRUE;
}
#define SINGLES_GIMMICK_TRIGGER_POS_X_OPTIMAL (30)
#define SINGLES_GIMMICK_TRIGGER_POS_X_PRIORITY (31)
#define SINGLES_GIMMICK_TRIGGER_POS_X_SLIDE (15)
#define SINGLES_GIMMICK_TRIGGER_POS_Y_DIFF (-11)
#define DOUBLES_GIMMICK_TRIGGER_POS_X_OPTIMAL (30)
#define DOUBLES_GIMMICK_TRIGGER_POS_X_PRIORITY (31)
#define DOUBLES_GIMMICK_TRIGGER_POS_X_SLIDE (15)
#define DOUBLES_GIMMICK_TRIGGER_POS_Y_DIFF (-4)
#define tBattler data[0]
#define tHide data[1]
void ChangeGimmickTriggerSprite(u32 spriteId, u32 animId)
{
StartSpriteAnim(&gSprites[spriteId], animId);
}
void CreateGimmickTriggerSprite(u32 battler)
{
const struct GimmickInfo * gimmick = &gGimmicksInfo[gBattleStruct->gimmick.usableGimmick[battler]];
// Exit if there shouldn't be a sprite produced.
if (GetBattlerSide(battler) == B_SIDE_OPPONENT
|| gBattleStruct->gimmick.usableGimmick[battler] == GIMMICK_NONE
|| gimmick->triggerSheet == NULL)
{
return;
}
LoadSpritePalette(gimmick->triggerPal);
if (GetSpriteTileStartByTag(TAG_GIMMICK_TRIGGER_TILE) == 0xFFFF)
LoadSpriteSheet(gimmick->triggerSheet);
if (gBattleStruct->gimmick.triggerSpriteId == 0xFF)
{
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
gBattleStruct->gimmick.triggerSpriteId = CreateSprite(gimmick->triggerTemplate,
gSprites[gHealthboxSpriteIds[battler]].x - DOUBLES_GIMMICK_TRIGGER_POS_X_SLIDE,
gSprites[gHealthboxSpriteIds[battler]].y - DOUBLES_GIMMICK_TRIGGER_POS_Y_DIFF, 0);
else
gBattleStruct->gimmick.triggerSpriteId = CreateSprite(gimmick->triggerTemplate,
gSprites[gHealthboxSpriteIds[battler]].x - SINGLES_GIMMICK_TRIGGER_POS_X_SLIDE,
gSprites[gHealthboxSpriteIds[battler]].y - SINGLES_GIMMICK_TRIGGER_POS_Y_DIFF, 0);
}
gSprites[gBattleStruct->gimmick.triggerSpriteId].tBattler = battler;
gSprites[gBattleStruct->gimmick.triggerSpriteId].tHide = FALSE;
ChangeGimmickTriggerSprite(gBattleStruct->gimmick.triggerSpriteId, 0);
}
bool32 IsGimmickTriggerSpriteActive(void)
{
if (GetSpriteTileStartByTag(TAG_GIMMICK_TRIGGER_TILE) == 0xFFFF)
return FALSE;
else if (IndexOfSpritePaletteTag(TAG_GIMMICK_TRIGGER_PAL) != 0xFF)
return TRUE;
else
return FALSE;
}
void HideGimmickTriggerSprite(void)
{
if (gBattleStruct->gimmick.triggerSpriteId != 0xFF)
{
ChangeGimmickTriggerSprite(gBattleStruct->gimmick.triggerSpriteId, 0);
gSprites[gBattleStruct->gimmick.triggerSpriteId].tHide = TRUE;
}
}
void DestroyGimmickTriggerSprite(void)
{
FreeSpritePaletteByTag(TAG_GIMMICK_TRIGGER_PAL);
FreeSpriteTilesByTag(TAG_GIMMICK_TRIGGER_TILE);
if (gBattleStruct->gimmick.triggerSpriteId != 0xFF)
DestroySprite(&gSprites[gBattleStruct->gimmick.triggerSpriteId]);
gBattleStruct->gimmick.triggerSpriteId = 0xFF;
}
static void SpriteCb_GimmickTrigger(struct Sprite *sprite)
{
s32 xSlide, xPriority, xOptimal;
s32 yDiff;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
{
xSlide = DOUBLES_GIMMICK_TRIGGER_POS_X_SLIDE;
xPriority = DOUBLES_GIMMICK_TRIGGER_POS_X_PRIORITY;
xOptimal = DOUBLES_GIMMICK_TRIGGER_POS_X_OPTIMAL;
yDiff = DOUBLES_GIMMICK_TRIGGER_POS_Y_DIFF;
}
else
{
xSlide = SINGLES_GIMMICK_TRIGGER_POS_X_SLIDE;
xPriority = SINGLES_GIMMICK_TRIGGER_POS_X_PRIORITY;
xOptimal = SINGLES_GIMMICK_TRIGGER_POS_X_OPTIMAL;
yDiff = SINGLES_GIMMICK_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)
DestroyGimmickTriggerSprite();
}
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;
}
}
#undef tBattler
#undef tHide
// for sprite data fields
#define tBattler data[0]
#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_Battler data[6]
void LoadIndicatorSpritesGfx(void)
{
u32 gimmick;
for (gimmick = 0; gimmick < GIMMICKS_COUNT; ++gimmick)
{
if (gimmick == GIMMICK_TERA) // special case
LoadSpriteSheets(sTeraIndicatorSpriteSheets);
else if (gGimmicksInfo[gimmick].indicatorSheet != NULL)
LoadSpriteSheet(gGimmicksInfo[gimmick].indicatorSheet);
if (gGimmicksInfo[gimmick].indicatorPal != NULL)
LoadSpritePalette(gGimmicksInfo[gimmick].indicatorPal);
}
// Primal reversion graphics aren't loaded as part of gimmick data
LoadSpriteSheet(&sSpriteSheet_AlphaIndicator);
LoadSpriteSheet(&sSpriteSheet_OmegaIndicator);
}
static void SpriteCb_GimmickIndicator(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;
}
static inline u32 GetIndicatorSpriteId(u32 healthboxId)
{
return gBattleStruct->gimmick.indicatorSpriteId[gSprites[healthboxId].hMain_Battler];
}
u32 GetIndicatorTileTag(u32 battler)
{
u32 gimmick = GetActiveGimmick(battler);
if (IsBattlerPrimalReverted(battler))
{
if (gBattleMons[battler].species == SPECIES_GROUDON_PRIMAL)
return TAG_OMEGA_INDICATOR_TILE;
else
return TAG_ALPHA_INDICATOR_TILE;
}
else if (gimmick == GIMMICK_TERA) // special case
{
return sTeraIndicatorSpriteSheets[GetBattlerTeraType(battler)].tag;
}
else if (gGimmicksInfo[gimmick].indicatorSheet != NULL)
{
return gGimmicksInfo[gimmick].indicatorSheet->tag;
}
else
{
return TAG_NONE;
}
}
u32 GetIndicatorPalTag(u32 battler)
{
u32 gimmick = GetActiveGimmick(battler);
if (IsBattlerPrimalReverted(battler))
return TAG_MISC_INDICATOR_PAL;
else if (gGimmicksInfo[gimmick].indicatorPal != NULL)
return gGimmicksInfo[gimmick].indicatorPal->tag;
else
return TAG_NONE;
}
void UpdateIndicatorVisibilityAndType(u32 healthboxId, bool32 invisible)
{
u32 battler = gSprites[healthboxId].hMain_Battler;
u32 tileTag = GetIndicatorTileTag(battler);
u32 palTag = GetIndicatorPalTag(battler);
struct Sprite *sprite = &gSprites[GetIndicatorSpriteId(healthboxId)];
if (tileTag != TAG_NONE && palTag != TAG_NONE)
{
sprite->oam.tileNum = GetSpriteTileStartByTag(tileTag);
sprite->oam.paletteNum = IndexOfSpritePaletteTag(palTag);
sprite->invisible = invisible;
}
else // in case of error
{
sprite->invisible = TRUE;
}
}
void UpdateIndicatorOamPriority(u32 healthboxId, u32 oamPriority)
{
gSprites[GetIndicatorSpriteId(healthboxId)].oam.priority = oamPriority;
}
void UpdateIndicatorLevelData(u32 healthboxId, u32 level)
{
s32 xDelta = 0;
if (level >= 100)
xDelta -= 4;
else if (level < 10)
xDelta += 5;
gSprites[GetIndicatorSpriteId(healthboxId)].tLevelXDelta = xDelta;
}
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},
};
void CreateIndicatorSprite(u32 battler)
{
u32 position, spriteId;
s16 xHealthbox = 0, x = 0, y = 0;
position = GetBattlerPosition(battler);
GetBattlerHealthboxCoords(battler, &xHealthbox, &y);
x = sIndicatorPositions[position][0];
y += sIndicatorPositions[position][1];
spriteId = CreateSpriteAtEnd(&sSpriteTemplate_GimmickIndicator, 0, y, 0);
gBattleStruct->gimmick.indicatorSpriteId[battler] = spriteId;
gSprites[spriteId].tBattler = battler;
gSprites[spriteId].tPosX = x;
gSprites[spriteId].invisible = FALSE;
}
#undef tBattler
#undef tPosX
#undef tLevelXDelta
#undef hMain_Battler

View File

@ -195,13 +195,6 @@ static void SpriteCB_StatusSummaryBalls_Enter(struct Sprite *);
static void SpriteCB_StatusSummaryBalls_Exit(struct Sprite *);
static void SpriteCB_StatusSummaryBalls_OnSwitchout(struct Sprite *);
static void SpriteCb_MegaTrigger(struct Sprite *);
static void SpriteCb_BurstTrigger(struct Sprite *);
static void MegaIndicator_UpdateLevel(u32 healthboxId, u32 level);
static void MegaIndicator_CreateSprite(u32 battlerId, u32 healthboxSpriteId);
static void MegaIndicator_UpdateOamPriority(u32 healthboxId, u32 oamPriority);
static void SpriteCb_MegaIndicator(struct Sprite *);
static u8 GetStatusIconForBattlerId(u8, u8);
static s32 CalcNewBarValue(s32, s32, s32, s32 *, u8, u16);
static u8 GetScaledExpFraction(s32, s32, s32, u8);
@ -620,122 +613,6 @@ static const struct WindowTemplate sHealthboxWindowTemplate = {
.baseBlock = 0
};
static const u8 ALIGNED(4) sMegaTriggerGfx[] = INCBIN_U8("graphics/battle_interface/mega_trigger.4bpp");
static const u16 sMegaTriggerPal[] = INCBIN_U16("graphics/battle_interface/mega_trigger.gbapal");
static const struct SpriteSheet sSpriteSheet_MegaTrigger =
{
sMegaTriggerGfx, sizeof(sMegaTriggerGfx), TAG_MEGA_TRIGGER_TILE
};
static const struct SpritePalette sSpritePalette_MegaTrigger =
{
sMegaTriggerPal, TAG_MEGA_TRIGGER_PAL
};
static const struct OamData sOamData_MegaTrigger =
{
.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_MegaTriggerOff[] =
{
ANIMCMD_FRAME(0, 0),
ANIMCMD_END
};
static const union AnimCmd sSpriteAnim_MegaTriggerOn[] =
{
ANIMCMD_FRAME(16, 0),
ANIMCMD_END
};
static const union AnimCmd *const sSpriteAnimTable_MegaTrigger[] =
{
sSpriteAnim_MegaTriggerOff,
sSpriteAnim_MegaTriggerOn,
};
static const struct SpriteTemplate sSpriteTemplate_MegaTrigger =
{
.tileTag = TAG_MEGA_TRIGGER_TILE,
.paletteTag = TAG_MEGA_TRIGGER_PAL,
.oam = &sOamData_MegaTrigger,
.anims = sSpriteAnimTable_MegaTrigger,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_MegaTrigger
};
static const u8 ALIGNED(4) sBurstTriggerGfx[] = INCBIN_U8("graphics/battle_interface/burst_trigger.4bpp");
static const u16 sBurstTriggerPal[] = INCBIN_U16("graphics/battle_interface/burst_trigger.gbapal");
static const struct SpriteSheet sSpriteSheet_BurstTrigger =
{
sBurstTriggerGfx, sizeof(sBurstTriggerGfx), TAG_BURST_TRIGGER_TILE
};
static const struct SpritePalette sSpritePalette_BurstTrigger =
{
sBurstTriggerPal, TAG_BURST_TRIGGER_PAL
};
static const struct OamData sOamData_BurstTrigger =
{
.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_BurstTriggerOff[] =
{
ANIMCMD_FRAME(0, 0),
ANIMCMD_END
};
static const union AnimCmd sSpriteAnim_BurstTriggerOn[] =
{
ANIMCMD_FRAME(16, 0),
ANIMCMD_END
};
static const union AnimCmd *const sSpriteAnimTable_BurstTrigger[] =
{
sSpriteAnim_BurstTriggerOff,
sSpriteAnim_BurstTriggerOn,
};
static const struct SpriteTemplate sSpriteTemplate_BurstTrigger =
{
.tileTag = TAG_BURST_TRIGGER_TILE,
.paletteTag = TAG_BURST_TRIGGER_PAL,
.oam = &sOamData_BurstTrigger,
.anims = sSpriteAnimTable_BurstTrigger,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_BurstTrigger
};
// Because the healthbox is too large to fit into one sprite, it is divided into two sprites.
// healthboxLeft or healthboxMain is the left part that is used as the 'main' sprite.
// healthboxRight or healthboxOther is the right part of the healthbox.
@ -743,7 +620,6 @@ static const struct SpriteTemplate sSpriteTemplate_BurstTrigger =
// data fields for healthboxMain
// oam.affineParam holds healthboxRight spriteId
#define hMain_MegaIndicatorId data[3]
#define hMain_HealthBarSpriteId data[5]
#define hMain_Battler data[6]
#define hMain_Data7 data[7]
@ -852,11 +728,7 @@ u8 CreateBattlerHealthboxSprites(u8 battlerId)
healthBarSpritePtr->hBar_Data6 = data6;
healthBarSpritePtr->invisible = TRUE;
// Create mega indicator sprite.
MegaIndicator_CreateSprite(battlerId, healthboxLeftSpriteId);
// Create tera indicator sprites.
TeraIndicator_CreateSprite(battlerId, healthboxLeftSpriteId);
CreateIndicatorSprite(battlerId);
gBattleStruct->ballSpriteIds[0] = MAX_SPRITES;
gBattleStruct->ballSpriteIds[1] = MAX_SPRITES;
@ -940,8 +812,7 @@ void SetHealthboxSpriteInvisible(u8 healthboxSpriteId)
gSprites[healthboxSpriteId].invisible = TRUE;
gSprites[gSprites[healthboxSpriteId].hMain_HealthBarSpriteId].invisible = TRUE;
gSprites[gSprites[healthboxSpriteId].oam.affineParam].invisible = TRUE;
MegaIndicator_SetVisibilities(healthboxSpriteId, TRUE);
TeraIndicator_SetVisibilities(healthboxSpriteId, TRUE);
UpdateIndicatorVisibilityAndType(healthboxSpriteId, TRUE);
}
void SetHealthboxSpriteVisible(u8 healthboxSpriteId)
@ -949,8 +820,7 @@ void SetHealthboxSpriteVisible(u8 healthboxSpriteId)
gSprites[healthboxSpriteId].invisible = FALSE;
gSprites[gSprites[healthboxSpriteId].hMain_HealthBarSpriteId].invisible = FALSE;
gSprites[gSprites[healthboxSpriteId].oam.affineParam].invisible = FALSE;
MegaIndicator_SetVisibilities(healthboxSpriteId, FALSE);
TeraIndicator_SetVisibilities(healthboxSpriteId, FALSE);
UpdateIndicatorVisibilityAndType(healthboxSpriteId, FALSE);
}
static void UpdateSpritePos(u8 spriteId, s16 x, s16 y)
@ -976,8 +846,8 @@ static void TryToggleHealboxVisibility(u32 priority, u32 healthboxLeftSpriteId,
gSprites[healthboxLeftSpriteId].invisible = invisible;
gSprites[healthboxRightSpriteId].invisible = invisible;
gSprites[healthbarSpriteId].invisible = invisible;
MegaIndicator_SetVisibilities(healthboxLeftSpriteId, invisible);
TeraIndicator_SetVisibilities(healthboxLeftSpriteId, invisible);
UpdateIndicatorVisibilityAndType(healthboxLeftSpriteId, invisible);
}
void UpdateOamPriorityInAllHealthboxes(u8 priority, bool32 hideHPBoxes)
@ -994,8 +864,7 @@ void UpdateOamPriorityInAllHealthboxes(u8 priority, bool32 hideHPBoxes)
gSprites[healthboxRightSpriteId].oam.priority = priority;
gSprites[healthbarSpriteId].oam.priority = priority;
MegaIndicator_UpdateOamPriority(healthboxLeftSpriteId, priority);
TeraIndicator_UpdateOamPriorities(healthboxLeftSpriteId, priority);
UpdateIndicatorOamPriority(healthboxLeftSpriteId, priority);
if (B_HIDE_HEALTHBOX_IN_ANIMS == TRUE && hideHPBoxes && IsBattlerAlive(i))
TryToggleHealboxVisibility(priority, healthboxLeftSpriteId, healthboxRightSpriteId, healthbarSpriteId);
@ -1050,20 +919,13 @@ static void UpdateLvlInHealthbox(u8 healthboxSpriteId, u8 lvl)
u8 *objVram;
u8 battler = gSprites[healthboxSpriteId].hMain_Battler;
// Don't print Lv char if mon is mega evolved or primal reverted or Dynamaxed.
if (IsBattlerMegaEvolved(battler) || IsBattlerPrimalReverted(battler) || IsDynamaxed(battler))
// Don't print Lv char if mon has a gimmick with an indicator active.
if (GetIndicatorTileTag(battler) != TAG_NONE)
{
objVram = ConvertIntToDecimalStringN(text, lvl, STR_CONV_MODE_LEFT_ALIGN, 3);
xPos = 5 * (3 - (objVram - (text + 2))) - 1;
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);
UpdateIndicatorLevelData(healthboxSpriteId, lvl);
UpdateIndicatorVisibilityAndType(healthboxSpriteId, FALSE);
}
else
{
@ -1072,7 +934,7 @@ static void UpdateLvlInHealthbox(u8 healthboxSpriteId, u8 lvl)
objVram = ConvertIntToDecimalStringN(text + 2, lvl, STR_CONV_MODE_LEFT_ALIGN, 3);
xPos = 5 * (3 - (objVram - (text + 2)));
MegaIndicator_SetVisibilities(healthboxSpriteId, TRUE);
UpdateIndicatorVisibilityAndType(healthboxSpriteId, TRUE);
}
windowTileData = AddTextPrinterAndCreateWindowOnHealthbox(text, xPos, 3, 2, &windowId);
@ -1373,435 +1235,6 @@ void SwapHpBarsWithHpText(void)
}
}
// Mega Evolution Trigger icon functions.
void ChangeMegaTriggerSprite(u8 spriteId, u8 animId)
{
StartSpriteAnim(&gSprites[spriteId], animId);
}
#define SINGLES_MEGA_TRIGGER_POS_X_OPTIMAL (30)
#define SINGLES_MEGA_TRIGGER_POS_X_PRIORITY (31)
#define SINGLES_MEGA_TRIGGER_POS_X_SLIDE (15)
#define SINGLES_MEGA_TRIGGER_POS_Y_DIFF (-11)
#define DOUBLES_MEGA_TRIGGER_POS_X_OPTIMAL (30)
#define DOUBLES_MEGA_TRIGGER_POS_X_PRIORITY (31)
#define DOUBLES_MEGA_TRIGGER_POS_X_SLIDE (15)
#define DOUBLES_MEGA_TRIGGER_POS_Y_DIFF (-4)
#define tBattler data[0]
#define tHide data[1]
void CreateMegaTriggerSprite(u8 battlerId, u8 palId)
{
LoadSpritePalette(&sSpritePalette_MegaTrigger);
if (GetSpriteTileStartByTag(TAG_MEGA_TRIGGER_TILE) == 0xFFFF)
LoadSpriteSheet(&sSpriteSheet_MegaTrigger);
if (gBattleStruct->mega.triggerSpriteId == 0xFF)
{
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
gBattleStruct->mega.triggerSpriteId = CreateSprite(&sSpriteTemplate_MegaTrigger,
gSprites[gHealthboxSpriteIds[battlerId]].x - DOUBLES_MEGA_TRIGGER_POS_X_SLIDE,
gSprites[gHealthboxSpriteIds[battlerId]].y - DOUBLES_MEGA_TRIGGER_POS_Y_DIFF, 0);
else
gBattleStruct->mega.triggerSpriteId = CreateSprite(&sSpriteTemplate_MegaTrigger,
gSprites[gHealthboxSpriteIds[battlerId]].x - SINGLES_MEGA_TRIGGER_POS_X_SLIDE,
gSprites[gHealthboxSpriteIds[battlerId]].y - SINGLES_MEGA_TRIGGER_POS_Y_DIFF, 0);
}
gSprites[gBattleStruct->mega.triggerSpriteId].tBattler = battlerId;
gSprites[gBattleStruct->mega.triggerSpriteId].tHide = FALSE;
ChangeMegaTriggerSprite(gBattleStruct->mega.triggerSpriteId, palId);
}
static void SpriteCb_MegaTrigger(struct Sprite *sprite)
{
s32 xSlide, xPriority, xOptimal;
s32 yDiff;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
{
xSlide = DOUBLES_MEGA_TRIGGER_POS_X_SLIDE;
xPriority = DOUBLES_MEGA_TRIGGER_POS_X_PRIORITY;
xOptimal = DOUBLES_MEGA_TRIGGER_POS_X_OPTIMAL;
yDiff = DOUBLES_MEGA_TRIGGER_POS_Y_DIFF;
}
else
{
xSlide = SINGLES_MEGA_TRIGGER_POS_X_SLIDE;
xPriority = SINGLES_MEGA_TRIGGER_POS_X_PRIORITY;
xOptimal = SINGLES_MEGA_TRIGGER_POS_X_OPTIMAL;
yDiff = SINGLES_MEGA_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)
DestroyMegaTriggerSprite();
}
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 IsMegaTriggerSpriteActive(void)
{
if (GetSpriteTileStartByTag(TAG_MEGA_TRIGGER_TILE) == 0xFFFF)
return FALSE;
else if (IndexOfSpritePaletteTag(TAG_MEGA_TRIGGER_PAL) != 0xFF)
return TRUE;
else
return FALSE;
}
void HideMegaTriggerSprite(void)
{
if (gBattleStruct->mega.triggerSpriteId >= MAX_SPRITES)
return;
ChangeMegaTriggerSprite(gBattleStruct->mega.triggerSpriteId, 0);
gSprites[gBattleStruct->mega.triggerSpriteId].tHide = TRUE;
}
void HideTriggerSprites(void)
{
HideMegaTriggerSprite();
HideBurstTriggerSprite();
HideZMoveTriggerSprite();
HideDynamaxTriggerSprite();
HideTeraTriggerSprite();
}
void DestroyMegaTriggerSprite(void)
{
FreeSpritePaletteByTag(TAG_MEGA_TRIGGER_PAL);
FreeSpriteTilesByTag(TAG_MEGA_TRIGGER_TILE);
if (gBattleStruct->mega.triggerSpriteId != 0xFF)
DestroySprite(&gSprites[gBattleStruct->mega.triggerSpriteId]);
gBattleStruct->mega.triggerSpriteId = 0xFF;
}
#undef tBattler
#undef tHide
// Ultra Burst Trigger icon functions.
void ChangeBurstTriggerSprite(u8 spriteId, u8 animId)
{
StartSpriteAnim(&gSprites[spriteId], animId);
}
#define SINGLES_BURST_TRIGGER_POS_X_OPTIMAL (30)
#define SINGLES_BURST_TRIGGER_POS_X_PRIORITY (31)
#define SINGLES_BURST_TRIGGER_POS_X_SLIDE (15)
#define SINGLES_BURST_TRIGGER_POS_Y_DIFF (-11)
#define DOUBLES_BURST_TRIGGER_POS_X_OPTIMAL (30)
#define DOUBLES_BURST_TRIGGER_POS_X_PRIORITY (31)
#define DOUBLES_BURST_TRIGGER_POS_X_SLIDE (15)
#define DOUBLES_BURST_TRIGGER_POS_Y_DIFF (-4)
#define tBattler data[0]
#define tHide data[1]
void CreateBurstTriggerSprite(u8 battlerId, u8 palId)
{
LoadSpritePalette(&sSpritePalette_BurstTrigger);
if (GetSpriteTileStartByTag(TAG_BURST_TRIGGER_TILE) == 0xFFFF)
LoadSpriteSheet(&sSpriteSheet_BurstTrigger);
if (gBattleStruct->burst.triggerSpriteId == 0xFF)
{
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
gBattleStruct->burst.triggerSpriteId = CreateSprite(&sSpriteTemplate_BurstTrigger,
gSprites[gHealthboxSpriteIds[battlerId]].x - DOUBLES_BURST_TRIGGER_POS_X_SLIDE,
gSprites[gHealthboxSpriteIds[battlerId]].y - DOUBLES_BURST_TRIGGER_POS_Y_DIFF, 0);
else
gBattleStruct->burst.triggerSpriteId = CreateSprite(&sSpriteTemplate_BurstTrigger,
gSprites[gHealthboxSpriteIds[battlerId]].x - SINGLES_BURST_TRIGGER_POS_X_SLIDE,
gSprites[gHealthboxSpriteIds[battlerId]].y - SINGLES_BURST_TRIGGER_POS_Y_DIFF, 0);
}
gSprites[gBattleStruct->burst.triggerSpriteId].tBattler = battlerId;
gSprites[gBattleStruct->burst.triggerSpriteId].tHide = FALSE;
ChangeBurstTriggerSprite(gBattleStruct->burst.triggerSpriteId, palId);
}
static void SpriteCb_BurstTrigger(struct Sprite *sprite)
{
s32 xSlide, xPriority, xOptimal;
s32 yDiff;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
{
xSlide = DOUBLES_BURST_TRIGGER_POS_X_SLIDE;
xPriority = DOUBLES_BURST_TRIGGER_POS_X_PRIORITY;
xOptimal = DOUBLES_BURST_TRIGGER_POS_X_OPTIMAL;
yDiff = DOUBLES_BURST_TRIGGER_POS_Y_DIFF;
}
else
{
xSlide = SINGLES_BURST_TRIGGER_POS_X_SLIDE;
xPriority = SINGLES_BURST_TRIGGER_POS_X_PRIORITY;
xOptimal = SINGLES_BURST_TRIGGER_POS_X_OPTIMAL;
yDiff = SINGLES_BURST_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)
DestroyBurstTriggerSprite();
}
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 IsBurstTriggerSpriteActive(void)
{
if (GetSpriteTileStartByTag(TAG_BURST_TRIGGER_TILE) == 0xFFFF)
return FALSE;
else if (IndexOfSpritePaletteTag(TAG_BURST_TRIGGER_PAL) != 0xFF)
return TRUE;
else
return FALSE;
}
void HideBurstTriggerSprite(void)
{
if (gBattleStruct->burst.triggerSpriteId >= MAX_SPRITES)
return;
ChangeBurstTriggerSprite(gBattleStruct->burst.triggerSpriteId, 0);
gSprites[gBattleStruct->burst.triggerSpriteId].tHide = TRUE;
}
void DestroyBurstTriggerSprite(void)
{
FreeSpritePaletteByTag(TAG_BURST_TRIGGER_PAL);
FreeSpriteTilesByTag(TAG_BURST_TRIGGER_TILE);
if (gBattleStruct->burst.triggerSpriteId != 0xFF)
DestroySprite(&gSprites[gBattleStruct->burst.triggerSpriteId]);
gBattleStruct->burst.triggerSpriteId = 0xFF;
}
#undef tBattler
#undef tHide
// Code for Mega Evolution (And Alpha/Omega) Trigger icon visible on the battler's healthbox.
enum
{
INDICATOR_MEGA,
INDICATOR_ALPHA,
INDICATOR_OMEGA,
INDICATOR_DYNAMAX,
INDICATOR_COUNT,
};
static const u8 ALIGNED(4) sMegaIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/mega_indicator.4bpp");
static const u16 sMegaIndicatorPal[] = INCBIN_U16("graphics/battle_interface/mega_indicator.gbapal");
static const u8 ALIGNED(4) sAlphaIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/alpha_indicator.4bpp");
static const u8 ALIGNED(4) sOmegaIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/omega_indicator.4bpp");
static const u16 sAlphaOmegaIndicatorPal[] = INCBIN_U16("graphics/battle_interface/misc_indicator.gbapal");
static const u8 ALIGNED(4) sDynamaxIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/dynamax_indicator.4bpp");
static const u16 sDynamaxIndicatorPal[] = INCBIN_U16("graphics/battle_interface/misc_indicator.gbapal");
static const struct SpriteSheet sMegaIndicator_SpriteSheets[] =
{
[INDICATOR_MEGA] = {sMegaIndicatorGfx, sizeof(sMegaIndicatorGfx), TAG_MEGA_INDICATOR_TILE},
[INDICATOR_ALPHA] = {sAlphaIndicatorGfx, sizeof(sAlphaIndicatorGfx), TAG_ALPHA_INDICATOR_TILE},
[INDICATOR_OMEGA] = {sOmegaIndicatorGfx, sizeof(sOmegaIndicatorGfx), TAG_OMEGA_INDICATOR_TILE},
[INDICATOR_DYNAMAX] = {sDynamaxIndicatorGfx, sizeof(sDynamaxIndicatorGfx), TAG_DYNAMAX_INDICATOR_TILE},
[INDICATOR_COUNT] = {0}
};
static const struct SpritePalette sMegaIndicator_SpritePalettes[] =
{
[INDICATOR_MEGA] = {sMegaIndicatorPal, TAG_MEGA_INDICATOR_PAL},
[INDICATOR_ALPHA] = {sAlphaOmegaIndicatorPal, TAG_MISC_INDICATOR_PAL},
[INDICATOR_OMEGA] = {sAlphaOmegaIndicatorPal, TAG_MISC_INDICATOR_PAL},
[INDICATOR_DYNAMAX] = {sDynamaxIndicatorPal, TAG_MISC_INDICATOR_PAL},
[INDICATOR_COUNT] = {0}
};
static const struct OamData sOamData_MegaIndicator =
{
.shape = SPRITE_SHAPE(16x16),
.size = SPRITE_SIZE(16x16),
.priority = 1,
};
static const struct SpriteTemplate sSpriteTemplate_MegaIndicator =
{
.tileTag = TAG_MEGA_INDICATOR_TILE,
.paletteTag = TAG_MEGA_INDICATOR_PAL,
.oam = &sOamData_MegaIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_MegaIndicator,
};
static const u16 sMegaIndicatorTags[][2] =
{
[INDICATOR_MEGA] = {TAG_MEGA_INDICATOR_TILE, TAG_MEGA_INDICATOR_PAL},
[INDICATOR_ALPHA] = {TAG_ALPHA_INDICATOR_TILE, TAG_MISC_INDICATOR_PAL},
[INDICATOR_OMEGA] = {TAG_OMEGA_INDICATOR_TILE, TAG_MISC_INDICATOR_PAL},
[INDICATOR_DYNAMAX] = {TAG_DYNAMAX_INDICATOR_TILE, TAG_MISC_INDICATOR_PAL},
};
static const s8 sIndicatorPositions[][2] =
{
[B_POSITION_PLAYER_LEFT] = {52, -9},
[B_POSITION_OPPONENT_LEFT] = {44, -9},
[B_POSITION_PLAYER_RIGHT] = {52, -9},
[B_POSITION_OPPONENT_RIGHT] = {44, -9},
};
// for sprite data fields
#define tBattler data[0]
#define tType data[1] // Indicator type: mega, alpha, omega
#define tPosX data[2]
#define tLevelXDelta data[3] // X position depends whether level has 3, 2 or 1 digit
void MegaIndicator_LoadSpritesGfx(void)
{
LoadSpriteSheets(sMegaIndicator_SpriteSheets);
LoadSpritePalettes(sMegaIndicator_SpritePalettes);
}
static bool32 MegaIndicator_ShouldBeInvisible(u32 battlerId, struct Sprite *sprite)
{
bool32 megaEvolved = IsBattlerMegaEvolved(battlerId);
bool32 primalReverted = IsBattlerPrimalReverted(battlerId);
bool32 dynamaxed = IsDynamaxed(battlerId);
if (!megaEvolved && !primalReverted && !dynamaxed)
return TRUE;
if (megaEvolved)
sprite->tType = INDICATOR_MEGA;
else if (primalReverted && gBattleMons[battlerId].species == SPECIES_KYOGRE_PRIMAL)
sprite->tType = INDICATOR_ALPHA;
else if (primalReverted && gBattleMons[battlerId].species == SPECIES_GROUDON_PRIMAL)
sprite->tType = INDICATOR_OMEGA;
else if (dynamaxed)
sprite->tType = INDICATOR_DYNAMAX;
sprite->oam.tileNum = GetSpriteTileStartByTag(sMegaIndicatorTags[sprite->tType][0]);
sprite->oam.paletteNum = IndexOfSpritePaletteTag(sMegaIndicatorTags[sprite->tType][1]);
return FALSE;
}
static u8 *MegaIndicator_GetSpriteId(u32 healthboxSpriteId)
{
u8 *spriteId = (u8 *)(&gSprites[healthboxSpriteId].hMain_MegaIndicatorId);
return spriteId;
}
void MegaIndicator_SetVisibilities(u32 healthboxId, bool32 invisible)
{
u8 *spriteId = MegaIndicator_GetSpriteId(healthboxId);
u32 battlerId = gSprites[healthboxId].hMain_Battler;
if (GetSafariZoneFlag())
return;
if (invisible == TRUE)
gSprites[*spriteId].invisible = TRUE;
else // Try visible.
gSprites[*spriteId].invisible = MegaIndicator_ShouldBeInvisible(battlerId, &gSprites[*spriteId]);
}
static void MegaIndicator_UpdateOamPriority(u32 healthboxId, u32 oamPriority)
{
u8 *spriteId = MegaIndicator_GetSpriteId(healthboxId);
gSprites[*spriteId].oam.priority = oamPriority;
}
static void MegaIndicator_UpdateLevel(u32 healthboxId, u32 level)
{
s16 xDelta = 0;
u8 *spriteId = MegaIndicator_GetSpriteId(healthboxId);
if (level >= 100)
xDelta -= 4;
else if (level < 10)
xDelta += 5;
gSprites[*spriteId].tLevelXDelta = xDelta;
}
static void MegaIndicator_CreateSprite(u32 battlerId, u32 healthboxSpriteId)
{
struct SpriteTemplate sprTemplate;
u32 position;
u8 *spriteId;
s16 xHealthbox = 0, y = 0;
s32 x = 0;
position = GetBattlerPosition(battlerId);
GetBattlerHealthboxCoords(battlerId, &xHealthbox, &y);
x = sIndicatorPositions[position][0];
y += sIndicatorPositions[position][1];
spriteId = MegaIndicator_GetSpriteId(healthboxSpriteId);
sprTemplate = sSpriteTemplate_MegaIndicator;
sprTemplate.tileTag = sMegaIndicatorTags[INDICATOR_MEGA][0];
sprTemplate.paletteTag = sMegaIndicatorTags[INDICATOR_MEGA][1];
*spriteId = CreateSpriteAtEnd(&sprTemplate, 0, y, 0);
gSprites[*spriteId].tType = INDICATOR_MEGA;
gSprites[*spriteId].tBattler = battlerId;
gSprites[*spriteId].tPosX = x;
gSprites[*spriteId].invisible = TRUE;
}
static void SpriteCb_MegaIndicator(struct Sprite *sprite)
{
u32 battlerId = sprite->tBattler;
sprite->x = gSprites[gHealthboxSpriteIds[battlerId]].x + sprite->tPosX + sprite->tLevelXDelta;
sprite->x2 = gSprites[gHealthboxSpriteIds[battlerId]].x2;
sprite->y2 = gSprites[gHealthboxSpriteIds[battlerId]].y2;
}
#undef tBattler
#undef tType
#undef tPosX
@ -2545,10 +1978,6 @@ 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)
{

View File

@ -13,6 +13,8 @@
#include "battle_setup.h"
#include "battle_tower.h"
#include "battle_util.h"
#include "battle_z_move.h"
#include "battle_gimmick.h"
#include "berry.h"
#include "bg.h"
#include "data.h"
@ -3096,9 +3098,6 @@ static void BattleStartClearSetData(void)
gBattleStruct->arenaLostPlayerMons = 0;
gBattleStruct->arenaLostOpponentMons = 0;
gBattleStruct->mega.triggerSpriteId = 0xFF;
gBattleStruct->burst.triggerSpriteId = 0xFF;
for (i = 0; i < ARRAY_COUNT(gSideTimers); i++)
{
gSideTimers[i].stickyWebBattlerId = 0xFF;
@ -3117,6 +3116,8 @@ static void BattleStartClearSetData(void)
}
gBattleStruct->swapDamageCategory = FALSE; // Photon Geyser, Shell Side Arm, Light That Burns the Sky
gBattleStruct->categoryOverride = FALSE; // used for Z-Moves and Max Moves
gSelectedMonPartyId = PARTY_SIZE; // Revival Blessing
gCategoryIconSpriteId = 0xFF;
}
@ -3246,9 +3247,6 @@ void SwitchInClearSetData(u32 battler)
// Reset G-Max Chi Strike boosts.
gBattleStruct->bonusCritStages[battler] = 0;
// Reset Dynamax flags.
UndoDynamax(battler);
gBattleStruct->overwrittenAbilities[battler] = ABILITY_NONE;
// Clear selected party ID so Revival Blessing doesn't get confused.
@ -3415,10 +3413,6 @@ const u8* FaintClearSetData(u32 battler)
}
}
// Clear Z-Move data
gBattleStruct->zmove.active = FALSE;
gBattleStruct->zmove.toBeUsed[battler] = MOVE_NONE;
gBattleStruct->zmove.effect = EFFECT_HIT;
// Clear Dynamax data
UndoDynamax(battler);
@ -3880,6 +3874,7 @@ static void TryDoEventsBeforeFirstTurn(void)
SpecialStatusesClear();
*(&gBattleStruct->absentBattlerFlags) = gAbsentBattlerFlags;
BattlePutTextOnWindow(gText_EmptyString3, B_WIN_MSG);
AssignUsableGimmicks();
gBattleMainFunc = HandleTurnActionSelectionState;
ResetSentPokesToOpponentValue();
@ -3998,6 +3993,7 @@ void BattleTurnPassed(void)
*(&gBattleStruct->absentBattlerFlags) = gAbsentBattlerFlags;
BattlePutTextOnWindow(gText_EmptyString3, B_WIN_MSG);
AssignUsableGimmicks();
SetShellSideArmCategory();
SetAiLogicDataForTurn(AI_DATA); // get assumed abilities, hold effects, etc of all battlers
gBattleMainFunc = HandleTurnActionSelectionState;
@ -4225,7 +4221,6 @@ static void HandleTurnActionSelectionState(void)
struct ChooseMoveStruct moveInfo;
moveInfo.zmove = gBattleStruct->zmove;
moveInfo.mega = gBattleStruct->mega;
moveInfo.species = gBattleMons[battler].species;
moveInfo.monType1 = gBattleMons[battler].type1;
moveInfo.monType2 = gBattleMons[battler].type2;
@ -4350,12 +4345,7 @@ static void HandleTurnActionSelectionState(void)
RecordedBattle_ClearBattlerAction(GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler))), 3);
}
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;
gBattleStruct->gimmick.toActivate &= ~(gBitTable[BATTLE_PARTNER(GetBattlerPosition(battler))]);
BtlController_EmitEndBounceEffect(battler, BUFFER_A);
MarkBattlerForControllerExec(battler);
return;
@ -4443,25 +4433,18 @@ 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 | RET_TERASTAL);
gBattleStruct->chosenMovePositions[battler] = gBattleResources->bufferB[battler][2] & ~RET_GIMMICK;
gChosenMoveByBattler[battler] = gBattleMons[battler].moves[gBattleStruct->chosenMovePositions[battler]];
gBattleStruct->moveTarget[battler] = gBattleResources->bufferB[battler][3];
// Check to see if any gimmicks need to be prepared.
if (gBattleResources->bufferB[battler][2] & RET_MEGA_EVOLUTION)
gBattleStruct->mega.toEvolve |= gBitTable[battler];
else if (gBattleResources->bufferB[battler][2] & RET_ULTRA_BURST)
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];
if (gBattleResources->bufferB[battler][2] & RET_GIMMICK)
gBattleStruct->gimmick.toActivate |= gBitTable[battler];
// Max Move check
if (ShouldUseMaxMove(battler, gChosenMoveByBattler[battler]))
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX || IsGimmickSelected(battler, GIMMICK_DYNAMAX))
{
gBattleStruct->dynamax.baseMove[battler] = gBattleMons[battler].moves[gBattleStruct->chosenMovePositions[battler]];
gBattleStruct->dynamax.usingMaxMove[battler] = TRUE;
gBattleStruct->dynamax.baseMoves[battler] = gBattleMons[battler].moves[gBattleStruct->chosenMovePositions[battler]];
}
gBattleCommunication[battler]++;
@ -4737,7 +4720,7 @@ u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, u32 holdEffect)
speed /= 2;
else if (holdEffect == HOLD_EFFECT_IRON_BALL)
speed /= 2;
else if (holdEffect == HOLD_EFFECT_CHOICE_SCARF && !IsDynamaxed(battler))
else if (holdEffect == HOLD_EFFECT_CHOICE_SCARF && GetActiveGimmick(battler) != GIMMICK_DYNAMAX)
speed = (speed * 150) / 100;
else if (holdEffect == HOLD_EFFECT_QUICK_POWDER && gBattleMons[battler].species == SPECIES_DITTO && !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED))
speed *= 2;
@ -4783,13 +4766,13 @@ s8 GetMovePriority(u32 battler, u16 move)
s8 priority;
u16 ability = GetBattlerAbility(battler);
if (gBattleStruct->zmove.toBeUsed[battler] && gMovesInfo[move].power != 0)
move = gBattleStruct->zmove.toBeUsed[battler];
if (GetActiveGimmick(battler) == GIMMICK_Z_MOVE && gMovesInfo[move].power != 0)
move = GetUsableZMove(battler, move);
priority = gMovesInfo[move].priority;
// Max Guard check
if (gBattleStruct->dynamax.usingMaxMove[battler] && gMovesInfo[move].category == DAMAGE_CATEGORY_STATUS)
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX && gMovesInfo[move].category == DAMAGE_CATEGORY_STATUS)
return gMovesInfo[MOVE_MAX_GUARD].priority;
if (ability == ABILITY_GALE_WINGS
@ -4803,7 +4786,7 @@ s8 GetMovePriority(u32 battler, u16 move)
gProtectStructs[battler].pranksterElevated = 1;
priority++;
}
else if (gMovesInfo[move].effect == EFFECT_GRASSY_GLIDE && gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && IsBattlerGrounded(battler) && !IsDynamaxed(battler) && !(gBattleStruct->dynamax.toDynamax & gBitTable[battler]))
else if (gMovesInfo[move].effect == EFFECT_GRASSY_GLIDE && gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && IsBattlerGrounded(battler) && GetActiveGimmick(gBattlerAttacker) != GIMMICK_DYNAMAX && !IsGimmickSelected(battler, GIMMICK_DYNAMAX))
{
priority++;
}
@ -5068,9 +5051,7 @@ static void PopulateArrayWithBattlers(u8 *battlers)
static bool32 TryDoGimmicksBeforeMoves(void)
{
if (!(gHitMarker & HITMARKER_RUN)
&& (gBattleStruct->mega.toEvolve || gBattleStruct->burst.toBurst
|| gBattleStruct->dynamax.toDynamax || gBattleStruct->tera.toTera))
if (!(gHitMarker & HITMARKER_RUN) && gBattleStruct->gimmick.toActivate)
{
u32 i, battler;
u8 order[MAX_BATTLERS_COUNT];
@ -5079,52 +5060,16 @@ static bool32 TryDoGimmicksBeforeMoves(void)
SortBattlersBySpeed(order, FALSE);
for (i = 0; i < gBattlersCount; i++)
{
// Tera Check
if (gBattleStruct->tera.toTera & gBitTable[order[i]])
// Search through each battler and activate their gimmick if they have one prepared.
if ((gBattleStruct->gimmick.toActivate & gBitTable[order[i]]) && !(gProtectStructs[order[i]].noValidMoves))
{
gBattlerAttacker = order[i];
gBattleScripting.battler = gBattlerAttacker;
gBattleStruct->tera.toTera &= ~(gBitTable[gBattlerAttacker]);
PrepareBattlerForTera(gBattlerAttacker);
PREPARE_TYPE_BUFFER(gBattleTextBuff1, GetBattlerTeraType(gBattlerAttacker));
if (TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_TERASTALLIZATION))
BattleScriptExecute(BattleScript_TeraFormChange);
else
BattleScriptExecute(BattleScript_Terastallization);
return TRUE;
}
// Dynamax Check
if (gBattleStruct->dynamax.toDynamax & gBitTable[order[i]])
{
gBattlerAttacker = order[i];
gBattleScripting.battler = gBattlerAttacker;
gBattleStruct->dynamax.toDynamax &= ~(gBitTable[gBattlerAttacker]);
PrepareBattlerForDynamax(gBattlerAttacker);
BattleScriptExecute(BattleScript_DynamaxBegins);
return TRUE;
}
// Mega Evo Check
if (gBattleStruct->mega.toEvolve & gBitTable[order[i]]
&& !(gProtectStructs[order[i]].noValidMoves))
{
gBattlerAttacker = order[i];
gBattleStruct->mega.toEvolve &= ~(gBitTable[gBattlerAttacker]);
gLastUsedItem = gBattleMons[gBattlerAttacker].item;
if (GetBattleFormChangeTargetSpecies(gBattlerAttacker, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_MOVE) != SPECIES_NONE)
BattleScriptExecute(BattleScript_WishMegaEvolution);
else
BattleScriptExecute(BattleScript_MegaEvolution);
return TRUE;
}
// Ultra Burst Check
if (gBattleStruct->burst.toBurst & gBitTable[order[i]]
&& !(gProtectStructs[order[i]].noValidMoves))
{
battler = gBattlerAttacker = order[i];
gBattleStruct->burst.toBurst &= ~(gBitTable[battler]);
gLastUsedItem = gBattleMons[battler].item;
BattleScriptExecute(BattleScript_UltraBurst);
return TRUE;
battler = gBattlerAttacker = gBattleScripting.battler = order[i];
gBattleStruct->gimmick.toActivate &= ~(gBitTable[battler]);
if (gGimmicksInfo[gBattleStruct->gimmick.usableGimmick[battler]].ActivateGimmick != NULL)
{
gGimmicksInfo[gBattleStruct->gimmick.usableGimmick[battler]].ActivateGimmick(battler);
return TRUE;
}
}
}
}
@ -5511,10 +5456,10 @@ static void HandleEndTurn_FinishBattle(void)
TryRestoreHeldItems();
// Undo Dynamax HP multiplier before recalculating stats.
for (i = 0; i < gBattlersCount; ++i)
for (battler = 0; battler < gBattlersCount; ++battler)
{
if (IsDynamaxed(i))
UndoDynamax(i);
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX)
UndoDynamax(battler);
}
for (i = 0; i < PARTY_SIZE; i++)
@ -5692,7 +5637,7 @@ bool32 TrySetAteType(u32 move, u32 battlerAtk, u32 attackerAbility)
switch (gMovesInfo[move].effect)
{
case EFFECT_TERA_BLAST:
if (IsTerastallized(battlerAtk))
if (GetActiveGimmick(battlerAtk) == GIMMICK_TERA)
return FALSE;
break;
case EFFECT_TERA_STARSTORM:
@ -5726,7 +5671,7 @@ bool32 TrySetAteType(u32 move, u32 battlerAtk, u32 attackerAbility)
break;
}
if (ateType != TYPE_NONE)
if (ateType != TYPE_NONE && GetActiveGimmick(battlerAtk) != GIMMICK_Z_MOVE)
{
gBattleStruct->dynamicMoveType = ateType | F_DYNAMIC_TYPE_SET;
return TRUE;
@ -5785,7 +5730,7 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk)
}
else if (gMovesInfo[move].effect == EFFECT_REVELATION_DANCE)
{
if (IsTerastallized(battlerAtk) && GetBattlerTeraType(battlerAtk) != TYPE_STELLAR)
if (GetActiveGimmick(battlerAtk) == GIMMICK_TERA && GetBattlerTeraType(battlerAtk) != TYPE_STELLAR)
gBattleStruct->dynamicMoveType = GetBattlerTeraType(battlerAtk);
else if (gBattleMons[battlerAtk].type1 != TYPE_MYSTERY)
gBattleStruct->dynamicMoveType = gBattleMons[battlerAtk].type1 | F_DYNAMIC_TYPE_SET;
@ -5829,7 +5774,7 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk)
gBattleStruct->dynamicMoveType = TYPE_NORMAL | F_DYNAMIC_TYPE_SET;
}
}
else if (gMovesInfo[move].effect == EFFECT_TERA_BLAST && IsTerastallized(battlerAtk))
else if (gMovesInfo[move].effect == EFFECT_TERA_BLAST && GetActiveGimmick(battlerAtk) == GIMMICK_TERA)
{
gBattleStruct->dynamicMoveType = GetBattlerTeraType(battlerAtk) | F_DYNAMIC_TYPE_SET;
}
@ -5839,18 +5784,20 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk)
}
attackerAbility = GetBattlerAbility(battlerAtk);
if (gMovesInfo[move].type == TYPE_NORMAL && TrySetAteType(move, battlerAtk, attackerAbility))
if (gMovesInfo[move].type == TYPE_NORMAL
&& TrySetAteType(move, battlerAtk, attackerAbility)
&& GetActiveGimmick(battlerAtk) != GIMMICK_DYNAMAX)
{
if (!IsDynamaxed(battlerAtk))
gBattleStruct->ateBoost[battlerAtk] = 1;
}
else if (gMovesInfo[move].type != TYPE_NORMAL
&& gMovesInfo[move].effect != EFFECT_HIDDEN_POWER
&& gMovesInfo[move].effect != EFFECT_WEATHER_BALL
&& attackerAbility == ABILITY_NORMALIZE)
&& attackerAbility == ABILITY_NORMALIZE
&& GetActiveGimmick(battlerAtk) != GIMMICK_Z_MOVE)
{
gBattleStruct->dynamicMoveType = TYPE_NORMAL | F_DYNAMIC_TYPE_SET;
if (!IsDynamaxed(battlerAtk))
if (GetActiveGimmick(battlerAtk) != GIMMICK_DYNAMAX)
gBattleStruct->ateBoost[battlerAtk] = 1;
}
else if (gMovesInfo[move].soundMove && attackerAbility == ABILITY_LIQUID_VOICE)

View File

@ -3967,7 +3967,10 @@ void BattlePutTextOnWindow(const u8 *text, u8 windowId)
// We cannot check the actual width of the window because
// B_WIN_MOVE_NAME_1 and B_WIN_MOVE_NAME_3 are 16 wide for
// Z-move details.
printerTemplate.fontId = GetFontIdToFit(text, printerTemplate.fontId, printerTemplate.letterSpacing, 8 * TILE_WIDTH);
if (gBattleStruct->zmove.viewing && windowId == B_WIN_MOVE_NAME_1)
printerTemplate.fontId = GetFontIdToFit(text, printerTemplate.fontId, printerTemplate.letterSpacing, 16 * TILE_WIDTH);
else
printerTemplate.fontId = GetFontIdToFit(text, printerTemplate.fontId, printerTemplate.letterSpacing, 8 * TILE_WIDTH);
}
if (printerTemplate.x == 0xFF)
@ -4023,7 +4026,7 @@ void SetPpNumbersPaletteInMoveSelection(u32 battler)
var = GetCurrentPpToMaxPpState(chooseMoveStruct->currentPp[gMoveSelectionCursor[battler]],
chooseMoveStruct->maxPp[gMoveSelectionCursor[battler]]);
else
var = GetCurrentPpToMaxPpState(chooseMoveStruct->currentPp[gMoveSelectionCursor[battler]], gMovesInfo[gMoveSelectionCursor[battler]].pp);
var = 3;
gPlttBufferUnfaded[BG_PLTT_ID(5) + 12] = palPtr[(var * 2) + 0];
gPlttBufferUnfaded[BG_PLTT_ID(5) + 11] = palPtr[(var * 2) + 1];

View File

@ -1163,7 +1163,7 @@ bool32 ProteanTryChangeType(u32 battler, u32 ability, u32 move, u32 moveType)
&& (gBattleMons[battler].type1 != moveType || gBattleMons[battler].type2 != moveType
|| (gBattleMons[battler].type3 != moveType && gBattleMons[battler].type3 != TYPE_MYSTERY))
&& move != MOVE_STRUGGLE
&& !IsTerastallized(battler))
&& GetActiveGimmick(battler) != GIMMICK_TERA)
{
SET_BATTLER_TYPE(battler, moveType);
return TRUE;
@ -1198,7 +1198,7 @@ static void Cmd_attackcanceler(void)
GET_MOVE_TYPE(gCurrentMove, moveType);
// Weight-based moves are blocked by Dynamax.
if (IsDynamaxed(gBattlerTarget) && IsMoveBlockedByDynamax(gCurrentMove))
if ((GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX) && IsMoveBlockedByDynamax(gCurrentMove))
{
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_MoveBlockedByDynamax;
@ -1243,7 +1243,7 @@ static void Cmd_attackcanceler(void)
&& GetBattlerAbility(gBattlerAttacker) == ABILITY_PARENTAL_BOND
&& IsMoveAffectedByParentalBond(gCurrentMove, gBattlerAttacker)
&& !(gAbsentBattlerFlags & gBitTable[gBattlerTarget])
&& gBattleStruct->zmove.toBeUsed[gBattlerAttacker] == MOVE_NONE)
&& GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE)
{
gSpecialStatuses[gBattlerAttacker].parentalBondState = PARENTAL_BOND_1ST_HIT;
gMultiHitCounter = 2;
@ -1364,7 +1364,8 @@ static void Cmd_attackcanceler(void)
}
// Z-moves and Max Moves bypass protection, but deal reduced damage (factored in AccumulateOtherModifiers)
if ((gBattleStruct->zmove.active || IsMaxMove(gCurrentMove))
if ((IsZMove(gCurrentMove)
|| IsMaxMove(gCurrentMove))
&& IS_BATTLER_PROTECTED(gBattlerTarget))
{
BattleScriptPush(cmd->nextInstr);
@ -1513,7 +1514,7 @@ static bool32 AccuracyCalcHelper(u16 move)
return TRUE;
}
if (gBattleStruct->zmove.active && !(gStatuses3[gBattlerTarget] & STATUS3_SEMI_INVULNERABLE))
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !(gStatuses3[gBattlerTarget] & STATUS3_SEMI_INVULNERABLE))
{
JumpIfMoveFailed(7, move);
return TRUE;
@ -3178,7 +3179,7 @@ void SetMoveEffect(bool32 primary, bool32 certain)
}
}
else if (GetBattlerTurnOrderNum(gEffectBattler) > gCurrentTurnActionNumber
&& !IsDynamaxed(gEffectBattler))
&& !(GetActiveGimmick(gEffectBattler) == GIMMICK_DYNAMAX))
{
gBattleMons[gEffectBattler].status2 |= sStatusFlagsForMoveEffects[gBattleScripting.moveEffect];
gBattlescriptCurrInstr++;
@ -3800,7 +3801,7 @@ void SetMoveEffect(bool32 primary, bool32 certain)
}
break;
case MOVE_EFFECT_TERA_BLAST:
if (IsTerastallized(gEffectBattler)
if (GetActiveGimmick(gEffectBattler) == GIMMICK_TERA
&& GetBattlerTeraType(gEffectBattler) == TYPE_STELLAR
&& !NoAliveMonsForEitherParty())
{
@ -3977,7 +3978,7 @@ static void Cmd_tryfaintmon(void)
gSideTimers[B_SIDE_OPPONENT].retaliateTimer = 2;
}
if ((gHitMarker & HITMARKER_DESTINYBOND) && IsBattlerAlive(gBattlerAttacker)
&& !IsDynamaxed(gBattlerAttacker))
&& !(GetActiveGimmick(gBattlerAttacker) == GIMMICK_DYNAMAX))
{
gHitMarker &= ~HITMARKER_DESTINYBOND;
BattleScriptPush(gBattlescriptCurrInstr);
@ -5857,7 +5858,7 @@ static void Cmd_moveend(void)
gLastPrintedMoves[gBattlerAttacker] = gChosenMove;
gLastUsedMove = gCurrentMove;
if (IsMaxMove(gCurrentMove))
gBattleStruct->dynamax.lastUsedBaseMove = gBattleStruct->dynamax.baseMove[gBattlerAttacker];
gBattleStruct->dynamax.lastUsedBaseMove = gBattleStruct->dynamax.baseMoves[gBattlerAttacker];
}
}
if (!(gAbsentBattlerFlags & gBitTable[gBattlerAttacker])
@ -6390,17 +6391,16 @@ static void Cmd_moveend(void)
gSpecialStatuses[gBattlerAttacker].preventLifeOrbDamage = 0;
gSpecialStatuses[gBattlerTarget].berryReduced = FALSE;
gBattleScripting.moveEffect = 0;
// clear attacker z move data
gBattleStruct->zmove.active = FALSE;
gBattleStruct->zmove.toBeUsed[gBattlerAttacker] = MOVE_NONE;
gBattleStruct->zmove.effect = EFFECT_HIT;
gBattleStruct->hitSwitchTargetFailed = FALSE;
gBattleStruct->isAtkCancelerForCalledMove = FALSE;
gBattleStruct->swapDamageCategory = FALSE;
gBattleStruct->categoryOverride = FALSE;
gBattleStruct->bouncedMoveIsUsed = FALSE;
gBattleStruct->enduredDamage = 0;
gBattleStruct->additionalEffectsCounter = 0;
gBattleStruct->poisonPuppeteerConfusion = FALSE;
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE)
SetActiveGimmick(gBattlerAttacker, GIMMICK_NONE);
gBattleStruct->distortedTypeMatchups = 0;
gBattleScripting.moveendState++;
break;
@ -8770,7 +8770,6 @@ static void HandleScriptMegaPrimalBurst(u32 caseId, u32 battler, u32 type)
{
struct Pokemon *party = GetBattlerParty(battler);
struct Pokemon *mon = &party[gBattlerPartyIndexes[battler]];
u32 position = GetBattlerPosition(battler);
u32 side = GetBattlerSide(battler);
// Change species.
@ -8798,9 +8797,9 @@ static void HandleScriptMegaPrimalBurst(u32 caseId, u32 battler, u32 type)
if (side == B_SIDE_OPPONENT)
SetBattlerShadowSpriteCallback(battler, gBattleMons[battler].species);
if (type == HANDLE_TYPE_MEGA_EVOLUTION)
gBattleStruct->mega.alreadyEvolved[position] = TRUE;
SetGimmickAsActivated(battler, GIMMICK_MEGA);
if (type == HANDLE_TYPE_ULTRA_BURST)
gBattleStruct->burst.alreadyBursted[position] = TRUE;
SetGimmickAsActivated(battler, GIMMICK_ULTRA_BURST);
}
}
@ -9663,7 +9662,7 @@ static void Cmd_various(void)
else
{
if (gBattleMons[gBattlerTarget].ability == gBattleMons[gBattlerAttacker].ability
|| IsDynamaxed(gBattlerTarget))
|| (GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX))
{
gBattlescriptCurrInstr = cmd->failInstr;
}
@ -9702,7 +9701,15 @@ static void Cmd_various(void)
gBattlescriptCurrInstr = cmd->failInstr;
else
{
gCalledMove = move;
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !IS_MOVE_STATUS(move))
{
gBattleStruct->zmove.baseMoves[gBattlerAttacker] = move;
gCalledMove = GetTypeBasedZMove(move);
}
else
{
gCalledMove = move;
}
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
gBattlerTarget = GetMoveTarget(gCalledMove, NO_TARGET_OVERRIDE);
gStatuses3[gBattlerAttacker] |= STATUS3_ME_FIRST;
@ -9738,7 +9745,7 @@ static void Cmd_various(void)
VARIOUS_ARGS(const u8 *failInstr);
if ((GetBattlerType(gBattlerTarget, 0, FALSE) == gMovesInfo[gCurrentMove].type
&& GetBattlerType(gBattlerTarget, 1, FALSE) == gMovesInfo[gCurrentMove].type)
|| IsTerastallized(gBattlerTarget))
|| GetActiveGimmick(gBattlerTarget) == GIMMICK_TERA)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
@ -9835,7 +9842,9 @@ static void Cmd_various(void)
if (move == MOVE_NONE || move == MOVE_UNAVAILABLE || MoveHasAdditionalEffectSelf(move, MOVE_EFFECT_RECHARGE)
|| gMovesInfo[move].instructBanned
|| gBattleMoveEffects[gMovesInfo[move].effect].twoTurnEffect
|| IsDynamaxed(gBattlerTarget))
|| (GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX)
|| IsZMove(move)
|| IsMaxMove(move))
{
gBattlescriptCurrInstr = cmd->failInstr;
}
@ -10071,7 +10080,7 @@ static void Cmd_various(void)
case VARIOUS_TRY_THIRD_TYPE:
{
VARIOUS_ARGS(const u8 *failInstr);
if (IS_BATTLER_OF_TYPE(battler, gMovesInfo[gCurrentMove].argument) || IsTerastallized(battler))
if (IS_BATTLER_OF_TYPE(battler, gMovesInfo[gCurrentMove].argument) || GetActiveGimmick(battler) == GIMMICK_TERA)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
@ -11064,10 +11073,10 @@ static void SetMoveForMirrorMove(u32 move)
{
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
// Edge case, we used Z Mirror Move, got the stat boost and now need to use the Z-move
if (gBattleStruct->zmove.toBeUsed[gBattlerAttacker] && !IS_MOVE_STATUS(move))
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !IS_MOVE_STATUS(move))
{
gCurrentMove = gBattleStruct->zmove.chosenZMove = GetTypeBasedZMove(move, gBattlerAttacker);
QueueZMove(gBattlerAttacker, move);
gBattleStruct->zmove.baseMoves[gBattlerAttacker] = move;
gCurrentMove = GetTypeBasedZMove(move);
}
else
{
@ -12107,7 +12116,7 @@ static void Cmd_tryconversiontypechange(void)
u8 moveChecked = 0;
u8 moveType = 0;
if (IsTerastallized(gBattlerAttacker))
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA)
{
gBattlescriptCurrInstr = cmd->failInstr;
return;
@ -12251,7 +12260,7 @@ static void Cmd_tryKO(void)
u16 targetAbility = GetBattlerAbility(gBattlerTarget);
// Dynamaxed Pokemon cannot be hit by OHKO moves.
if (IsDynamaxed(gBattlerTarget))
if ((GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX))
{
gMoveResultFlags |= MOVE_RESULT_MISSED;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_KO_UNAFFECTED;
@ -12799,11 +12808,11 @@ static void Cmd_trysetencore(void)
s32 i;
if (IsMaxMove(gLastMoves[gBattlerTarget]) && !IsDynamaxed(gBattlerTarget))
if (IsMaxMove(gLastMoves[gBattlerTarget]) && !(GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX))
{
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (gBattleMons[gBattlerTarget].moves[i] == gBattleStruct->dynamax.baseMove[gBattlerTarget])
if (gBattleMons[gBattlerTarget].moves[i] == gBattleStruct->dynamax.baseMoves[gBattlerTarget])
break;
}
}
@ -12882,7 +12891,7 @@ static void Cmd_settypetorandomresistance(void)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else if (IsTerastallized(gBattlerAttacker))
else if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
@ -13021,7 +13030,15 @@ static void Cmd_trychoosesleeptalkmove(void)
movePosition = MOD(Random(), MAX_MON_MOVES);
} while ((gBitTable[movePosition] & unusableMovesBits));
gCalledMove = gBattleMons[gBattlerAttacker].moves[movePosition];
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !IS_MOVE_STATUS(gBattleMons[gBattlerAttacker].moves[movePosition]))
{
gBattleStruct->zmove.baseMoves[gBattlerAttacker] = gBattleMons[gBattlerAttacker].moves[movePosition];
gCalledMove = GetTypeBasedZMove(gBattleMons[gBattlerAttacker].moves[movePosition]);
}
else
{
gCalledMove = gBattleMons[gBattlerAttacker].moves[movePosition];
}
gCurrMovePos = movePosition;
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
gBattlerTarget = GetMoveTarget(gCalledMove, NO_TARGET_OVERRIDE);
@ -13090,7 +13107,7 @@ static void Cmd_tryspiteppreduce(void)
{
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (gBattleStruct->dynamax.baseMove[gBattlerTarget] == gBattleMons[gBattlerTarget].moves[i])
if (gBattleStruct->dynamax.baseMoves[gBattlerTarget] == gBattleMons[gBattlerTarget].moves[i])
break;
}
}
@ -13943,25 +13960,33 @@ static void Cmd_callterrainattack(void)
CMD_ARGS();
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
gCurrentMove = GetNaturePowerMove();
gCurrentMove = GetNaturePowerMove(gBattlerAttacker);
gBattlerTarget = GetMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE);
BattleScriptPush(GET_MOVE_BATTLESCRIPT(gCurrentMove));
gBattlescriptCurrInstr = cmd->nextInstr;
}
u16 GetNaturePowerMove(void)
u32 GetNaturePowerMove(u32 battler)
{
u32 move = sNaturePowerMoves[gBattleTerrain];
if (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN)
return MOVE_MOONBLAST;
move = MOVE_MOONBLAST;
else if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN)
return MOVE_THUNDERBOLT;
move = MOVE_THUNDERBOLT;
else if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN)
return MOVE_ENERGY_BALL;
move = MOVE_ENERGY_BALL;
else if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN)
return MOVE_PSYCHIC;
move = MOVE_PSYCHIC;
else if (sNaturePowerMoves[gBattleTerrain] == MOVE_NONE)
return MOVE_TRI_ATTACK;
return sNaturePowerMoves[gBattleTerrain];
move = MOVE_TRI_ATTACK;
if (GetActiveGimmick(battler) == GIMMICK_Z_MOVE)
{
gBattleStruct->zmove.baseMoves[gBattlerAttacker] = move;
move = GetTypeBasedZMove(move);
}
return move;
}
// Refresh
@ -13987,7 +14012,7 @@ static void Cmd_settorment(void)
CMD_ARGS(const u8 *failInstr);
if (gBattleMons[gBattlerTarget].status2 & STATUS2_TORMENT
|| IsDynamaxed(gBattlerTarget))
|| (GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX))
{
gBattlescriptCurrInstr = cmd->failInstr;
}
@ -14376,7 +14401,7 @@ static void Cmd_tryswapabilities(void)
}
else
{
if (gMoveResultFlags & MOVE_RESULT_NO_EFFECT || IsDynamaxed(gBattlerTarget))
if (gMoveResultFlags & MOVE_RESULT_NO_EFFECT || (GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX))
{
gBattlescriptCurrInstr = cmd->failInstr;
}
@ -14854,7 +14879,7 @@ static void Cmd_settypetoterrain(void)
break;
}
if (!IS_BATTLER_OF_TYPE(gBattlerAttacker, terrainType) && !IsTerastallized(gBattlerAttacker))
if (!IS_BATTLER_OF_TYPE(gBattlerAttacker, terrainType) && GetActiveGimmick(gBattlerAttacker) != GIMMICK_TERA)
{
SET_BATTLER_TYPE(gBattlerAttacker, terrainType);
PREPARE_TYPE_BUFFER(gBattleTextBuff1, terrainType);
@ -16476,7 +16501,7 @@ void BS_TryReflectType(void)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else if (IsTerastallized(gBattlerAttacker))
else if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
@ -16707,9 +16732,8 @@ void BS_TryTrainerSlideDynamaxMsg(void)
NATIVE_ARGS();
s32 shouldSlide;
if ((shouldSlide = ShouldDoTrainerSlide(gBattlerAttacker, TRAINER_SLIDE_DYNAMAX)))
if ((shouldSlide = ShouldDoTrainerSlide(gBattleScripting.battler, TRAINER_SLIDE_DYNAMAX)))
{
gBattleScripting.battler = gBattlerAttacker;
BattleScriptPush(cmd->nextInstr);
gBattlescriptCurrInstr = (shouldSlide == 1 ? BattleScript_TrainerASlideMsgRet : BattleScript_TrainerBSlideMsgRet);
}
@ -16750,10 +16774,19 @@ void BS_TryCopycat(void)
}
else
{
if (IsMaxMove(gLastUsedMove))
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !IS_MOVE_STATUS(gLastUsedMove))
{
gBattleStruct->zmove.baseMoves[gBattlerAttacker] = gLastUsedMove;
gCalledMove = GetTypeBasedZMove(gLastUsedMove);
}
else if (IsMaxMove(gLastUsedMove))
{
gCalledMove = gBattleStruct->dynamax.lastUsedBaseMove;
}
else
{
gCalledMove = gLastUsedMove;
}
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
gBattlerTarget = GetMoveTarget(gCalledMove, NO_TARGET_OVERRIDE);
@ -16841,8 +16874,8 @@ void BS_AllySwitchFailChance(void)
void BS_SetPhotonGeyserCategory(void)
{
NATIVE_ARGS();
if (!((gMovesInfo[gCurrentMove].effect == EFFECT_TERA_BLAST && !IsTerastallized(gBattlerAttacker))
|| (gMovesInfo[gCurrentMove].effect == EFFECT_TERA_STARSTORM && !(IsTerastallized(gBattlerAttacker) && gBattleMons[gBattlerAttacker].species == SPECIES_TERAPAGOS_STELLAR))))
if (!((gMovesInfo[gCurrentMove].effect == EFFECT_TERA_BLAST && GetActiveGimmick(gBattlerAttacker) != GIMMICK_TERA)
|| (gMovesInfo[gCurrentMove].effect == EFFECT_TERA_STARSTORM && GetActiveGimmick(gBattlerAttacker) != GIMMICK_TERA && gBattleMons[gBattlerAttacker].species == SPECIES_TERAPAGOS_STELLAR)))
gBattleStruct->swapDamageCategory = (GetCategoryBasedOnStats(gBattlerAttacker) == DAMAGE_CATEGORY_PHYSICAL);
gBattlescriptCurrInstr = cmd->nextInstr;
}
@ -17043,3 +17076,12 @@ void BS_ApplyTerastallization(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_DamageToQuarterTargetHP(void)
{
NATIVE_ARGS();
gBattleMoveDamage = (3 * GetNonDynamaxHP(gBattlerTarget)) / 4;
if (gBattleMoveDamage == 0)
gBattleMoveDamage = 1;
gBattlescriptCurrInstr = cmd->nextInstr;
}

View File

@ -4,6 +4,8 @@
#include "battle_controllers.h"
#include "battle_interface.h"
#include "battle_terastal.h"
#include "battle_gimmick.h"
#include "battle_scripts.h"
#include "event_data.h"
#include "item.h"
#include "palette.h"
@ -16,14 +18,13 @@
#include "constants/rgb.h"
// Sets flags and variables upon a battler's Terastallization.
void PrepareBattlerForTera(u32 battler)
void ActivateTera(u32 battler)
{
u32 side = GetBattlerSide(battler);
u32 index = gBattlerPartyIndexes[battler];
// Update TeraData fields.
gBattleStruct->tera.isTerastallized[side] |= gBitTable[index];
gBattleStruct->tera.alreadyTerastallized[battler] = TRUE;
// Set appropriate flags.
SetActiveGimmick(battler, GIMMICK_TERA);
SetGimmickAsActivated(battler, GIMMICK_TERA);
// Remove Tera Orb charge.
if (B_FLAG_TERA_ORB_CHARGED != 0
@ -33,6 +34,13 @@ void PrepareBattlerForTera(u32 battler)
{
FlagClear(B_FLAG_TERA_ORB_CHARGED);
}
// Execute battle script.
PREPARE_TYPE_BUFFER(gBattleTextBuff1, GetBattlerTeraType(battler));
if (TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_TERASTALLIZATION))
BattleScriptExecute(BattleScript_TeraFormChange);
else
BattleScriptExecute(BattleScript_Terastallization);
}
// Applies palette blend and enables UI indicator after animation has played
@ -56,33 +64,32 @@ bool32 CanTerastallize(u32 battler)
u32 holdEffect = GetBattlerHoldEffect(battler, FALSE);
// Check if Player has Tera Orb and has charge.
if (!CheckBagHasItem(ITEM_TERA_ORB, 1)
|| !((B_FLAG_TERA_ORB_NO_COST != 0 && FlagGet(B_FLAG_TERA_ORB_NO_COST))
|| (B_FLAG_TERA_ORB_CHARGED != 0 && FlagGet(B_FLAG_TERA_ORB_CHARGED)
&& ((battler == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && battler == B_POSITION_PLAYER_RIGHT))))))
{
if (!TESTING && !CheckBagHasItem(ITEM_TERA_ORB, 1))
return FALSE;
if (!TESTING
&& !(B_FLAG_TERA_ORB_NO_COST != 0 && FlagGet(B_FLAG_TERA_ORB_NO_COST))
&& (battler == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && battler == B_POSITION_PLAYER_RIGHT)))
{
if (B_FLAG_TERA_ORB_CHARGED != 0 && !FlagGet(B_FLAG_TERA_ORB_CHARGED))
return FALSE;
}
// Check if Trainer has already Terastallized.
if (gBattleStruct->tera.alreadyTerastallized[battler])
{
if (HasTrainerUsedGimmick(battler, GIMMICK_TERA))
return FALSE;
}
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE
&& IsPartnerMonFromSameTrainer(battler)
&& (gBattleStruct->tera.alreadyTerastallized[BATTLE_PARTNER(battler)]
|| (gBattleStruct->tera.toTera & gBitTable[BATTLE_PARTNER(battler)])))
{
// Check if AI battler is intended to Terastallize.
if (!ShouldTrainerBattlerUseGimmick(battler, GIMMICK_TERA))
return FALSE;
// Check if battler has another gimmick active.
if (GetActiveGimmick(battler) != GIMMICK_NONE)
return FALSE;
}
// Check if battler is holding a Z-Crystal or Mega Stone.
if (holdEffect == HOLD_EFFECT_Z_CRYSTAL || holdEffect == HOLD_EFFECT_MEGA_STONE)
{
if (!TESTING && (holdEffect == HOLD_EFFECT_Z_CRYSTAL || holdEffect == HOLD_EFFECT_MEGA_STONE)) // tests make this check already
return FALSE;
}
// Every check passed!
return TRUE;
@ -94,25 +101,18 @@ u32 GetBattlerTeraType(u32 battler)
return GetMonData(&GetBattlerParty(battler)[gBattlerPartyIndexes[battler]], MON_DATA_TERA_TYPE);
}
// Returns whether a battler is terastallized.
bool32 IsTerastallized(u32 battler)
{
return gBattleStruct->tera.isTerastallized[GetBattlerSide(battler)] & gBitTable[gBattlerPartyIndexes[battler]];
}
// Uses up a type's Stellar boost.
void ExpendTypeStellarBoost(u32 battler, u32 type)
{
if (type < 32) // avoid OOB access
gBattleStruct->tera.stellarBoostFlags[GetBattlerSide(battler)] |= gBitTable[type];
gBattleStruct->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]);
return !(gBattleStruct->stellarBoostFlags[GetBattlerSide(battler)] & gBitTable[type]);
else
return FALSE;
}
@ -125,7 +125,7 @@ uq4_12_t GetTeraMultiplier(u32 battler, u32 type)
bool32 hasAdaptability = (GetBattlerAbility(battler) == ABILITY_ADAPTABILITY);
// Safety check.
if (!IsTerastallized(battler))
if (GetActiveGimmick(battler) != GIMMICK_TERA)
return UQ_4_12(1.0);
// Stellar-type checks.
@ -172,604 +172,3 @@ u16 GetTeraTypeRGB(u32 type)
{
return gTypesInfo[type].teraTypeRGBValue;
}
// TERASTAL TRIGGER:
static const u8 ALIGNED(4) sTeraTriggerGfx[] = INCBIN_U8("graphics/battle_interface/tera_trigger.4bpp");
static const u16 sTeraTriggerPal[] = INCBIN_U16("graphics/battle_interface/tera_trigger.gbapal");
static const struct SpriteSheet sSpriteSheet_TeraTrigger =
{
sTeraTriggerGfx, sizeof(sTeraTriggerGfx), TAG_TERA_TRIGGER_TILE
};
static const struct SpritePalette sSpritePalette_TeraTrigger =
{
sTeraTriggerPal, TAG_TERA_TRIGGER_PAL
};
static const struct OamData sOamData_TeraTrigger =
{
.y = 0,
.affineMode = 0,
.objMode = 0,
.mosaic = 0,
.bpp = 0,
.shape = ST_OAM_SQUARE,
.x = 0,
.matrixNum = 0,
.size = 2,
.tileNum = 0,
.priority = 1,
.paletteNum = 0,
.affineParam = 0,
};
static const union AnimCmd sSpriteAnim_TeraTriggerOff[] =
{
ANIMCMD_FRAME(0, 0),
ANIMCMD_END
};
static const union AnimCmd sSpriteAnim_TeraTriggerOn[] =
{
ANIMCMD_FRAME(16, 0),
ANIMCMD_END
};
static const union AnimCmd *const sSpriteAnimTable_TeraTrigger[] =
{
sSpriteAnim_TeraTriggerOff,
sSpriteAnim_TeraTriggerOn,
};
static void SpriteCb_TeraTrigger(struct Sprite *sprite);
static const struct SpriteTemplate sSpriteTemplate_TeraTrigger =
{
.tileTag = TAG_TERA_TRIGGER_TILE,
.paletteTag = TAG_TERA_TRIGGER_PAL,
.oam = &sOamData_TeraTrigger,
.anims = sSpriteAnimTable_TeraTrigger,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraTrigger
};
// Tera Evolution Trigger icon functions.
void ChangeTeraTriggerSprite(u8 spriteId, u8 animId)
{
StartSpriteAnim(&gSprites[spriteId], animId);
}
#define SINGLES_TERA_TRIGGER_POS_X_OPTIMAL (30)
#define SINGLES_TERA_TRIGGER_POS_X_PRIORITY (31)
#define SINGLES_TERA_TRIGGER_POS_X_SLIDE (15)
#define SINGLES_TERA_TRIGGER_POS_Y_DIFF (-11)
#define DOUBLES_TERA_TRIGGER_POS_X_OPTIMAL (30)
#define DOUBLES_TERA_TRIGGER_POS_X_PRIORITY (31)
#define DOUBLES_TERA_TRIGGER_POS_X_SLIDE (15)
#define DOUBLES_TERA_TRIGGER_POS_Y_DIFF (-4)
#define tBattler data[0]
#define tHide data[1]
void CreateTeraTriggerSprite(u8 battler, u8 palId)
{
LoadSpritePalette(&sSpritePalette_TeraTrigger);
if (GetSpriteTileStartByTag(TAG_TERA_TRIGGER_TILE) == 0xFFFF)
{
LoadSpriteSheet(&sSpriteSheet_TeraTrigger);
}
if (gBattleStruct->tera.triggerSpriteId == 0xFF)
{
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
gBattleStruct->tera.triggerSpriteId = CreateSprite(&sSpriteTemplate_TeraTrigger,
gSprites[gHealthboxSpriteIds[battler]].x - DOUBLES_TERA_TRIGGER_POS_X_SLIDE,
gSprites[gHealthboxSpriteIds[battler]].y - DOUBLES_TERA_TRIGGER_POS_Y_DIFF, 0);
else
gBattleStruct->tera.triggerSpriteId = CreateSprite(&sSpriteTemplate_TeraTrigger,
gSprites[gHealthboxSpriteIds[battler]].x - SINGLES_TERA_TRIGGER_POS_X_SLIDE,
gSprites[gHealthboxSpriteIds[battler]].y - SINGLES_TERA_TRIGGER_POS_Y_DIFF, 0);
}
gSprites[gBattleStruct->tera.triggerSpriteId].tBattler = battler;
gSprites[gBattleStruct->tera.triggerSpriteId].tHide = FALSE;
ChangeTeraTriggerSprite(gBattleStruct->tera.triggerSpriteId, palId);
}
static void SpriteCb_TeraTrigger(struct Sprite *sprite)
{
s32 xSlide, xPriority, xOptimal;
s32 yDiff;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
{
xSlide = DOUBLES_TERA_TRIGGER_POS_X_SLIDE;
xPriority = DOUBLES_TERA_TRIGGER_POS_X_PRIORITY;
xOptimal = DOUBLES_TERA_TRIGGER_POS_X_OPTIMAL;
yDiff = DOUBLES_TERA_TRIGGER_POS_Y_DIFF;
}
else
{
xSlide = SINGLES_TERA_TRIGGER_POS_X_SLIDE;
xPriority = SINGLES_TERA_TRIGGER_POS_X_PRIORITY;
xOptimal = SINGLES_TERA_TRIGGER_POS_X_OPTIMAL;
yDiff = SINGLES_TERA_TRIGGER_POS_Y_DIFF;
}
if (sprite->tHide)
{
if (sprite->x != gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xSlide)
sprite->x++;
if (sprite->x >= gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xPriority)
sprite->oam.priority = 2;
else
sprite->oam.priority = 1;
sprite->y = gSprites[gHealthboxSpriteIds[sprite->tBattler]].y - yDiff;
sprite->y2 = gSprites[gHealthboxSpriteIds[sprite->tBattler]].y2 - yDiff;
if (sprite->x == gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xSlide)
DestroyTeraTriggerSprite();
}
else
{
if (sprite->x != gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xOptimal)
sprite->x--;
if (sprite->x >= gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xPriority)
sprite->oam.priority = 2;
else
sprite->oam.priority = 1;
sprite->y = gSprites[gHealthboxSpriteIds[sprite->tBattler]].y - yDiff;
sprite->y2 = gSprites[gHealthboxSpriteIds[sprite->tBattler]].y2 - yDiff;
}
}
bool32 IsTeraTriggerSpriteActive(void)
{
if (GetSpriteTileStartByTag(TAG_TERA_TRIGGER_TILE) == 0xFFFF)
return FALSE;
else if (IndexOfSpritePaletteTag(TAG_TERA_TRIGGER_PAL) != 0xFF)
return TRUE;
else
return FALSE;
}
void HideTeraTriggerSprite(void)
{
if (gBattleStruct->tera.triggerSpriteId != 0xFF)
{
ChangeTeraTriggerSprite(gBattleStruct->tera.triggerSpriteId, 0);
gSprites[gBattleStruct->tera.triggerSpriteId].tHide = TRUE;
}
}
void DestroyTeraTriggerSprite(void)
{
FreeSpritePaletteByTag(TAG_TERA_TRIGGER_PAL);
FreeSpriteTilesByTag(TAG_TERA_TRIGGER_TILE);
if (gBattleStruct->tera.triggerSpriteId != 0xFF)
DestroySprite(&gSprites[gBattleStruct->tera.triggerSpriteId]);
gBattleStruct->tera.triggerSpriteId = 0xFF;
}
#undef tBattler
#undef tHide
// TERA INDICATOR:
static const u16 sTeraIndicatorPal[] = INCBIN_U16("graphics/battle_interface/tera_indicator.gbapal");
static const u8 ALIGNED(4) sNormalIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/normal_indicator.4bpp");
static const u8 ALIGNED(4) sFightingIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/fighting_indicator.4bpp");
static const u8 ALIGNED(4) sFlyingIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/flying_indicator.4bpp");
static const u8 ALIGNED(4) sPoisonIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/poison_indicator.4bpp");
static const u8 ALIGNED(4) sGroundIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/ground_indicator.4bpp");
static const u8 ALIGNED(4) sRockIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/rock_indicator.4bpp");
static const u8 ALIGNED(4) sBugIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/bug_indicator.4bpp");
static const u8 ALIGNED(4) sGhostIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/ghost_indicator.4bpp");
static const u8 ALIGNED(4) sSteelIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/steel_indicator.4bpp");
static const u8 ALIGNED(4) sFireIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/fire_indicator.4bpp");
static const u8 ALIGNED(4) sWaterIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/water_indicator.4bpp");
static const u8 ALIGNED(4) sGrassIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/grass_indicator.4bpp");
static const u8 ALIGNED(4) sElectricIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/electric_indicator.4bpp");
static const u8 ALIGNED(4) sPsychicIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/psychic_indicator.4bpp");
static const u8 ALIGNED(4) sIceIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/ice_indicator.4bpp");
static const u8 ALIGNED(4) sDragonIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/dragon_indicator.4bpp");
static const u8 ALIGNED(4) sDarkIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/dark_indicator.4bpp");
static const u8 ALIGNED(4) sFairyIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/fairy_indicator.4bpp");
static const u8 ALIGNED(4) sStellarIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/stellar_indicator.4bpp");
static void SpriteCb_TeraIndicator(struct Sprite *sprite);
static const s8 sIndicatorPositions[][2] =
{
[B_POSITION_PLAYER_LEFT] = {53, -9},
[B_POSITION_OPPONENT_LEFT] = {44, -9},
[B_POSITION_PLAYER_RIGHT] = {52, -9},
[B_POSITION_OPPONENT_RIGHT] = {44, -9},
};
static const struct SpritePalette sSpritePalette_TeraIndicator =
{
sTeraIndicatorPal, TAG_TERA_INDICATOR_PAL
};
static const struct OamData sOamData_TeraIndicator =
{
.shape = SPRITE_SHAPE(16x16),
.size = SPRITE_SIZE(16x16),
.priority = 1,
};
static const struct SpriteTemplate sSpriteTemplate_NormalIndicator =
{
.tileTag = TAG_NORMAL_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_FightingIndicator =
{
.tileTag = TAG_FIGHTING_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_FlyingIndicator =
{
.tileTag = TAG_FLYING_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_PoisonIndicator =
{
.tileTag = TAG_POISON_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_GroundIndicator =
{
.tileTag = TAG_GROUND_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_RockIndicator =
{
.tileTag = TAG_ROCK_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_BugIndicator =
{
.tileTag = TAG_BUG_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_GhostIndicator =
{
.tileTag = TAG_GHOST_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_SteelIndicator =
{
.tileTag = TAG_STEEL_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_FireIndicator =
{
.tileTag = TAG_FIRE_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_WaterIndicator =
{
.tileTag = TAG_WATER_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_GrassIndicator =
{
.tileTag = TAG_GRASS_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_ElectricIndicator =
{
.tileTag = TAG_ELECTRIC_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_PsychicIndicator =
{
.tileTag = TAG_PSYCHIC_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_IceIndicator =
{
.tileTag = TAG_ICE_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_DragonIndicator =
{
.tileTag = TAG_DRAGON_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_DarkIndicator =
{
.tileTag = TAG_DARK_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_FairyIndicator =
{
.tileTag = TAG_FAIRY_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_StellarIndicator =
{
.tileTag = TAG_STELLAR_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteSheet sTeraIndicatorSpriteSheets[NUMBER_OF_MON_TYPES + 1] =
{
{sNormalIndicatorGfx, sizeof(sNormalIndicatorGfx), TAG_NORMAL_INDICATOR_TILE}, // TYPE_NONE
{sNormalIndicatorGfx, sizeof(sNormalIndicatorGfx), TAG_NORMAL_INDICATOR_TILE},
{sFightingIndicatorGfx, sizeof(sFightingIndicatorGfx), TAG_FIGHTING_INDICATOR_TILE},
{sFlyingIndicatorGfx, sizeof(sFlyingIndicatorGfx), TAG_FLYING_INDICATOR_TILE},
{sPoisonIndicatorGfx, sizeof(sPoisonIndicatorGfx), TAG_POISON_INDICATOR_TILE},
{sGroundIndicatorGfx, sizeof(sGroundIndicatorGfx), TAG_GROUND_INDICATOR_TILE},
{sRockIndicatorGfx, sizeof(sRockIndicatorGfx), TAG_ROCK_INDICATOR_TILE},
{sBugIndicatorGfx, sizeof(sBugIndicatorGfx), TAG_BUG_INDICATOR_TILE},
{sGhostIndicatorGfx, sizeof(sGhostIndicatorGfx), TAG_GHOST_INDICATOR_TILE},
{sSteelIndicatorGfx, sizeof(sSteelIndicatorGfx), TAG_STEEL_INDICATOR_TILE},
{sNormalIndicatorGfx, sizeof(sNormalIndicatorGfx), TAG_NORMAL_INDICATOR_TILE}, // TYPE_MYSTERY
{sFireIndicatorGfx, sizeof(sFireIndicatorGfx), TAG_FIRE_INDICATOR_TILE},
{sWaterIndicatorGfx, sizeof(sWaterIndicatorGfx), TAG_WATER_INDICATOR_TILE},
{sGrassIndicatorGfx, sizeof(sGrassIndicatorGfx), TAG_GRASS_INDICATOR_TILE},
{sElectricIndicatorGfx, sizeof(sElectricIndicatorGfx), TAG_ELECTRIC_INDICATOR_TILE},
{sPsychicIndicatorGfx, sizeof(sPsychicIndicatorGfx), TAG_PSYCHIC_INDICATOR_TILE},
{sIceIndicatorGfx, sizeof(sIceIndicatorGfx), TAG_ICE_INDICATOR_TILE},
{sDragonIndicatorGfx, sizeof(sDragonIndicatorGfx), TAG_DRAGON_INDICATOR_TILE},
{sDarkIndicatorGfx, sizeof(sDarkIndicatorGfx), TAG_DARK_INDICATOR_TILE},
{sFairyIndicatorGfx, sizeof(sFairyIndicatorGfx), TAG_FAIRY_INDICATOR_TILE},
{sStellarIndicatorGfx, sizeof(sStellarIndicatorGfx), TAG_STELLAR_INDICATOR_TILE},
{0}
};
static const struct SpriteTemplate * const sTeraIndicatorSpriteTemplates[NUMBER_OF_MON_TYPES] =
{
[TYPE_NONE] = &sSpriteTemplate_NormalIndicator, // just in case
[TYPE_NORMAL] = &sSpriteTemplate_NormalIndicator,
[TYPE_FIGHTING] = &sSpriteTemplate_FightingIndicator,
[TYPE_FLYING] = &sSpriteTemplate_FlyingIndicator,
[TYPE_POISON] = &sSpriteTemplate_PoisonIndicator,
[TYPE_GROUND] = &sSpriteTemplate_GroundIndicator,
[TYPE_ROCK] = &sSpriteTemplate_RockIndicator,
[TYPE_BUG] = &sSpriteTemplate_BugIndicator,
[TYPE_GHOST] = &sSpriteTemplate_GhostIndicator,
[TYPE_STEEL] = &sSpriteTemplate_SteelIndicator,
[TYPE_MYSTERY] = &sSpriteTemplate_NormalIndicator, // just in case
[TYPE_FIRE] = &sSpriteTemplate_FireIndicator,
[TYPE_WATER] = &sSpriteTemplate_WaterIndicator,
[TYPE_GRASS] = &sSpriteTemplate_GrassIndicator,
[TYPE_ELECTRIC] = &sSpriteTemplate_ElectricIndicator,
[TYPE_PSYCHIC] = &sSpriteTemplate_PsychicIndicator,
[TYPE_ICE] = &sSpriteTemplate_IceIndicator,
[TYPE_DRAGON] = &sSpriteTemplate_DragonIndicator,
[TYPE_DARK] = &sSpriteTemplate_DarkIndicator,
[TYPE_FAIRY] = &sSpriteTemplate_FairyIndicator,
[TYPE_STELLAR] = &sSpriteTemplate_StellarIndicator,
};
// for sprite data fields
#define tBattler data[0]
#define tType data[1] // Indicator type: tera
#define tPosX data[2]
#define tLevelXDelta data[3] // X position depends whether level has 3, 2 or 1 digit
// data fields for healthboxMain
// oam.affineParam holds healthboxRight spriteId
#define hMain_TeraIndicatorId data[3]
#define hMain_HealthBarSpriteId data[5]
#define hMain_Battler data[6]
#define hMain_Data7 data[7]
// data fields for healthboxRight
#define hOther_HealthBoxSpriteId data[5]
// data fields for healthbar
#define hBar_HealthBoxSpriteId data[5]
void TeraIndicator_LoadSpriteGfx(void)
{
LoadSpriteSheets(sTeraIndicatorSpriteSheets);
LoadSpritePalette(&sSpritePalette_TeraIndicator);
}
bool32 TeraIndicator_ShouldBeInvisible(u32 battler)
{
return !IsTerastallized(battler);
}
u8 TeraIndicator_GetSpriteId(u32 healthboxSpriteId)
{
return gBattleStruct->tera.indicatorSpriteId[gSprites[healthboxSpriteId].hMain_Battler];
}
void TeraIndicator_SetVisibilities(u32 healthboxId, bool32 invisible)
{
u8 spriteId = TeraIndicator_GetSpriteId(healthboxId);
u32 battler = gSprites[healthboxId].hMain_Battler;
if (GetSafariZoneFlag())
return;
if (invisible == TRUE)
gSprites[spriteId].invisible = TRUE;
else // Try visible.
gSprites[spriteId].invisible = TeraIndicator_ShouldBeInvisible(battler);
}
void TeraIndicator_UpdateOamPriorities(u32 healthboxId, u32 oamPriority)
{
u8 spriteId = TeraIndicator_GetSpriteId(healthboxId);
gSprites[spriteId].oam.priority = oamPriority;
}
void TeraIndicator_UpdateLevel(u32 healthboxId, u32 level)
{
s16 xDelta = 0;
u8 spriteId = TeraIndicator_GetSpriteId(healthboxId);
if (level >= 100)
xDelta -= 4;
else if (level < 10)
xDelta += 5;
gSprites[spriteId].tLevelXDelta = xDelta;
}
void TeraIndicator_CreateSprite(u32 battler, u32 healthboxSpriteId)
{
u32 position;
u8 spriteId;
s16 xHealthbox = 0, y = 0;
s32 x = 0;
u32 type = GetBattlerTeraType(battler);
position = GetBattlerPosition(battler);
GetBattlerHealthboxCoords(battler, &xHealthbox, &y);
x = sIndicatorPositions[position][0];
y += sIndicatorPositions[position][1];
spriteId = gBattleStruct->tera.indicatorSpriteId[battler] = CreateSpriteAtEnd(sTeraIndicatorSpriteTemplates[type], 0, y, 0);
gSprites[spriteId].tBattler = battler;
gSprites[spriteId].tPosX = x;
gSprites[spriteId].invisible = TRUE;
}
void TeraIndicator_DestroySprite(u32 healthboxSpriteId)
{
u8 spriteId = TeraIndicator_GetSpriteId(healthboxSpriteId);
DestroySprite(&gSprites[spriteId]);
}
void TeraIndicator_UpdateType(u32 battler, u32 healthboxSpriteId)
{
TeraIndicator_DestroySprite(healthboxSpriteId);
TeraIndicator_CreateSprite(battler, healthboxSpriteId);
}
static void SpriteCb_TeraIndicator(struct Sprite *sprite)
{
u32 battler = sprite->tBattler;
sprite->x = gSprites[gHealthboxSpriteIds[battler]].x + sprite->tPosX + sprite->tLevelXDelta;
sprite->x2 = gSprites[gHealthboxSpriteIds[battler]].x2;
sprite->y2 = gSprites[gHealthboxSpriteIds[battler]].y2;
}
#undef tBattler
#undef tType
#undef tPosX
#undef tLevelXDelta

View File

@ -8,6 +8,7 @@
#include "battle_interface.h"
#include "battle_setup.h"
#include "battle_z_move.h"
#include "battle_gimmick.h"
#include "party_menu.h"
#include "pokemon.h"
#include "international_string_util.h"
@ -157,7 +158,7 @@ void HandleAction_UseMove(void)
gCurrentMove = gChosenMove = gLockedMoves[gBattlerAttacker];
}
// encore forces you to use the same move
else if (!gBattleStruct->zmove.active && gDisableStructs[gBattlerAttacker].encoredMove != MOVE_NONE
else if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && gDisableStructs[gBattlerAttacker].encoredMove != MOVE_NONE
&& gDisableStructs[gBattlerAttacker].encoredMove == gBattleMons[gBattlerAttacker].moves[gDisableStructs[gBattlerAttacker].encoredMovePos])
{
gCurrentMove = gChosenMove = gDisableStructs[gBattlerAttacker].encoredMove;
@ -165,7 +166,7 @@ void HandleAction_UseMove(void)
*(gBattleStruct->moveTarget + gBattlerAttacker) = GetMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE);
}
// check if the encored move wasn't overwritten
else if (!gBattleStruct->zmove.active && gDisableStructs[gBattlerAttacker].encoredMove != MOVE_NONE
else if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && gDisableStructs[gBattlerAttacker].encoredMove != MOVE_NONE
&& gDisableStructs[gBattlerAttacker].encoredMove != gBattleMons[gBattlerAttacker].moves[gDisableStructs[gBattlerAttacker].encoredMovePos])
{
gCurrMovePos = gChosenMovePos = gDisableStructs[gBattlerAttacker].encoredMovePos;
@ -185,12 +186,6 @@ void HandleAction_UseMove(void)
gCurrentMove = gChosenMove = gBattleMons[gBattlerAttacker].moves[gCurrMovePos];
}
// check z move used
if (gBattleStruct->zmove.toBeUsed[gBattlerAttacker] != MOVE_NONE && !IS_MOVE_STATUS(gCurrentMove))
{
gCurrentMove = gBattleStruct->zmove.toBeUsed[gBattlerAttacker];
}
if (IsBattlerAlive(gBattlerAttacker))
{
if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER)
@ -203,11 +198,17 @@ void HandleAction_UseMove(void)
SetTypeBeforeUsingMove(gChosenMove, gBattlerAttacker);
GET_MOVE_TYPE(gChosenMove, moveType);
// check max move used
if (gBattleStruct->dynamax.usingMaxMove[gBattlerAttacker])
// check Z-Move used
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !IS_MOVE_STATUS(gCurrentMove) && !IsZMove(gCurrentMove))
{
gBattleStruct->categoryOverride = gMovesInfo[gCurrentMove].category;
gCurrentMove = gChosenMove = GetUsableZMove(gBattlerAttacker, gCurrentMove);
}
// check Max Move used
else if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_DYNAMAX)
{
gBattleStruct->categoryOverride = gMovesInfo[gCurrentMove].category;
gCurrentMove = gChosenMove = GetMaxMove(gBattlerAttacker, gCurrentMove);
gBattleStruct->dynamax.activeCategory = gBattleStruct->dynamax.categories[gBattlerAttacker];
}
moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove);
@ -423,6 +424,8 @@ void HandleAction_Switch(void)
if (gBattleResults.playerSwitchesCounter < 255)
gBattleResults.playerSwitchesCounter++;
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_DYNAMAX)
UndoDynamax(gBattlerAttacker); // this is better performed here instead of SwitchInClearSetData
TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_SWITCH);
}
@ -720,7 +723,7 @@ void HandleAction_ActionFinished(void)
// check if Stellar type boost should be used up
GET_MOVE_TYPE(gCurrentMove, moveType);
if (IsTerastallized(gBattlerAttacker)
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA
&& GetBattlerTeraType(gBattlerAttacker) == TYPE_STELLAR
&& gMovesInfo[gCurrentMove].category != DAMAGE_CATEGORY_STATUS
&& IsTypeStellarBoosted(gBattlerAttacker, moveType))
@ -741,7 +744,6 @@ void HandleAction_ActionFinished(void)
gBattleCommunication[4] = 0;
gBattleScripting.multihitMoveEffect = 0;
gBattleResources->battleScriptsStack->size = 0;
gBattleStruct->dynamax.usingMaxMove[gBattlerAttacker] = 0;
if (B_RECALC_TURN_AFTER_ACTIONS >= GEN_8 && !afterYouActive && !gBattleStruct->pledgeMove)
{
@ -1252,17 +1254,17 @@ bool32 IsBelchPreventingMove(u32 battler, u32 move)
}
// Dynamax bypasses all selection prevention except Taunt and Assault Vest.
#define DYNAMAX_BYPASS_CHECK !gBattleStruct->dynamax.playerSelect && !IsDynamaxed(gBattlerAttacker)
#define DYNAMAX_BYPASS_CHECK (!IsGimmickSelected(gBattlerAttacker, GIMMICK_DYNAMAX) && GetActiveGimmick(gBattlerAttacker) != GIMMICK_DYNAMAX)
u32 TrySetCantSelectMoveBattleScript(u32 battler)
{
u32 limitations = 0;
u8 moveId = gBattleResources->bufferB[battler][2] & ~(RET_MEGA_EVOLUTION | RET_ULTRA_BURST | RET_DYNAMAX | RET_TERASTAL);
u8 moveId = gBattleResources->bufferB[battler][2] & ~RET_GIMMICK;
u32 move = gBattleMons[battler].moves[moveId];
u32 holdEffect = GetBattlerHoldEffect(battler, TRUE);
u16 *choicedMove = &gBattleStruct->choicedMove[battler];
if (DYNAMAX_BYPASS_CHECK && gBattleStruct->zmove.toBeUsed[gBattlerAttacker] == MOVE_NONE && gDisableStructs[battler].disabledMove == move && move != MOVE_NONE)
if (DYNAMAX_BYPASS_CHECK && GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && gDisableStructs[battler].disabledMove == move && move != MOVE_NONE)
{
gBattleScripting.battler = battler;
gCurrentMove = move;
@ -1278,7 +1280,7 @@ u32 TrySetCantSelectMoveBattleScript(u32 battler)
}
}
if (DYNAMAX_BYPASS_CHECK && gBattleStruct->zmove.toBeUsed[gBattlerAttacker] == MOVE_NONE && move == gLastMoves[battler] && move != MOVE_STRUGGLE && (gBattleMons[battler].status2 & STATUS2_TORMENT))
if (DYNAMAX_BYPASS_CHECK && GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && move == gLastMoves[battler] && move != MOVE_STRUGGLE && (gBattleMons[battler].status2 & STATUS2_TORMENT))
{
CancelMultiTurnMoves(battler);
if (gBattleTypeFlags & BATTLE_TYPE_PALACE)
@ -1293,9 +1295,9 @@ u32 TrySetCantSelectMoveBattleScript(u32 battler)
}
}
if (gBattleStruct->zmove.toBeUsed[gBattlerAttacker] == MOVE_NONE && gDisableStructs[battler].tauntTimer != 0 && IS_MOVE_STATUS(move))
if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && gDisableStructs[battler].tauntTimer != 0 && IS_MOVE_STATUS(move))
{
if (IsDynamaxed(gBattlerAttacker))
if ((GetActiveGimmick(gBattlerAttacker) == GIMMICK_DYNAMAX))
gCurrentMove = MOVE_MAX_GUARD;
else
gCurrentMove = move;
@ -1311,7 +1313,7 @@ u32 TrySetCantSelectMoveBattleScript(u32 battler)
}
}
if (DYNAMAX_BYPASS_CHECK && gBattleStruct->zmove.toBeUsed[gBattlerAttacker] == MOVE_NONE && gDisableStructs[battler].throatChopTimer != 0 && gMovesInfo[move].soundMove)
if (DYNAMAX_BYPASS_CHECK && GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && gDisableStructs[battler].throatChopTimer != 0 && gMovesInfo[move].soundMove)
{
gCurrentMove = move;
if (gBattleTypeFlags & BATTLE_TYPE_PALACE)
@ -1326,7 +1328,7 @@ u32 TrySetCantSelectMoveBattleScript(u32 battler)
}
}
if (DYNAMAX_BYPASS_CHECK && gBattleStruct->zmove.toBeUsed[gBattlerAttacker] == MOVE_NONE && GetImprisonedMovesCount(battler, move))
if (DYNAMAX_BYPASS_CHECK && GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && GetImprisonedMovesCount(battler, move))
{
gCurrentMove = move;
if (gBattleTypeFlags & BATTLE_TYPE_PALACE)
@ -1341,7 +1343,7 @@ u32 TrySetCantSelectMoveBattleScript(u32 battler)
}
}
if (DYNAMAX_BYPASS_CHECK && gBattleStruct->zmove.toBeUsed[gBattlerAttacker] == MOVE_NONE && IsGravityPreventingMove(move))
if (DYNAMAX_BYPASS_CHECK && GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && IsGravityPreventingMove(move))
{
gCurrentMove = move;
if (gBattleTypeFlags & BATTLE_TYPE_PALACE)
@ -1356,7 +1358,7 @@ u32 TrySetCantSelectMoveBattleScript(u32 battler)
}
}
if (DYNAMAX_BYPASS_CHECK && gBattleStruct->zmove.toBeUsed[gBattlerAttacker] == MOVE_NONE && IsHealBlockPreventingMove(battler, move))
if (DYNAMAX_BYPASS_CHECK && GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && IsHealBlockPreventingMove(battler, move))
{
gCurrentMove = move;
if (gBattleTypeFlags & BATTLE_TYPE_PALACE)
@ -1371,7 +1373,7 @@ u32 TrySetCantSelectMoveBattleScript(u32 battler)
}
}
if (DYNAMAX_BYPASS_CHECK && gBattleStruct->zmove.toBeUsed[gBattlerAttacker] == MOVE_NONE && IsBelchPreventingMove(battler, move))
if (DYNAMAX_BYPASS_CHECK && GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && IsBelchPreventingMove(battler, move))
{
gCurrentMove = move;
if (gBattleTypeFlags & BATTLE_TYPE_PALACE)
@ -1435,7 +1437,7 @@ u32 TrySetCantSelectMoveBattleScript(u32 battler)
}
else if (holdEffect == HOLD_EFFECT_ASSAULT_VEST && IS_MOVE_STATUS(move) && gMovesInfo[move].effect != EFFECT_ME_FIRST)
{
if (IsDynamaxed(gBattlerAttacker))
if ((GetActiveGimmick(gBattlerAttacker) == GIMMICK_DYNAMAX))
gCurrentMove = MOVE_MAX_GUARD;
else
gCurrentMove = move;
@ -2918,7 +2920,7 @@ u8 DoBattlerEndTurnEffects(void)
gBattleStruct->turnEffectsTracker++;
break;
case ENDTURN_DYNAMAX:
if (IsDynamaxed(battler)
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX
&& --gBattleStruct->dynamax.dynamaxTurns[battler] == 0)
{
gBattleScripting.battler = battler;
@ -3297,7 +3299,7 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType)
gBattleStruct->atkCancellerTracker++;
break;
case CANCELLER_DISABLED: // disabled move
if (gBattleStruct->zmove.toBeUsed[gBattlerAttacker] == MOVE_NONE && gDisableStructs[gBattlerAttacker].disabledMove == gCurrentMove && gDisableStructs[gBattlerAttacker].disabledMove != MOVE_NONE)
if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && gDisableStructs[gBattlerAttacker].disabledMove == gCurrentMove && gDisableStructs[gBattlerAttacker].disabledMove != MOVE_NONE)
{
gProtectStructs[gBattlerAttacker].usedDisabledMove = TRUE;
gBattleScripting.battler = gBattlerAttacker;
@ -3309,7 +3311,7 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType)
gBattleStruct->atkCancellerTracker++;
break;
case CANCELLER_HEAL_BLOCKED:
if (gBattleStruct->zmove.toBeUsed[gBattlerAttacker] == MOVE_NONE && gStatuses3[gBattlerAttacker] & STATUS3_HEAL_BLOCK && IsHealBlockPreventingMove(gBattlerAttacker, gCurrentMove))
if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && gStatuses3[gBattlerAttacker] & STATUS3_HEAL_BLOCK && IsHealBlockPreventingMove(gBattlerAttacker, gCurrentMove))
{
gProtectStructs[gBattlerAttacker].usedHealBlockedMove = TRUE;
gBattleScripting.battler = gBattlerAttacker;
@ -3333,7 +3335,7 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType)
gBattleStruct->atkCancellerTracker++;
break;
case CANCELLER_TAUNTED: // taunt
if (gBattleStruct->zmove.toBeUsed[gBattlerAttacker] == MOVE_NONE && gDisableStructs[gBattlerAttacker].tauntTimer && IS_MOVE_STATUS(gCurrentMove))
if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && gDisableStructs[gBattlerAttacker].tauntTimer && IS_MOVE_STATUS(gCurrentMove))
{
gProtectStructs[gBattlerAttacker].usedTauntedMove = TRUE;
CancelMultiTurnMoves(gBattlerAttacker);
@ -3344,7 +3346,7 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType)
gBattleStruct->atkCancellerTracker++;
break;
case CANCELLER_IMPRISONED: // imprisoned
if (gBattleStruct->zmove.toBeUsed[gBattlerAttacker] == MOVE_NONE && GetImprisonedMovesCount(gBattlerAttacker, gCurrentMove))
if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && GetImprisonedMovesCount(gBattlerAttacker, gCurrentMove))
{
gProtectStructs[gBattlerAttacker].usedImprisonedMove = TRUE;
CancelMultiTurnMoves(gBattlerAttacker);
@ -3508,7 +3510,7 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType)
gBattleStruct->atkCancellerTracker++;
break;
case CANCELLER_THROAT_CHOP:
if (gBattleStruct->zmove.toBeUsed[gBattlerAttacker] == MOVE_NONE && gDisableStructs[gBattlerAttacker].throatChopTimer && gMovesInfo[gCurrentMove].soundMove)
if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && gDisableStructs[gBattlerAttacker].throatChopTimer && gMovesInfo[gCurrentMove].soundMove)
{
gProtectStructs[gBattlerAttacker].usedThroatChopPreventedMove = TRUE;
CancelMultiTurnMoves(gBattlerAttacker);
@ -3519,23 +3521,18 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType)
gBattleStruct->atkCancellerTracker++;
break;
case CANCELLER_Z_MOVES:
if (gBattleStruct->zmove.toBeUsed[gBattlerAttacker] != MOVE_NONE)
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE)
{
// For Z-Mirror Move, so it doesn't play the animation twice.
bool32 alreadyUsed = (gBattleStruct->zmove.used[gBattlerAttacker] == TRUE);
bool32 alreadyUsed = HasTrainerUsedGimmick(gBattlerAttacker, GIMMICK_Z_MOVE);
//attacker has a queued z move
gBattleStruct->zmove.active = TRUE;
gBattleStruct->zmove.activeCategory = gBattleStruct->zmove.categories[gBattlerAttacker];
// attacker has a queued z move
RecordItemEffectBattle(gBattlerAttacker, HOLD_EFFECT_Z_CRYSTAL);
gBattleStruct->zmove.used[gBattlerAttacker] = TRUE;
if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && IsPartnerMonFromSameTrainer(gBattlerAttacker))
gBattleStruct->zmove.used[BATTLE_PARTNER(gBattlerAttacker)] = TRUE; //if 1v1 double, set partner used flag as well
SetGimmickAsActivated(gBattlerAttacker, GIMMICK_Z_MOVE);
gBattleScripting.battler = gBattlerAttacker;
if (gBattleStruct->zmove.activeCategory == DAMAGE_CATEGORY_STATUS)
if (gMovesInfo[gCurrentMove].category == DAMAGE_CATEGORY_STATUS)
{
gBattleStruct->zmove.effect = gMovesInfo[gBattleStruct->zmove.baseMoves[gBattlerAttacker]].zMove.effect;
if (!alreadyUsed)
{
BattleScriptPushCursor();
@ -5341,7 +5338,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
&& IsBattlerAlive(gBattlerAttacker)
&& !IsAbilityOnSide(gBattlerAttacker, ABILITY_AROMA_VEIL)
&& gBattleMons[gBattlerAttacker].pp[gChosenMovePos] != 0
&& !IsDynamaxed(gBattlerAttacker) // TODO: Max Moves don't make contact, useless?
&& !(GetActiveGimmick(gBattlerAttacker) == GIMMICK_DYNAMAX) // TODO: Max Moves don't make contact, useless?
&& (Random() % 3) == 0)
{
gDisableStructs[gBattlerAttacker].disabledMove = gChosenMove;
@ -5383,7 +5380,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
&& TARGET_TURN_DAMAGED
&& GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_PROTECTIVE_PADS
&& IsMoveMakingContact(move, gBattlerAttacker)
&& !IsDynamaxed(gBattlerTarget)
&& !(GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX)
&& !gAbilitiesInfo[gBattleMons[gBattlerAttacker].ability].cantBeSwapped)
{
if (GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_ABILITY_SHIELD)
@ -8418,7 +8415,7 @@ bool32 IsBattlerProtected(u32 battlerAtk, u32 battlerDef, u32 move)
}
// Z-Moves and Max Moves bypass protection (except Max Guard).
if ((IsMaxMove(move) || gBattleStruct->zmove.active)
if ((IsZMove(move) || IsMaxMove(move))
&& (!gProtectStructs[battlerDef].maxGuarded
|| gMovesInfo[move].argument == MAX_EFFECT_BYPASS_PROTECT))
return FALSE;
@ -8715,7 +8712,7 @@ static inline u32 CalcMoveBasePower(u32 move, u32 battlerAtk, u32 battlerDef, u3
u32 basePower = gMovesInfo[move].power;
u32 weight, hpFraction, speed;
if (gBattleStruct->zmove.active)
if (GetActiveGimmick(battlerAtk) == GIMMICK_Z_MOVE)
return GetZMovePower(gBattleStruct->zmove.baseMoves[battlerAtk]);
switch (gMovesInfo[move].effect)
@ -8895,7 +8892,7 @@ static inline u32 CalcMoveBasePower(u32 move, u32 battlerAtk, u32 battlerDef, u3
basePower = uq4_12_multiply(basePower, UQ_4_12(1.5));
break;
case EFFECT_DYNAMAX_DOUBLE_DMG:
if (IsDynamaxed(battlerDef))
if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
basePower *= 2;
break;
case EFFECT_HIDDEN_POWER:
@ -8950,7 +8947,7 @@ static inline u32 CalcMoveBasePower(u32 move, u32 battlerAtk, u32 battlerDef, u3
basePower *= 2;
break;
case EFFECT_TERA_BLAST:
if (IsTerastallized(battlerAtk) && GetBattlerTeraType(battlerAtk) == TYPE_STELLAR)
if (GetActiveGimmick(battlerAtk) == GIMMICK_TERA && GetBattlerTeraType(battlerAtk) == TYPE_STELLAR)
basePower = 100;
break;
case EFFECT_LAST_RESPECTS:
@ -9322,7 +9319,7 @@ static inline u32 CalcMoveBasePowerAfterModifiers(u32 move, u32 battlerAtk, u32
}
// Terastallization boosts weak, non-priority, non-multi hit moves after modifiers to 60 BP.
if (IsTerastallized(battlerAtk)
if (GetActiveGimmick(battlerAtk) == GIMMICK_TERA
&& (moveType == GetBattlerTeraType(battlerAtk)
|| (GetBattlerTeraType(battlerAtk) == TYPE_STELLAR && IsTypeStellarBoosted(battlerAtk, moveType)))
&& uq4_12_multiply_by_int_half_down(modifier, basePower) < 60
@ -9526,11 +9523,11 @@ static inline u32 CalcAttackStat(u32 move, u32 battlerAtk, u32 battlerDef, u32 m
modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(2.0));
break;
case HOLD_EFFECT_CHOICE_BAND:
if (IS_MOVE_PHYSICAL(move) && !IsDynamaxed(battlerAtk))
if (IS_MOVE_PHYSICAL(move) && GetActiveGimmick(battlerAtk) != GIMMICK_DYNAMAX)
modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5));
break;
case HOLD_EFFECT_CHOICE_SPECS:
if (IS_MOVE_SPECIAL(move) && !IsDynamaxed(battlerAtk))
if (IS_MOVE_SPECIAL(move) && GetActiveGimmick(battlerAtk) != GIMMICK_DYNAMAX)
modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5));
break;
}
@ -9798,7 +9795,7 @@ static inline uq4_12_t GetGlaiveRushModifier(u32 battlerDef)
static inline uq4_12_t GetZMaxMoveAgainstProtectionModifier(u32 battlerDef, u32 move)
{
if ((gBattleStruct->zmove.active || IsMaxMove(move)) && IS_BATTLER_PROTECTED(battlerDef))
if ((IsZMove(move) || IsMaxMove(move)) && IS_BATTLER_PROTECTED(battlerDef))
return UQ_4_12(0.25);
return UQ_4_12(1.0);
}
@ -10039,7 +10036,7 @@ static inline s32 DoMoveDamageCalcVars(u32 move, u32 battlerAtk, u32 battlerDef,
dmg /= 100;
}
if (IsTerastallized(battlerAtk))
if (GetActiveGimmick(battlerAtk) == GIMMICK_TERA)
DAMAGE_APPLY_MODIFIER(GetTeraMultiplier(battlerAtk, moveType));
else
DAMAGE_APPLY_MODIFIER(GetSameTypeAttackBonusModifier(battlerAtk, moveType, move, abilityAtk));
@ -10197,7 +10194,7 @@ static inline void MulByTypeEffectiveness(uq4_12_t *modifier, u32 move, u32 move
mod = UQ_4_12(2.0);
if (moveType == TYPE_GROUND && defType == TYPE_FLYING && IsBattlerGrounded(battlerDef) && mod == UQ_4_12(0.0))
mod = UQ_4_12(1.0);
if (moveType == TYPE_STELLAR && IsTerastallized(battlerDef))
if (moveType == TYPE_STELLAR && GetActiveGimmick(battlerDef) == GIMMICK_TERA)
mod = UQ_4_12(2.0);
// B_WEATHER_STRONG_WINDS weakens Super Effective moves against Flying-type Pokémon
@ -10293,7 +10290,7 @@ static inline uq4_12_t CalcTypeEffectivenessMultiplierInternal(u32 move, u32 mov
// Thousand Arrows ignores type modifiers for flying mons
if (!IsBattlerGrounded(battlerDef) && (gMovesInfo[move].ignoreTypeIfFlyingAndUngrounded)
&& (gBattleMons[battlerDef].type1 == TYPE_FLYING || gBattleMons[battlerDef].type2 == TYPE_FLYING || gBattleMons[battlerDef].type3 == TYPE_FLYING))
&& (GetBattlerType(battlerDef, 0, FALSE) == TYPE_FLYING || GetBattlerType(battlerDef, 1, FALSE) == TYPE_FLYING || GetBattlerType(battlerDef, 2, FALSE) == TYPE_FLYING))
{
modifier = UQ_4_12(1.0);
}
@ -10502,62 +10499,37 @@ bool32 DoesSpeciesUseHoldItemToChangeForm(u16 species, u16 heldItemId)
bool32 CanMegaEvolve(u32 battler)
{
u32 itemId, holdEffect;
struct Pokemon *mon;
u32 battlerPosition = GetBattlerPosition(battler);
u8 partnerPosition = GetBattlerPosition(BATTLE_PARTNER(battler));
struct MegaEvolutionData *mega = &(((struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]))->mega);
u32 holdEffect = GetBattlerHoldEffect(battler, FALSE);
// Check if Player has a Mega Ring
if ((GetBattlerPosition(battler) == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && GetBattlerPosition(battler) == B_POSITION_PLAYER_RIGHT))
&& !CheckBagHasItem(ITEM_MEGA_RING, 1))
// Check if Player has a Mega Ring.
if (!TESTING
&& (GetBattlerPosition(battler) == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && GetBattlerPosition(battler) == B_POSITION_PLAYER_RIGHT))
&& !CheckBagHasItem(ITEM_MEGA_RING, 1))
return FALSE;
// Check if trainer already mega evolved a pokemon.
if (mega->alreadyEvolved[battlerPosition])
// Check if Trainer has already Mega Evolved.
if (HasTrainerUsedGimmick(battler, GIMMICK_MEGA))
return FALSE;
// Cannot use z move and mega evolve on same turn
if (gBattleStruct->zmove.toBeUsed[battler])
// Check if battler has another gimmick active.
if (GetActiveGimmick(battler) != GIMMICK_NONE)
return FALSE;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE
&& IsPartnerMonFromSameTrainer(battler)
&& (mega->alreadyEvolved[partnerPosition] || (mega->toEvolve & gBitTable[BATTLE_PARTNER(battler)])))
return FALSE;
// Check if mon is currently held by Sky Drop
// Check if battler is currently held by Sky Drop.
if (gStatuses3[battler] & STATUS3_SKY_DROPPED)
return FALSE;
// Gets mon data.
if (GetBattlerSide(battler) == B_SIDE_OPPONENT)
mon = &gEnemyParty[gBattlerPartyIndexes[battler]];
else
mon = &gPlayerParty[gBattlerPartyIndexes[battler]];
// Check if battler is holding a Z-Crystal.
if (holdEffect == HOLD_EFFECT_Z_CRYSTAL)
return FALSE;
itemId = GetMonData(mon, MON_DATA_HELD_ITEM);
// Check if there is an entry in the form change table for regular Mega Evolution and battler is holding Mega Stone.
if (GetBattleFormChangeTargetSpecies(battler, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM) != SPECIES_NONE && holdEffect == HOLD_EFFECT_MEGA_STONE)
return TRUE;
if (itemId == ITEM_ENIGMA_BERRY_E_READER)
holdEffect = gEnigmaBerries[battler].holdEffect;
else
holdEffect = ItemId_GetHoldEffect(itemId);
// Check if there is an entry in the evolution table for regular Mega Evolution.
if (GetBattleFormChangeTargetSpecies(battler, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM) != SPECIES_NONE)
{
// Can Mega Evolve via Mega Stone.
if (holdEffect == HOLD_EFFECT_MEGA_STONE)
return TRUE;
}
// Check if there is an entry in the evolution table for Wish Mega Evolution.
// Check if there is an entry in the form change table for Wish Mega Evolution.
if (GetBattleFormChangeTargetSpecies(battler, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_MOVE) != SPECIES_NONE)
{
// Can't Wish Mega Evolve if holding a Z Crystal.
if (holdEffect != HOLD_EFFECT_Z_CRYSTAL)
return TRUE;
}
return TRUE;
// No checks passed, the mon CAN'T mega evolve.
return FALSE;
@ -10565,58 +10537,51 @@ bool32 CanMegaEvolve(u32 battler)
bool32 CanUltraBurst(u32 battler)
{
u32 itemId, holdEffect;
struct Pokemon *mon;
u32 battlerPosition = GetBattlerPosition(battler);
u8 partnerPosition = GetBattlerPosition(BATTLE_PARTNER(battler));
u32 holdEffect = GetBattlerHoldEffect(battler, FALSE);
// Check if Player has a Z Ring
if ((GetBattlerPosition(battler) == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && GetBattlerPosition(battler) == B_POSITION_PLAYER_RIGHT))
&& !CheckBagHasItem(ITEM_Z_POWER_RING, 1))
// Check if Player has a Z-Ring
if (!TESTING && (GetBattlerPosition(battler) == B_POSITION_PLAYER_LEFT
|| (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && GetBattlerPosition(battler) == B_POSITION_PLAYER_RIGHT))
&& !CheckBagHasItem(ITEM_Z_POWER_RING, 1))
return FALSE;
// Check if trainer already ultra bursted a pokemon.
if (gBattleStruct->burst.alreadyBursted[battlerPosition])
// Check if Trainer has already Ultra Bursted.
if (HasTrainerUsedGimmick(battler, GIMMICK_ULTRA_BURST))
return FALSE;
// Cannot use z move and ultra burst on same turn
if (gBattleStruct->zmove.toBeUsed[battler])
return FALSE;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE
&& IsPartnerMonFromSameTrainer(battler)
&& (gBattleStruct->burst.alreadyBursted[partnerPosition] || (gBattleStruct->burst.toBurst & gBitTable[BATTLE_PARTNER(battler)])))
// Check if battler has another gimmick active.
if (GetActiveGimmick(battler) != GIMMICK_NONE)
return FALSE;
// Check if mon is currently held by Sky Drop
if (gStatuses3[battler] & STATUS3_SKY_DROPPED)
return FALSE;
// Gets mon data.
if (GetBattlerSide(battler) == B_SIDE_OPPONENT)
mon = &gEnemyParty[gBattlerPartyIndexes[battler]];
else
mon = &gPlayerParty[gBattlerPartyIndexes[battler]];
itemId = GetMonData(mon, MON_DATA_HELD_ITEM);
// Check if there is an entry in the evolution table for Ultra Burst.
if (GetBattleFormChangeTargetSpecies(battler, FORM_CHANGE_BATTLE_ULTRA_BURST) != SPECIES_NONE)
{
if (itemId == ITEM_ENIGMA_BERRY_E_READER)
holdEffect = gEnigmaBerries[battler].holdEffect;
else
holdEffect = ItemId_GetHoldEffect(itemId);
// Can Ultra Burst via Z Crystal.
if (holdEffect == HOLD_EFFECT_Z_CRYSTAL)
return TRUE;
}
// Check if there is an entry in the form change table for Ultra Burst and battler is holding a Z-Crystal.
if (GetBattleFormChangeTargetSpecies(battler, FORM_CHANGE_BATTLE_ULTRA_BURST) != SPECIES_NONE && holdEffect == HOLD_EFFECT_Z_CRYSTAL)
return TRUE;
// No checks passed, the mon CAN'T ultra burst.
return FALSE;
}
void ActivateMegaEvolution(u32 battler)
{
gLastUsedItem = gBattleMons[battler].item;
SetActiveGimmick(battler, GIMMICK_MEGA);
if (GetBattleFormChangeTargetSpecies(battler, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_MOVE) != SPECIES_NONE)
BattleScriptExecute(BattleScript_WishMegaEvolution);
else
BattleScriptExecute(BattleScript_MegaEvolution);
}
void ActivateUltraBurst(u32 battler)
{
gLastUsedItem = gBattleMons[battler].item;
SetActiveGimmick(battler, GIMMICK_ULTRA_BURST);
BattleScriptExecute(BattleScript_UltraBurst);
}
bool32 IsBattlerMegaEvolved(u32 battler)
{
// While Transform does copy stats and visuals, it shouldn't be counted as true Mega Evolution.
@ -10953,12 +10918,10 @@ bool32 ShouldGetStatBadgeBoost(u16 badgeFlag, u32 battler)
u8 GetBattleMoveCategory(u32 moveId)
{
if (gBattleStruct != NULL && gBattleStruct->zmove.active && !IS_MOVE_STATUS(moveId))
return gBattleStruct->zmove.activeCategory;
if (gBattleStruct != NULL && IsMaxMove(moveId)) // TODO: Might be buggy depending on when this is called.
return gBattleStruct->dynamax.activeCategory;
if (gBattleStruct != NULL && gBattleStruct->swapDamageCategory) // Photon Geyser, Shell Side Arm, Light That Burns the Sky
if (gBattleStruct != NULL && gBattleStruct->swapDamageCategory) // Photon Geyser, Shell Side Arm, Light That Burns the Sky, Tera Blast
return DAMAGE_CATEGORY_PHYSICAL;
if (gBattleStruct != NULL && (IsZMove(moveId) || IsMaxMove(moveId))) // TODO: Might be buggy depending on when this is called.
return gBattleStruct->categoryOverride;
if (B_PHYSICAL_SPECIAL_SPLIT >= GEN_4)
return gMovesInfo[moveId].category;
@ -11343,7 +11306,7 @@ bool32 CanTargetBattler(u32 battlerAtk, u32 battlerDef, u16 move)
&& GetBattlerSide(battlerAtk) == GetBattlerSide(battlerDef)
&& gStatuses3[battlerAtk] & STATUS3_HEAL_BLOCK)
return FALSE; // Pokémon affected by Heal Block cannot target allies with Pollen Puff
if ((IsDynamaxed(battlerAtk) || gBattleStruct->dynamax.playerSelect)
if ((GetActiveGimmick(battlerAtk) == GIMMICK_DYNAMAX || IsGimmickSelected(battlerAtk, GIMMICK_DYNAMAX))
&& GetBattlerSide(battlerAtk) == GetBattlerSide(battlerDef))
return FALSE;
return TRUE;
@ -11382,7 +11345,7 @@ void CopyMonAbilityAndTypesToBattleMon(u32 battler, struct Pokemon *mon)
void RecalcBattlerStats(u32 battler, struct Pokemon *mon)
{
CalculateMonStats(mon);
if (IsDynamaxed(battler) && gChosenActionByBattler[battler] != B_ACTION_SWITCH)
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX && gChosenActionByBattler[battler] != B_ACTION_SWITCH)
ApplyDynamaxHPMultiplier(battler, mon);
CopyMonLevelAndBaseStatsToBattleMon(battler, mon);
CopyMonAbilityAndTypesToBattleMon(battler, mon);
@ -11586,14 +11549,14 @@ u8 GetBattlerType(u32 battler, u8 typeIndex, bool32 ignoreTera)
types[2] = gBattleMons[battler].type3;
// Handle Terastallization
if (IsTerastallized(battler) && teraType != TYPE_STELLAR && !ignoreTera)
if (GetActiveGimmick(battler) == GIMMICK_TERA && teraType != TYPE_STELLAR && !ignoreTera)
return GetBattlerTeraType(battler);
// Handle Roost's Flying-type suppression
if (typeIndex == 0 || typeIndex == 1)
{
if (gBattleResources->flags->flags[battler] & RESOURCE_FLAG_ROOST
&& !IsTerastallized(battler))
&& GetActiveGimmick(battler) != GIMMICK_TERA)
{
if (types[0] == TYPE_FLYING && types[1] == TYPE_FLYING)
return B_ROOST_PURE_FLYING >= GEN_5 ? TYPE_NORMAL : TYPE_MYSTERY;
@ -11608,7 +11571,7 @@ u8 GetBattlerType(u32 battler, u8 typeIndex, bool32 ignoreTera)
void RemoveBattlerType(u32 battler, u8 type)
{
u32 i;
if (IsTerastallized(battler)) // don't remove type if Terastallized
if (GetActiveGimmick(battler) == GIMMICK_TERA) // don't remove type if Terastallized
return;
for (i = 0; i < 3; i++)
{

View File

@ -45,12 +45,8 @@
#define STAT_STAGE(battler, stat) (gBattleMons[battler].statStages[stat - 1])
// Function Declarations
static void SpriteCB_ZMoveTrigger(struct Sprite *sprite);
static u16 GetSignatureZMove(u16 move, u16 species, u16 item);
static void ZMoveSelectionDisplayPpNumber(u32 battler);
static void ZMoveSelectionDisplayPower(u16 move, u16 zMove);
static void ShowZMoveTriggerSprite(u8 battleId);
static bool32 AreStatsMaxed(u8 battler, u8 n);
static void ZMoveSelectionDisplayMoveType(u16 zMove, u32 battler);
// Const Data
@ -108,260 +104,130 @@ static const u8 sText_RecoverHP[] = _("Recover HP");
static const u8 sText_HealAllyHP[] = _("Heal Replacement HP");
static const u8 sText_PowerColon[] = _("Power: ");
static const u32 sZMoveTriggerGfx[] = INCBIN_U32("graphics/battle_interface/z_move_trigger.4bpp.lz");
static const u16 sZMoveTriggerPal[] = INCBIN_U16("graphics/battle_interface/z_move_trigger.gbapal");
static const struct CompressedSpriteSheet sSpriteSheet_ZMoveTrigger = {
sZMoveTriggerGfx, (32 * 32) / 2, TAG_ZMOVE_TRIGGER_TILE
};
static const struct SpritePalette sSpritePalette_ZMoveTrigger = {
sZMoveTriggerPal, TAG_ZMOVE_TRIGGER_PAL
};
static const struct OamData sOamData_ZMoveTrigger =
{
.affineMode = ST_OAM_AFFINE_OFF,
.objMode = ST_OAM_OBJ_NORMAL,
.shape = SPRITE_SHAPE(32x32),
.size = SPRITE_SIZE(32x32),
.priority = 1,
};
static const struct SpriteTemplate sSpriteTemplate_ZMoveTrigger =
{
.tileTag = TAG_ZMOVE_TRIGGER_TILE,
.paletteTag = TAG_ZMOVE_TRIGGER_PAL,
.oam = &sOamData_ZMoveTrigger,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCB_ZMoveTrigger
};
// Functions
bool8 IsZMove(u16 move)
bool32 IsZMove(u32 move)
{
return move >= FIRST_Z_MOVE && move <= LAST_Z_MOVE;
}
void QueueZMove(u8 battler, u16 baseMove)
bool32 CanUseZMove(u32 battler)
{
gBattleStruct->zmove.toBeUsed[battler] = gBattleStruct->zmove.chosenZMove;
gBattleStruct->zmove.baseMoves[battler] = baseMove;
if (gBattleStruct->zmove.chosenZMove == MOVE_LIGHT_THAT_BURNS_THE_SKY)
gBattleStruct->zmove.categories[battler] = GetCategoryBasedOnStats(battler);
else
gBattleStruct->zmove.categories[battler] = gMovesInfo[baseMove].category;
u32 holdEffect = GetBattlerHoldEffect(battler, FALSE);
// Check if Player has Z-Power Ring.
if (!TESTING && (battler == B_POSITION_PLAYER_LEFT
|| (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && battler == B_POSITION_PLAYER_RIGHT))
&& !CheckBagHasItem(ITEM_Z_POWER_RING, 1))
return FALSE;
// Add '| BATTLE_TYPE_FRONTIER' to below if issues occur
if (gBattleTypeFlags & (BATTLE_TYPE_SAFARI | BATTLE_TYPE_WALLY_TUTORIAL))
return FALSE;
// Check if Trainer has already used a Z-Move.
if (HasTrainerUsedGimmick(battler, GIMMICK_Z_MOVE))
return FALSE;
// Check if battler has another gimmick active.
if (GetActiveGimmick(battler) != GIMMICK_NONE && GetActiveGimmick(battler) != GIMMICK_ULTRA_BURST)
return FALSE;
// Check if battler isn't holding a Z-Crystal.
if (holdEffect != HOLD_EFFECT_Z_CRYSTAL)
return FALSE;
// All checks passed!
return TRUE;
}
bool32 IsViableZMove(u8 battler, u16 move)
u32 GetUsableZMove(u32 battler, u32 move)
{
u32 item = gBattleMons[battler].item;
u32 holdEffect = GetBattlerHoldEffect(battler, FALSE);
if (holdEffect == HOLD_EFFECT_Z_CRYSTAL)
{
u16 zMove = GetSignatureZMove(move, gBattleMons[battler].species, item);
if (zMove != MOVE_NONE)
return zMove; // Signature z move exists
if (move != MOVE_NONE && zMove != MOVE_Z_STATUS && gMovesInfo[move].type == ItemId_GetSecondaryId(item))
return GetTypeBasedZMove(move);
}
return MOVE_NONE;
}
void ActivateZMove(u32 battler)
{
gBattleStruct->zmove.baseMoves[battler] = gBattleMons[battler].moves[gBattleStruct->chosenMovePositions[battler]];
SetActiveGimmick(battler, GIMMICK_Z_MOVE);
}
bool32 IsViableZMove(u32 battler, u32 move)
{
u32 item;
u16 holdEffect;
u32 holdEffect = GetBattlerHoldEffect(battler, FALSE);
int moveSlotIndex;
item = gBattleMons[battler].item;
if (gBattleStruct->gimmick.usableGimmick[battler] != GIMMICK_Z_MOVE)
return FALSE;
for (moveSlotIndex = 0; moveSlotIndex < MAX_MON_MOVES; moveSlotIndex++)
{
if (gBattleMons[battler].moves[moveSlotIndex] == move && gBattleMons[battler].pp[moveSlotIndex] == 0)
return FALSE;
}
if (gBattleStruct->zmove.used[battler])
// Check if Player has Z-Power Ring.
if ((battler == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && battler == B_POSITION_PLAYER_RIGHT))
&& !CheckBagHasItem(ITEM_Z_POWER_RING, 1))
{
return FALSE;
}
// Add '| BATTLE_TYPE_FRONTIER' to below if issues occur
if (gBattleTypeFlags & (BATTLE_TYPE_SAFARI | BATTLE_TYPE_WALLY_TUTORIAL))
return FALSE;
if ((GetBattlerPosition(battler) == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && GetBattlerPosition(battler) == B_POSITION_PLAYER_RIGHT)) && !CheckBagHasItem(ITEM_Z_POWER_RING, 1))
return FALSE;
if (item == ITEM_ENIGMA_BERRY_E_READER)
return FALSE; // HoldEffect = gEnigmaBerries[battler].holdEffect;
else
holdEffect = ItemId_GetHoldEffect(item);
// Check for signature Z-Move or type-based Z-Move.
if (holdEffect == HOLD_EFFECT_Z_CRYSTAL)
{
u16 zMove = GetSignatureZMove(move, gBattleMons[battler].species, item);
if (zMove != MOVE_NONE)
{
gBattleStruct->zmove.chosenZMove = zMove; // Signature z move exists
return TRUE;
}
if (move != MOVE_NONE && zMove != MOVE_Z_STATUS && gMovesInfo[move].type == ItemId_GetSecondaryId(item))
{
gBattleStruct->zmove.chosenZMove = GetTypeBasedZMove(move, battler);
if (move != MOVE_NONE && gMovesInfo[move].type == ItemId_GetSecondaryId(item))
return TRUE;
}
}
return FALSE;
}
void GetUsableZMoves(u8 battler, u16 *moves)
void AssignUsableZMoves(u32 battler, u16 *moves)
{
u32 i;
gBattleStruct->zmove.possibleZMoves[battler] = 0;
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (moves[i] != MOVE_NONE && IsViableZMove(battler, moves[i]))
gBattleStruct->zmove.possibleZMoves[battler] |= (1 << i);
gBattleStruct->zmove.possibleZMoves[battler] |= gBitTable[i];
}
}
bool32 IsZMoveUsable(u8 battler, u16 moveIndex)
bool32 TryChangeZTrigger(u32 battler, u32 moveIndex)
{
if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && IsPartnerMonFromSameTrainer(battler) && gBattleStruct->zmove.toBeUsed[BATTLE_PARTNER(battler)] != MOVE_NONE)
return FALSE; // Player's other mon has a z move queued up already
if (gBattleStruct->zmove.possibleZMoves[battler] & (1 << moveIndex))
return TRUE;
return FALSE;
}
bool32 TryChangeZIndicator(u8 battler, u8 moveIndex)
{
bool32 viableZMove = IsZMoveUsable(battler, moveIndex);
bool32 viableZMove = (gBattleStruct->zmove.possibleZMoves[battler] & gBitTable[moveIndex]) != 0;
if (gBattleStruct->zmove.viable && !viableZMove)
HideZMoveTriggerSprite(); // Was a viable z move, now is not -> slide out
HideGimmickTriggerSprite(); // Was a viable z move, now is not -> slide out
else if (!gBattleStruct->zmove.viable && viableZMove)
ShowZMoveTriggerSprite(battler); // Was not a viable z move, now is -> slide back in
CreateGimmickTriggerSprite(battler); // Was not a viable z move, now is -> slide back in
gBattleStruct->zmove.viable = viableZMove;
return viableZMove;
}
#define SINGLES_Z_TRIGGER_POS_X_OPTIMAL (29)
#define SINGLES_Z_TRIGGER_POS_X_PRIORITY (29)
#define SINGLES_Z_TRIGGER_POS_X_SLIDE (15)
#define SINGLES_Z_TRIGGER_POS_Y_DIFF (-10)
#define DOUBLES_Z_TRIGGER_POS_X_OPTIMAL SINGLES_Z_TRIGGER_POS_X_OPTIMAL
#define DOUBLES_Z_TRIGGER_POS_X_PRIORITY SINGLES_Z_TRIGGER_POS_X_PRIORITY
#define DOUBLES_Z_TRIGGER_POS_X_SLIDE SINGLES_Z_TRIGGER_POS_X_SLIDE
#define DOUBLES_Z_TRIGGER_POS_Y_DIFF (-4)
#define tBattler data[0]
#define tHide data[1]
void CreateZMoveTriggerSprite(u8 battler, bool8 viable)
{
s16 x, y;
LoadSpritePalette(&sSpritePalette_ZMoveTrigger);
if (GetSpriteTileStartByTag(TAG_ZMOVE_TRIGGER_TILE) == 0xFFFF)
LoadCompressedSpriteSheetUsingHeap(&sSpriteSheet_ZMoveTrigger);
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
{
x = gSprites[gHealthboxSpriteIds[battler]].x - DOUBLES_Z_TRIGGER_POS_X_SLIDE;
y = gSprites[gHealthboxSpriteIds[battler]].y - DOUBLES_Z_TRIGGER_POS_Y_DIFF;
}
else
{
x = gSprites[gHealthboxSpriteIds[battler]].x - SINGLES_Z_TRIGGER_POS_X_SLIDE;
y = gSprites[gHealthboxSpriteIds[battler]].y - SINGLES_Z_TRIGGER_POS_Y_DIFF;
}
if (gBattleStruct->zmove.triggerSpriteId == 0xFF)
gBattleStruct->zmove.triggerSpriteId = CreateSprite(&sSpriteTemplate_ZMoveTrigger, x, y, 0);
gSprites[gBattleStruct->zmove.triggerSpriteId].tBattler = battler;
gSprites[gBattleStruct->zmove.triggerSpriteId].tHide = (viable == TRUE) ? FALSE : TRUE;
}
static void SpriteCB_ZMoveTrigger(struct Sprite *sprite)
{
s32 xSlide, xPriority, xOptimal;
s32 yDiff;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
{
xSlide = DOUBLES_Z_TRIGGER_POS_X_SLIDE;
xPriority = DOUBLES_Z_TRIGGER_POS_X_PRIORITY;
xOptimal = DOUBLES_Z_TRIGGER_POS_X_OPTIMAL;
yDiff = DOUBLES_Z_TRIGGER_POS_Y_DIFF;
}
else
{
xSlide = SINGLES_Z_TRIGGER_POS_X_SLIDE;
xPriority = SINGLES_Z_TRIGGER_POS_X_PRIORITY;
xOptimal = SINGLES_Z_TRIGGER_POS_X_OPTIMAL;
yDiff = SINGLES_Z_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)
DestroyZMoveTriggerSprite();
}
else
{
if (sprite->x != gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xOptimal)
{
sprite->x--;
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 IsZMoveTriggerSpriteActive(void)
{
if (GetSpriteTileStartByTag(TAG_ZMOVE_TRIGGER_TILE) == 0xFFFF)
return FALSE;
else if (IndexOfSpritePaletteTag(TAG_ZMOVE_TRIGGER_PAL) != 0xFF)
return TRUE;
else
return FALSE;
}
void HideZMoveTriggerSprite(void)
{
struct Sprite *sprite;
gBattleStruct->zmove.viable = FALSE;
if (gBattleStruct->zmove.triggerSpriteId >= MAX_SPRITES)
return;
sprite = &gSprites[gBattleStruct->zmove.triggerSpriteId];
sprite->tHide = TRUE;
}
static void ShowZMoveTriggerSprite(u8 battler)
{
gBattleStruct->zmove.viable = TRUE;
CreateZMoveTriggerSprite(battler, TRUE);
}
void DestroyZMoveTriggerSprite(void)
{
FreeSpritePaletteByTag(TAG_ZMOVE_TRIGGER_PAL);
FreeSpriteTilesByTag(TAG_ZMOVE_TRIGGER_TILE);
if (gBattleStruct->zmove.triggerSpriteId != 0xFF)
DestroySprite(&gSprites[gBattleStruct->zmove.triggerSpriteId]);
gBattleStruct->zmove.triggerSpriteId = 0xFF;
}
static u16 GetSignatureZMove(u16 move, u16 species, u16 item)
u32 GetSignatureZMove(u32 move, u32 species, u32 item)
{
u32 i;
@ -375,13 +241,17 @@ static u16 GetSignatureZMove(u16 move, u16 species, u16 item)
return MOVE_NONE;
}
u16 GetTypeBasedZMove(u16 move, u8 battler)
u32 GetTypeBasedZMove(u32 move)
{
u8 moveType = gMovesInfo[move].type;
u32 moveType = gMovesInfo[move].type;
if (moveType >= NUMBER_OF_MON_TYPES)
moveType = TYPE_MYSTERY;
// Z-Weather Ball changes types, however Revelation Dance, -ate ability affected moves, and Hidden Power do not
if (gBattleStruct->dynamicMoveType && gMovesInfo[move].effect == EFFECT_WEATHER_BALL)
moveType = gBattleStruct->dynamicMoveType & DYNAMIC_TYPE_MASK;
// Get Z-Move from type
if (gTypesInfo[moveType].zMove == MOVE_NONE) // failsafe
return gTypesInfo[0].zMove;
@ -544,7 +414,7 @@ static void ZMoveSelectionDisplayPpNumber(u32 battler)
static void ZMoveSelectionDisplayMoveType(u16 zMove, u32 battler)
{
u8 *txtPtr;
u8 *txtPtr, *end;
u8 zMoveType;
GET_MOVE_TYPE(zMove, zMoveType);
@ -554,7 +424,8 @@ static void ZMoveSelectionDisplayMoveType(u16 zMove, u32 battler)
*(txtPtr)++ = EXT_CTRL_CODE_FONT;
*(txtPtr)++ = FONT_NORMAL;
StringCopy(txtPtr, gTypesInfo[zMoveType].name);
end = StringCopy(txtPtr, gTypesInfo[zMoveType].name);
PrependFontIdToFit(txtPtr, end, FONT_NORMAL, WindowWidthPx(B_WIN_MOVE_TYPE) - 25);
BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MOVE_TYPE);
}
@ -564,20 +435,20 @@ static void ZMoveSelectionDisplayMoveType(u16 zMove, u32 battler)
void SetZEffect(void)
{
u32 i;
u32 effect = gMovesInfo[gBattleStruct->zmove.baseMoves[gBattlerAttacker]].zMove.effect;
gBattleStruct->zmove.zStatusActive = TRUE;
if (gBattleStruct->zmove.effect == Z_EFFECT_CURSE)
if (effect == Z_EFFECT_CURSE)
{
if (IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GHOST))
gBattleStruct->zmove.effect = Z_EFFECT_RECOVER_HP;
effect = Z_EFFECT_RECOVER_HP;
else
gBattleStruct->zmove.effect = Z_EFFECT_ATK_UP_1;
effect = Z_EFFECT_ATK_UP_1;
}
gBattleScripting.savedStatChanger = gBattleScripting.statChanger; // Save used move's stat changer (e.g. for Z-Growl)
gBattleScripting.battler = gBattlerAttacker;
switch (gBattleStruct->zmove.effect)
switch (effect)
{
case Z_EFFECT_RESET_STATS:
for (i = 0; i < NUM_BATTLE_STATS - 1; i++)
@ -590,22 +461,27 @@ void SetZEffect(void)
gBattlescriptCurrInstr = BattleScript_ZEffectPrintString;
break;
case Z_EFFECT_ALL_STATS_UP_1:
if (!AreStatsMaxed(gBattlerAttacker, STAT_SPDEF))
{
bool32 canBoost = FALSE;
for (i = STAT_ATK; i < NUM_STATS; i++) // Doesn't increase Acc or Evsn
{
for (i = 0; i < STAT_ACC - 1; i++) // Doesn't increase Acc or Evsn
if (STAT_STAGE(gBattlerAttacker, i) < 12)
{
if (gBattleMons[gBattlerAttacker].statStages[i] < 12)
++gBattleMons[gBattlerAttacker].statStages[i];
canBoost = TRUE;
}
}
if (canBoost)
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_Z_ALL_STATS_UP;
BattleScriptPush(gBattlescriptCurrInstr + Z_EFFECT_BS_LENGTH);
gBattlescriptCurrInstr = BattleScript_ZEffectPrintString;
gBattlescriptCurrInstr = BattleScript_AllStatsUpZMove;
}
else
{
gBattlescriptCurrInstr += Z_EFFECT_BS_LENGTH;
}
break;
}
case Z_EFFECT_BOOST_CRITS:
if (!(gBattleMons[gBattlerAttacker].status2 & STATUS2_FOCUS_ENERGY_ANY))
{
@ -646,17 +522,17 @@ void SetZEffect(void)
gBattlescriptCurrInstr = BattleScript_ZEffectPrintString;
break;
case Z_EFFECT_ATK_UP_1 ... Z_EFFECT_EVSN_UP_1:
SET_STATCHANGER(gBattleStruct->zmove.effect - Z_EFFECT_ATK_UP_1 + 1, 1, FALSE);
SET_STATCHANGER(effect - Z_EFFECT_ATK_UP_1 + 1, 1, FALSE);
BattleScriptPush(gBattlescriptCurrInstr + Z_EFFECT_BS_LENGTH);
gBattlescriptCurrInstr = BattleScript_StatUpZMove;
break;
case Z_EFFECT_ATK_UP_2 ... Z_EFFECT_EVSN_UP_2:
SET_STATCHANGER(gBattleStruct->zmove.effect - Z_EFFECT_ATK_UP_2 + 1, 2, FALSE);
SET_STATCHANGER(effect - Z_EFFECT_ATK_UP_2 + 1, 2, FALSE);
BattleScriptPush(gBattlescriptCurrInstr + Z_EFFECT_BS_LENGTH);
gBattlescriptCurrInstr = BattleScript_StatUpZMove;
break;
case Z_EFFECT_ATK_UP_3 ... Z_EFFECT_EVSN_UP_3:
SET_STATCHANGER(gBattleStruct->zmove.effect - Z_EFFECT_ATK_UP_3 + 1, 3, FALSE);
SET_STATCHANGER(effect - Z_EFFECT_ATK_UP_3 + 1, 3, FALSE);
BattleScriptPush(gBattlescriptCurrInstr + Z_EFFECT_BS_LENGTH);
gBattlescriptCurrInstr = BattleScript_StatUpZMove;
break;
@ -664,22 +540,9 @@ void SetZEffect(void)
gBattlescriptCurrInstr += Z_EFFECT_BS_LENGTH;
break;
}
gBattleStruct->zmove.zStatusActive = FALSE;
}
static bool32 AreStatsMaxed(u8 battler, u8 n)
{
u32 i;
for (i = STAT_ATK; i <= n; i++)
{
if (STAT_STAGE(battler, i) < MAX_STAT_STAGE)
return FALSE;
}
return TRUE;
}
u16 GetZMovePower(u16 move)
u32 GetZMovePower(u32 move)
{
if (gMovesInfo[move].category == DAMAGE_CATEGORY_STATUS)
return 0;

View File

@ -2243,4 +2243,11 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
.battleScript = BattleScript_EffectHit,
.battleTvScore = 0, // TODO: Assign points
},
[EFFECT_GUARDIAN_OF_ALOLA] =
{
.battleScript = BattleScript_DamageToQuarterTargetHP,
.battleTvScore = 0, // TODO: Assign points
},
};

54
src/data/gimmicks.h Normal file
View File

@ -0,0 +1,54 @@
#include "graphics/gimmicks.h"
// Gimmick data
const struct GimmickInfo gGimmicksInfo[GIMMICKS_COUNT] =
{
[GIMMICK_NONE] = {0},
[GIMMICK_MEGA] =
{
.triggerSheet = &sSpriteSheet_MegaTrigger,
.triggerPal = &sSpritePalette_MegaTrigger,
.triggerTemplate = &sSpriteTemplate_GimmickTrigger,
.indicatorSheet = &sSpriteSheet_MegaIndicator,
.indicatorPal = &sSpritePalette_MegaIndicator,
.CanActivate = CanMegaEvolve,
.ActivateGimmick = ActivateMegaEvolution,
},
[GIMMICK_Z_MOVE] =
{
.triggerSheet = &sSpriteSheet_ZMoveTrigger,
.triggerPal = &sSpritePalette_ZMoveTrigger,
.triggerTemplate = &sSpriteTemplate_GimmickTrigger,
.CanActivate = CanUseZMove,
.ActivateGimmick = ActivateZMove,
},
[GIMMICK_ULTRA_BURST] =
{
.triggerSheet = &sSpriteSheet_BurstTrigger,
.triggerPal = &sSpritePalette_BurstTrigger,
.triggerTemplate = &sSpriteTemplate_GimmickTrigger,
.CanActivate = CanUltraBurst,
.ActivateGimmick = ActivateUltraBurst,
},
[GIMMICK_DYNAMAX] =
{
.triggerSheet = &sSpriteSheet_DynamaxTrigger,
.triggerPal = &sSpritePalette_DynamaxTrigger,
.triggerTemplate = &sSpriteTemplate_GimmickTrigger,
.indicatorSheet = &sSpriteSheet_DynamaxIndicator,
.indicatorPal = &sSpritePalette_MiscIndicator,
.CanActivate = CanDynamax,
.ActivateGimmick = ActivateDynamax,
},
[GIMMICK_TERA] =
{
.triggerSheet = &sSpriteSheet_TeraTrigger,
.triggerPal = &sSpritePalette_TeraTrigger,
.triggerTemplate = &sSpriteTemplate_GimmickTrigger,
.indicatorSheet = NULL, // handled separately
.indicatorPal = &sSpritePalette_TeraIndicator,
.CanActivate = CanTerastallize,
.ActivateGimmick = ActivateTera,
}
};

View File

@ -0,0 +1,152 @@
// trigger data
static const u8 ALIGNED(4) sMegaTriggerGfx[] = INCBIN_U8("graphics/battle_interface/mega_trigger.4bpp");
static const u8 ALIGNED(4) sZMoveTriggerGfx[] = INCBIN_U8("graphics/battle_interface/z_move_trigger.4bpp");
static const u8 ALIGNED(4) sBurstTriggerGfx[] = INCBIN_U8("graphics/battle_interface/burst_trigger.4bpp");
static const u8 ALIGNED(4) sDynamaxTriggerGfx[] = INCBIN_U8("graphics/battle_interface/dynamax_trigger.4bpp");
static const u8 ALIGNED(4) sTeraTriggerGfx[] = INCBIN_U8("graphics/battle_interface/tera_trigger.4bpp");
static const u16 sMegaTriggerPal[] = INCBIN_U16("graphics/battle_interface/mega_trigger.gbapal");
static const u16 sZMoveTriggerPal[] = INCBIN_U16("graphics/battle_interface/z_move_trigger.gbapal");
static const u16 sBurstTriggerPal[] = INCBIN_U16("graphics/battle_interface/burst_trigger.gbapal");
static const u16 sDynamaxTriggerPal[] = INCBIN_U16("graphics/battle_interface/dynamax_trigger.gbapal");
static const u16 sTeraTriggerPal[] = INCBIN_U16("graphics/battle_interface/tera_trigger.gbapal");
static const struct SpriteSheet sSpriteSheet_MegaTrigger = {sMegaTriggerGfx, sizeof(sMegaTriggerGfx), TAG_GIMMICK_TRIGGER_TILE};
static const struct SpriteSheet sSpriteSheet_ZMoveTrigger = {sZMoveTriggerGfx, sizeof(sZMoveTriggerGfx), TAG_GIMMICK_TRIGGER_TILE};
static const struct SpriteSheet sSpriteSheet_BurstTrigger = {sBurstTriggerGfx, sizeof(sBurstTriggerGfx), TAG_GIMMICK_TRIGGER_TILE};
static const struct SpriteSheet sSpriteSheet_DynamaxTrigger = {sDynamaxTriggerGfx, sizeof(sDynamaxTriggerGfx), TAG_GIMMICK_TRIGGER_TILE};
static const struct SpriteSheet sSpriteSheet_TeraTrigger = {sTeraTriggerGfx, sizeof(sTeraTriggerGfx), TAG_GIMMICK_TRIGGER_TILE};
static const struct SpritePalette sSpritePalette_MegaTrigger = {sMegaTriggerPal, TAG_GIMMICK_TRIGGER_TILE};
static const struct SpritePalette sSpritePalette_ZMoveTrigger = {sZMoveTriggerPal, TAG_GIMMICK_TRIGGER_PAL};
static const struct SpritePalette sSpritePalette_BurstTrigger = {sBurstTriggerPal, TAG_GIMMICK_TRIGGER_TILE};
static const struct SpritePalette sSpritePalette_DynamaxTrigger = {sDynamaxTriggerPal, TAG_GIMMICK_TRIGGER_PAL};
static const struct SpritePalette sSpritePalette_TeraTrigger = {sTeraTriggerPal, TAG_GIMMICK_TRIGGER_TILE};
static const struct OamData sOamData_GimmickTrigger =
{
.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_GimmickTriggerOff[] =
{
ANIMCMD_FRAME(0, 0),
ANIMCMD_END
};
static const union AnimCmd sSpriteAnim_GimmickTriggerOn[] =
{
ANIMCMD_FRAME(16, 0),
ANIMCMD_END
};
static const union AnimCmd *const sSpriteAnimTable_GimmickTrigger[] =
{
sSpriteAnim_GimmickTriggerOff,
sSpriteAnim_GimmickTriggerOn,
};
static void SpriteCb_GimmickTrigger(struct Sprite *sprite);
static const struct SpriteTemplate sSpriteTemplate_GimmickTrigger =
{
.tileTag = TAG_GIMMICK_TRIGGER_TILE,
.paletteTag = TAG_GIMMICK_TRIGGER_PAL,
.oam = &sOamData_GimmickTrigger,
.anims = sSpriteAnimTable_GimmickTrigger,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_GimmickTrigger,
};
// indicator data
static const u8 ALIGNED(4) sMegaIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/mega_indicator.4bpp");
static const u8 ALIGNED(4) sAlphaIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/alpha_indicator.4bpp");
static const u8 ALIGNED(4) sOmegaIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/omega_indicator.4bpp");
static const u8 ALIGNED(4) sDynamaxIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/dynamax_indicator.4bpp");
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 const u16 sMiscIndicatorPal[] = INCBIN_U16("graphics/battle_interface/misc_indicator.gbapal"); // has room for more colors
static const u16 sMegaIndicatorPal[] = INCBIN_U16("graphics/battle_interface/mega_indicator.gbapal");
static const u16 sTeraIndicatorPal[] = INCBIN_U16("graphics/battle_interface/tera_indicator.gbapal");
static const struct SpriteSheet sSpriteSheet_MegaIndicator = {sMegaIndicatorGfx, sizeof(sMegaIndicatorGfx), TAG_MEGA_INDICATOR_TILE};
static const struct SpriteSheet sSpriteSheet_AlphaIndicator = {sAlphaIndicatorGfx, sizeof(sAlphaIndicatorGfx), TAG_ALPHA_INDICATOR_TILE};
static const struct SpriteSheet sSpriteSheet_OmegaIndicator = {sOmegaIndicatorGfx, sizeof(sOmegaIndicatorGfx), TAG_OMEGA_INDICATOR_TILE};
static const struct SpriteSheet sSpriteSheet_DynamaxIndicator = {sDynamaxIndicatorGfx, sizeof(sDynamaxIndicatorGfx), TAG_DYNAMAX_INDICATOR_TILE};
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 SpritePalette sSpritePalette_MiscIndicator = {sMiscIndicatorPal, TAG_MISC_INDICATOR_PAL};
static const struct SpritePalette sSpritePalette_MegaIndicator = {sMegaIndicatorPal, TAG_MEGA_INDICATOR_PAL};
static const struct SpritePalette sSpritePalette_TeraIndicator = {sTeraIndicatorPal, TAG_TERA_INDICATOR_PAL};
static const struct OamData sOamData_GimmickIndicator =
{
.shape = SPRITE_SHAPE(16x16),
.size = SPRITE_SIZE(16x16),
.priority = 1,
};
static void SpriteCb_GimmickIndicator(struct Sprite *sprite);
static const struct SpriteTemplate sSpriteTemplate_GimmickIndicator =
{
.tileTag = TAG_NORMAL_INDICATOR_TILE, // updated dynamically
.paletteTag = TAG_TERA_INDICATOR_PAL, // updated dynamically
.oam = &sOamData_GimmickIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_GimmickIndicator,
};

View File

@ -21106,9 +21106,9 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
},
[MOVE_GUARDIAN_OF_ALOLA] =
{
.name = COMPOUND_STRING("Guardian Of Alola"),
.name = COMPOUND_STRING("Guardian of Alola"),
.description = sNullDescription,
.effect = EFFECT_SUPER_FANG,
.effect = EFFECT_GUARDIAN_OF_ALOLA,
.power = 1,
.type = TYPE_FAIRY,
.accuracy = 0,
@ -21152,7 +21152,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
{
.name = COMPOUND_STRING("Light That Burns The Sky"),
.description = sNullDescription,
.effect = EFFECT_HIT,
.effect = EFFECT_PHOTON_GEYSER,
.power = 200,
.type = TYPE_PSYCHIC,
.accuracy = 0,

View File

@ -48,7 +48,7 @@ SINGLE_BATTLE_TEST("Pixilate turns a normal type move into Fairy")
PLAYER(SPECIES_DRAGONITE);
OPPONENT(SPECIES_ALTARIA) { Item(ITEM_ALTARIANITE); }
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE, megaEvolve: TRUE); }
TURN { MOVE(opponent, MOVE_TACKLE, gimmick: GIMMICK_MEGA); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
@ -62,9 +62,8 @@ SINGLE_BATTLE_TEST("Refrigerate turns a normal type move into Ice")
PLAYER(SPECIES_MEGANIUM);
OPPONENT(SPECIES_AMAURA) { Ability(ABILITY_REFRIGERATE); }
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE, megaEvolve: TRUE); }
TURN { MOVE(opponent, MOVE_TACKLE); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
MESSAGE("It's super effective!");
}
@ -76,7 +75,7 @@ SINGLE_BATTLE_TEST("Aerilate turns a normal type move into Flying")
PLAYER(SPECIES_MEGANIUM);
OPPONENT(SPECIES_SALAMENCE) { Item(ITEM_SALAMENCITE); }
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE, megaEvolve: TRUE); }
TURN { MOVE(opponent, MOVE_TACKLE, gimmick: GIMMICK_MEGA); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);

View File

@ -10,7 +10,7 @@ SINGLE_BATTLE_TEST("Parental Bond converts Tackle into a two-strike move")
PLAYER(SPECIES_KANGASKHAN) { Item(ITEM_KANGASKHANITE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TACKLE, megaEvolve: TRUE); MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_MEGA); MOVE(opponent, MOVE_CELEBRATE); }
} SCENE {
MESSAGE("Kangaskhan's Kangaskhanite is reacting to 1's Mega Ring!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, player);
@ -32,7 +32,7 @@ SINGLE_BATTLE_TEST("Parental Bond does not convert a move with three or more str
PLAYER(SPECIES_KANGASKHAN) { Item(ITEM_KANGASKHANITE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TRIPLE_KICK, megaEvolve: TRUE); MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_TRIPLE_KICK, gimmick: GIMMICK_MEGA); MOVE(opponent, MOVE_CELEBRATE); }
} SCENE {
MESSAGE("Kangaskhan's Kangaskhanite is reacting to 1's Mega Ring!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, player);
@ -61,7 +61,7 @@ SINGLE_BATTLE_TEST("Parental Bond converts multi-target moves into a two-strike
PLAYER(SPECIES_KANGASKHAN) { Item(ITEM_KANGASKHANITE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, move, megaEvolve: TRUE); MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(player, move, gimmick: GIMMICK_MEGA); MOVE(opponent, MOVE_CELEBRATE); }
} SCENE {
MESSAGE("Kangaskhan's Kangaskhanite is reacting to 1's Mega Ring!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, player);
@ -86,7 +86,7 @@ DOUBLE_BATTLE_TEST("Parental Bond does not convert multi-target moves into a two
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_PIDGEY);
} WHEN {
TURN { MOVE(playerLeft, MOVE_EARTHQUAKE, megaEvolve: TRUE); MOVE(playerRight, MOVE_CELEBRATE); MOVE(opponentLeft, MOVE_CELEBRATE); MOVE(opponentRight, MOVE_CELEBRATE); }
TURN { MOVE(playerLeft, MOVE_EARTHQUAKE, gimmick: GIMMICK_MEGA); MOVE(playerRight, MOVE_CELEBRATE); MOVE(opponentLeft, MOVE_CELEBRATE); MOVE(opponentRight, MOVE_CELEBRATE); }
} SCENE {
MESSAGE("Kangaskhan's Kangaskhanite is reacting to 1's Mega Ring!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, playerLeft);
@ -114,7 +114,7 @@ SINGLE_BATTLE_TEST("Parental Bond-converted moves only hit once on Lightning Rod
PLAYER(SPECIES_KANGASKHAN) { Item(ITEM_KANGASKHANITE); }
OPPONENT(species) { Ability(ability); }
} WHEN {
TURN { MOVE(player, move, megaEvolve: TRUE); MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(player, move, gimmick: GIMMICK_MEGA); MOVE(opponent, MOVE_CELEBRATE); }
} SCENE {
MESSAGE("Kangaskhan's Kangaskhanite is reacting to 1's Mega Ring!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, player);
@ -142,7 +142,7 @@ SINGLE_BATTLE_TEST("Parental Bond has no affect on multi hit moves and they stil
PLAYER(SPECIES_KANGASKHAN) { Item(ITEM_KANGASKHANITE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_COMET_PUNCH, megaEvolve: TRUE); }
TURN { MOVE(player, MOVE_COMET_PUNCH, gimmick: GIMMICK_MEGA); }
} SCENE {
MESSAGE("Kangaskhan's Kangaskhanite is reacting to 1's Mega Ring!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, player);
@ -168,7 +168,7 @@ SINGLE_BATTLE_TEST("Parental Bond has no affect on multi hit moves and they stil
PLAYER(SPECIES_KANGASKHAN) { Item(ITEM_KANGASKHANITE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_COMET_PUNCH, megaEvolve: TRUE); }
TURN { MOVE(player, MOVE_COMET_PUNCH, gimmick: GIMMICK_MEGA); }
} SCENE {
MESSAGE("Kangaskhan's Kangaskhanite is reacting to 1's Mega Ring!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, player);
@ -195,7 +195,7 @@ SINGLE_BATTLE_TEST("Parental Bond has no affect on multi hit moves and they stil
PLAYER(SPECIES_KANGASKHAN) { Item(ITEM_KANGASKHANITE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_COMET_PUNCH, megaEvolve: TRUE); }
TURN { MOVE(player, MOVE_COMET_PUNCH, gimmick: GIMMICK_MEGA); }
} SCENE {
MESSAGE("Kangaskhan's Kangaskhanite is reacting to 1's Mega Ring!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, player);
@ -223,7 +223,7 @@ SINGLE_BATTLE_TEST("Parental Bond has no affect on multi hit moves and they stil
PLAYER(SPECIES_KANGASKHAN) { Item(ITEM_KANGASKHANITE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_COMET_PUNCH, megaEvolve: TRUE); }
TURN { MOVE(player, MOVE_COMET_PUNCH, gimmick: GIMMICK_MEGA); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_COMET_PUNCH, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_COMET_PUNCH, player);

View File

@ -196,7 +196,7 @@ SINGLE_BATTLE_TEST("Prankster-affected moves can still be bounced back by a Dark
PLAYER(SPECIES_ABSOL) { Item(ITEM_ABSOLITE); }
OPPONENT(SPECIES_VOLBEAT) { Ability(ABILITY_PRANKSTER); }
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, megaEvolve: TRUE); MOVE(opponent, MOVE_CONFUSE_RAY); }
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); MOVE(opponent, MOVE_CONFUSE_RAY); }
} SCENE {
MESSAGE("Foe Volbeat's Confuse Ray was bounced back by Absol's Magic Bounce!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_CONFUSE_RAY, player);
@ -209,7 +209,7 @@ SINGLE_BATTLE_TEST("Prankster-affected moves that are bounced back by Magic Boun
PLAYER(SPECIES_ABSOL) { Item(ITEM_ABSOLITE); }
OPPONENT(SPECIES_MURKROW) { Ability(ABILITY_PRANKSTER); }
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, megaEvolve: TRUE); MOVE(opponent, MOVE_CONFUSE_RAY); }
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); MOVE(opponent, MOVE_CONFUSE_RAY); }
} SCENE {
MESSAGE("Foe Murkrow's Confuse Ray was bounced back by Absol's Magic Bounce!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_CONFUSE_RAY, player);

View File

@ -7,7 +7,7 @@ SINGLE_BATTLE_TEST("Venusaur can Mega Evolve holding Venusaurite")
PLAYER(SPECIES_VENUSAUR) { Item(ITEM_VENUSAURITE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, megaEvolve: TRUE); }
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); }
} SCENE {
MESSAGE("Venusaur's Venusaurite is reacting to 1's Mega Ring!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, player);
@ -25,7 +25,7 @@ DOUBLE_BATTLE_TEST("Mega Evolution's order is determined by Speed - opponent fas
OPPONENT(SPECIES_GARDEVOIR) { Item(ITEM_GARDEVOIRITE); Speed(3); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(4); }
} WHEN {
TURN { MOVE(opponentLeft, MOVE_CELEBRATE, megaEvolve: TRUE); MOVE(playerLeft, MOVE_CELEBRATE, megaEvolve: TRUE); }
TURN { MOVE(opponentLeft, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); MOVE(playerLeft, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); }
} SCENE {
MESSAGE("Foe Gardevoir's Gardevoirite is reacting to 2's Mega Ring!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponentLeft);
@ -44,7 +44,7 @@ DOUBLE_BATTLE_TEST("Mega Evolution's order is determined by Speed - player faste
OPPONENT(SPECIES_GARDEVOIR) { Item(ITEM_GARDEVOIRITE); Speed(2); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(4); }
} WHEN {
TURN { MOVE(opponentLeft, MOVE_CELEBRATE, megaEvolve: TRUE); MOVE(playerLeft, MOVE_CELEBRATE, megaEvolve: TRUE); }
TURN { MOVE(opponentLeft, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); MOVE(playerLeft, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); }
} SCENE {
MESSAGE("Venusaur's Venusaurite is reacting to 1's Mega Ring!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, playerLeft);
@ -61,7 +61,7 @@ SINGLE_BATTLE_TEST("Rayquaza can Mega Evolve knowing Dragon Ascent")
PLAYER(SPECIES_RAYQUAZA) { Moves(MOVE_DRAGON_ASCENT, MOVE_CELEBRATE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, megaEvolve: TRUE); }
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); }
} SCENE {
MESSAGE("1's fervent wish has reached Rayquaza!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, player);
@ -78,7 +78,7 @@ SINGLE_BATTLE_TEST("Mega Evolution affects turn order")
PLAYER(SPECIES_GARDEVOIR) { Item(ITEM_GARDEVOIRITE); Speed(105); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(106); }
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, megaEvolve: TRUE); }
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); }
} SCENE {
MESSAGE("Gardevoir used Celebrate!");
MESSAGE("Foe Wobbuffet used Celebrate!");
@ -96,7 +96,7 @@ SINGLE_BATTLE_TEST("Abilities replaced by Mega Evolution do not affect turn orde
PLAYER(SPECIES_SABLEYE) { Item(ITEM_SABLENITE); Ability(ABILITY_STALL); Speed(105); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(44); }
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, megaEvolve: TRUE); }
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); }
} SCENE {
MESSAGE("Sableye used Celebrate!");
MESSAGE("Foe Wobbuffet used Celebrate!");
@ -115,7 +115,7 @@ DOUBLE_BATTLE_TEST("Mega Evolution happens after switching, but before Focus Pun
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { SWITCH(opponentRight, 2); MOVE(playerRight, MOVE_FOCUS_PUNCH, megaEvolve: TRUE, target: opponentLeft); MOVE(playerLeft, MOVE_FOCUS_PUNCH, target: opponentLeft); }
TURN { SWITCH(opponentRight, 2); MOVE(playerRight, MOVE_FOCUS_PUNCH, gimmick: GIMMICK_MEGA, target: opponentLeft); MOVE(playerLeft, MOVE_FOCUS_PUNCH, target: opponentLeft); }
TURN {}
} SCENE {
MESSAGE("2 withdrew Wobbuffet!");
@ -139,7 +139,7 @@ SINGLE_BATTLE_TEST("Regular Mega Evolution and Fervent Wish Mega Evolution can h
PLAYER(SPECIES_RAYQUAZA) { Moves(MOVE_DRAGON_ASCENT, MOVE_CELEBRATE); Speed(3); }
OPPONENT(SPECIES_GARDEVOIR) { Item(ITEM_GARDEVOIRITE); Speed(2); }
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, megaEvolve: TRUE); MOVE(opponent, MOVE_CELEBRATE, megaEvolve: TRUE); }
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); MOVE(opponent, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); }
} SCENE {
MESSAGE("1's fervent wish has reached Rayquaza!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, player);

View File

@ -7,7 +7,7 @@ SINGLE_BATTLE_TEST("Dusk Mane Necrozma can Ultra Burst holding Ultranecrozium Z"
PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Item(ITEM_ULTRANECROZIUM_Z); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, ultraBurst: TRUE); }
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_ULTRA_BURST); }
} SCENE {
MESSAGE("Bright light is about to burst out of Necrozma!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ULTRA_BURST, player);
@ -25,7 +25,7 @@ DOUBLE_BATTLE_TEST("Ultra Burst's order is determined by Speed - opponent faster
OPPONENT(SPECIES_NECROZMA_DAWN_WINGS) { Item(ITEM_ULTRANECROZIUM_Z); Speed(3); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(4); }
} WHEN {
TURN { MOVE(opponentLeft, MOVE_CELEBRATE, ultraBurst: TRUE); MOVE(playerLeft, MOVE_CELEBRATE, ultraBurst: TRUE); }
TURN { MOVE(opponentLeft, MOVE_CELEBRATE, gimmick: GIMMICK_ULTRA_BURST); MOVE(playerLeft, MOVE_CELEBRATE, gimmick: GIMMICK_ULTRA_BURST); }
} SCENE {
MESSAGE("Bright light is about to burst out of Foe Necrozma!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ULTRA_BURST, opponentLeft);
@ -44,7 +44,7 @@ DOUBLE_BATTLE_TEST("Ultra Burst's order is determined by Speed - player faster")
OPPONENT(SPECIES_NECROZMA_DAWN_WINGS) { Item(ITEM_ULTRANECROZIUM_Z); Speed(2); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(4); }
} WHEN {
TURN { MOVE(opponentLeft, MOVE_CELEBRATE, ultraBurst: TRUE); MOVE(playerLeft, MOVE_CELEBRATE, ultraBurst: TRUE); }
TURN { MOVE(opponentLeft, MOVE_CELEBRATE, gimmick: GIMMICK_ULTRA_BURST); MOVE(playerLeft, MOVE_CELEBRATE, gimmick: GIMMICK_ULTRA_BURST); }
} SCENE {
MESSAGE("Bright light is about to burst out of Necrozma!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ULTRA_BURST, playerLeft);
@ -62,7 +62,7 @@ SINGLE_BATTLE_TEST("Ultra Burst affects turn order")
PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Item(ITEM_ULTRANECROZIUM_Z); Speed(105); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(106); }
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, ultraBurst: TRUE); }
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_ULTRA_BURST); }
} SCENE {
MESSAGE("Necrozma used Celebrate!");
MESSAGE("Foe Wobbuffet used Celebrate!");
@ -81,7 +81,7 @@ DOUBLE_BATTLE_TEST("Ultra Burst happens after switching, but before Focus Punch-
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { SWITCH(opponentRight, 2); MOVE(playerRight, MOVE_FOCUS_PUNCH, ultraBurst: TRUE, target: opponentLeft); MOVE(playerLeft, MOVE_FOCUS_PUNCH, target: opponentLeft); }
TURN { SWITCH(opponentRight, 2); MOVE(playerRight, MOVE_FOCUS_PUNCH, gimmick: GIMMICK_ULTRA_BURST, target: opponentLeft); MOVE(playerLeft, MOVE_FOCUS_PUNCH, target: opponentLeft); }
TURN {}
} SCENE {
MESSAGE("2 withdrew Wobbuffet!");
@ -105,7 +105,7 @@ SINGLE_BATTLE_TEST("Ultra Burst and Mega Evolution can happen on the same turn")
PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Item(ITEM_ULTRANECROZIUM_Z); Speed(3); }
OPPONENT(SPECIES_GARDEVOIR) { Item(ITEM_GARDEVOIRITE); Speed(2); }
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, ultraBurst: TRUE); MOVE(opponent, MOVE_CELEBRATE, megaEvolve: TRUE); }
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_ULTRA_BURST); MOVE(opponent, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); }
} SCENE {
MESSAGE("Bright light is about to burst out of Necrozma!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ULTRA_BURST, player);

View File

@ -4,14 +4,14 @@
// ============= DYNAMAX AND MAX MOVE INTERACTIONS ===================
SINGLE_BATTLE_TEST("(DYNAMAX) Dynamax increases HP and max HP by 1.5x", u16 hp)
{
bool32 dynamax;
PARAMETRIZE { dynamax = FALSE; }
PARAMETRIZE { dynamax = TRUE; }
u32 dynamax;
PARAMETRIZE { dynamax = GIMMICK_NONE; }
PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; }
GIVEN { // TODO: Dynamax level
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TACKLE, dynamax: dynamax); MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_TACKLE, gimmick: dynamax); MOVE(opponent, MOVE_CELEBRATE); }
} SCENE {
if (dynamax) {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_DYNAMAX_GROWTH, player);
@ -27,14 +27,14 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamax increases HP and max HP by 1.5x", u16 hp)
SINGLE_BATTLE_TEST("(DYNAMAX) Dynamax expires after three turns", u16 hp)
{
bool32 dynamax;
PARAMETRIZE { dynamax = FALSE; }
PARAMETRIZE { dynamax = TRUE; }
u32 dynamax;
PARAMETRIZE { dynamax = GIMMICK_NONE; }
PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TACKLE, dynamax: dynamax); } // 1st max move
TURN { MOVE(player, MOVE_TACKLE, gimmick: dynamax); } // 1st max move
TURN { MOVE(player, MOVE_TACKLE); } // 2nd max move
TURN { MOVE(player, MOVE_TACKLE); } // 3rd max move
} SCENE {
@ -62,7 +62,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon cannot be flinched")
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_FAKE_OUT); MOVE(player, MOVE_TACKLE, dynamax: TRUE); }
TURN { MOVE(opponent, MOVE_FAKE_OUT); MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); }
} SCENE {
MESSAGE("Foe Wobbuffet used Fake Out!");
NONE_OF { MESSAGE("Wobbuffet flinched!"); }
@ -77,7 +77,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon cannot be hit by weight-based mo
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TACKLE, dynamax: TRUE); MOVE(opponent, MOVE_HEAVY_SLAM); }
TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); MOVE(opponent, MOVE_HEAVY_SLAM); }
} SCENE {
MESSAGE("Wobbuffet used Max Strike!");
MESSAGE("Foe Wobbuffet used Heavy Slam!");
@ -93,7 +93,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon cannot be hit by OHKO moves")
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_MACHAMP) { Ability(ABILITY_NO_GUARD); }
} WHEN {
TURN { MOVE(player, MOVE_TACKLE, dynamax: TRUE); MOVE(opponent, MOVE_FISSURE); }
TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); MOVE(opponent, MOVE_FISSURE); }
} SCENE {
MESSAGE("Wobbuffet used Max Strike!");
MESSAGE("Foe Machamp used Fissure!");
@ -109,7 +109,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon are not affected by Destiny Bond
PLAYER(SPECIES_WOBBUFFET) { Speed(50); };
OPPONENT(SPECIES_WOBBUFFET) { HP(1); Speed(100); }
} WHEN {
TURN { MOVE(opponent, MOVE_DESTINY_BOND); MOVE(player, MOVE_TACKLE, dynamax: TRUE); }
TURN { MOVE(opponent, MOVE_DESTINY_BOND); MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); }
} SCENE {
MESSAGE("Foe Wobbuffet used Destiny Bond!");
MESSAGE("Wobbuffet used Max Strike!");
@ -124,7 +124,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon are affected by Grudge")
PLAYER(SPECIES_WOBBUFFET) { Speed(50); };
OPPONENT(SPECIES_WOBBUFFET) { HP(1); Speed(100); }
} WHEN {
TURN { MOVE(opponent, MOVE_GRUDGE); MOVE(player, MOVE_TACKLE, dynamax: TRUE); }
TURN { MOVE(opponent, MOVE_GRUDGE); MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); }
} SCENE {
MESSAGE("Foe Wobbuffet used Grudge!");
MESSAGE("Wobbuffet used Max Strike!");
@ -142,7 +142,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon are not affected by phazing move
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_DRAGON_TAIL); MOVE(player, MOVE_TACKLE, dynamax: TRUE); }
TURN { MOVE(opponent, MOVE_DRAGON_TAIL); MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); }
TURN { MOVE(opponent, MOVE_WHIRLWIND); MOVE(player, MOVE_TACKLE); }
} SCENE {
MESSAGE("Wobbuffet used Max Strike!");
@ -163,7 +163,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon are not affected by phazing move
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_DRAGON_TAIL); MOVE(player, MOVE_TACKLE, dynamax: TRUE); SEND_OUT(player, 1); }
TURN { MOVE(opponent, MOVE_DRAGON_TAIL); MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); SEND_OUT(player, 1); }
} SCENE {
MESSAGE("Wobbuffet used Max Strike!");
MESSAGE("Foe Wobbuffet used Dragon Tail!");
@ -181,7 +181,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon are not affected by Red Card")
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_RED_CARD); }
} WHEN {
TURN { MOVE(player, MOVE_TACKLE, dynamax: TRUE); MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); MOVE(opponent, MOVE_CELEBRATE); }
} SCENE {
MESSAGE("Wobbuffet used Max Strike!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
@ -195,11 +195,12 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon are not affected by Red Card")
SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon can be switched out by Eject Button")
{
GIVEN {
ASSUME(gItemsInfo[ITEM_EJECT_BUTTON].holdEffect == HOLD_EFFECT_EJECT_BUTTON);
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_EJECT_BUTTON); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TACKLE, dynamax: TRUE); MOVE(opponent, MOVE_TACKLE); SEND_OUT(player, 1); }
TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); MOVE(opponent, MOVE_TACKLE); SEND_OUT(player, 1); }
} SCENE {
MESSAGE("Wobbuffet used Max Strike!");
MESSAGE("Foe Wobbuffet used Tackle!");
@ -216,7 +217,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon cannot have their ability swappe
PLAYER(SPECIES_MILTANK) { Ability(ABILITY_SCRAPPY); }
OPPONENT(SPECIES_RUNERIGUS) { Ability(ABILITY_WANDERING_SPIRIT); }
} WHEN {
TURN { MOVE(player, MOVE_TACKLE, dynamax: TRUE); MOVE(opponent, MOVE_SKILL_SWAP); }
TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); MOVE(opponent, MOVE_SKILL_SWAP); }
} SCENE {
MESSAGE("Miltank used Max Strike!");
MESSAGE("Foe Runerigus used Skill Swap!");
@ -232,7 +233,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon can have their ability changed o
PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_SHADOW_TAG); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TACKLE, dynamax: TRUE); MOVE(opponent, MOVE_SIMPLE_BEAM); }
TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); MOVE(opponent, MOVE_SIMPLE_BEAM); }
} SCENE {
MESSAGE("Wobbuffet used Max Strike!");
MESSAGE("Foe Wobbuffet used Simple Beam!");
@ -248,7 +249,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon are immune to Encore")
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TACKLE, dynamax: TRUE); MOVE(opponent, MOVE_ENCORE); }
TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); MOVE(opponent, MOVE_ENCORE); }
TURN { MOVE(player, MOVE_EMBER); }
} SCENE {
MESSAGE("Wobbuffet used Max Strike!");
@ -264,7 +265,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon can be encored immediately after
PLAYER(SPECIES_WOBBUFFET) { Speed(50); }; // yes, this speed is necessary
OPPONENT(SPECIES_WOBBUFFET) { Speed(100); };
} WHEN {
TURN { MOVE(player, MOVE_ARM_THRUST, dynamax: TRUE); }
TURN { MOVE(player, MOVE_ARM_THRUST, gimmick: GIMMICK_DYNAMAX); }
TURN { MOVE(player, MOVE_ARM_THRUST); }
TURN { MOVE(player, MOVE_ARM_THRUST); }
TURN { MOVE(opponent, MOVE_ENCORE); MOVE(player, MOVE_TACKLE); }
@ -284,7 +285,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon's Max Moves cannot be disabled")
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TACKLE, dynamax: TRUE); MOVE(opponent, MOVE_DISABLE); }
TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); MOVE(opponent, MOVE_DISABLE); }
} SCENE {
MESSAGE("Wobbuffet used Max Strike!");
MESSAGE("Foe Wobbuffet used Disable!");
@ -300,7 +301,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon can have base moves disabled on
OPPONENT(SPECIES_WOBBUFFET) { Speed(100); };
} WHEN {
TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_TACKLE); }
TURN { MOVE(opponent, MOVE_DISABLE); MOVE(player, MOVE_TACKLE, dynamax: TRUE); }
TURN { MOVE(opponent, MOVE_DISABLE); MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); }
TURN {}
TURN {}
TURN { MOVE(player, MOVE_TACKLE, allowed: FALSE); MOVE(player, MOVE_CELEBRATE); }
@ -319,7 +320,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon are immune to Torment")
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TACKLE, dynamax: TRUE); MOVE(opponent, MOVE_TORMENT); }
TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); MOVE(opponent, MOVE_TORMENT); }
} SCENE {
MESSAGE("Wobbuffet used Max Strike!");
MESSAGE("Foe Wobbuffet used Torment!");
@ -334,7 +335,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon are not immune to Knock Off")
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_POTION); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TACKLE, dynamax: TRUE); MOVE(opponent, MOVE_KNOCK_OFF); }
TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); MOVE(opponent, MOVE_KNOCK_OFF); }
} SCENE {
MESSAGE("Wobbuffet used Max Strike!");
MESSAGE("Foe Wobbuffet used Knock Off!");
@ -351,7 +352,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon lose their substitutes")
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SUBSTITUTE); MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_TACKLE, dynamax: TRUE); MOVE(opponent, MOVE_TACKLE); }
TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); MOVE(opponent, MOVE_TACKLE); }
} SCENE {
MESSAGE("Wobbuffet used Substitute!");
MESSAGE("Wobbuffet made a SUBSTITUTE!");
@ -369,7 +370,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon can have their base moves copied
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(playerLeft, MOVE_TRICK_ROOM, dynamax: TRUE, target: opponentLeft); MOVE(playerRight, MOVE_COPYCAT, target: opponentLeft); }
TURN { MOVE(playerLeft, MOVE_TRICK_ROOM, gimmick: GIMMICK_DYNAMAX, target: opponentLeft); MOVE(playerRight, MOVE_COPYCAT, target: opponentLeft); }
} SCENE {
MESSAGE("Wobbuffet used Max Guard!");
MESSAGE("Wynaut used Trick Room!");
@ -378,15 +379,15 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon can have their base moves copied
SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon take double damage from Dynamax Cannon", s16 damage)
{
bool32 dynamaxed;
PARAMETRIZE { dynamaxed = FALSE; }
PARAMETRIZE { dynamaxed = TRUE; }
u32 dynamax;
PARAMETRIZE { dynamax = GIMMICK_NONE; }
PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; }
GIVEN {
ASSUME(gMovesInfo[MOVE_DYNAMAX_CANNON].effect == EFFECT_DYNAMAX_DOUBLE_DMG);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TACKLE, dynamax: dynamaxed); MOVE(opponent, MOVE_DYNAMAX_CANNON); }
TURN { MOVE(player, MOVE_TACKLE, gimmick: dynamax); MOVE(opponent, MOVE_DYNAMAX_CANNON); }
} SCENE {
HP_BAR(player, captureDamage: &results[i].damage);
} FINALLY {
@ -404,9 +405,9 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Max Moves deal 1/4 damage through protect", s16 da
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
if (protected)
TURN { MOVE(player, MOVE_TACKLE, dynamax: TRUE); MOVE(opponent, MOVE_PROTECT); }
TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); MOVE(opponent, MOVE_PROTECT); }
else
TURN { MOVE(player, MOVE_TACKLE, dynamax: TRUE); }
TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
@ -420,7 +421,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Max Moves don't bypass Max Guard")
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TACKLE, dynamax: TRUE); MOVE(opponent, MOVE_PROTECT, dynamax: TRUE); }
TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); MOVE(opponent, MOVE_PROTECT, gimmick: GIMMICK_DYNAMAX); }
} SCENE {
NONE_OF { HP_BAR(opponent); }
}
@ -434,7 +435,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) Feint bypasses Max Guard but doesn't break it")
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(playerLeft, MOVE_PROTECT, dynamax: TRUE);
TURN { MOVE(playerLeft, MOVE_PROTECT, gimmick: GIMMICK_DYNAMAX);
MOVE(opponentLeft, MOVE_FEINT, target: playerLeft);
MOVE(opponentRight, MOVE_TACKLE, target: playerLeft);
}
@ -455,7 +456,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon are immune to Instruct")
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(playerLeft, MOVE_TACKLE, dynamax: TRUE, target: opponentLeft);
TURN { MOVE(playerLeft, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX, target: opponentLeft);
MOVE(playerRight, MOVE_INSTRUCT, target: playerLeft);
}
} SCENE {
@ -475,7 +476,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Pokemon with Gigantamax forms change upon Dynamaxi
PLAYER(SPECIES_VENUSAUR) { GigantamaxFactor(gigantamaxFactor); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TACKLE, dynamax: TRUE); }
TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); }
} THEN {
EXPECT_EQ(player->species, species);
}
@ -488,7 +489,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Pokemon with Gigantamax forms revert upon switchin
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TACKLE, dynamax: TRUE); }
TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); }
TURN { SWITCH(player, 1); }
TURN { SWITCH(player, 0); }
} THEN {
@ -506,8 +507,8 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon are not affected by Choice items
PLAYER(SPECIES_WOBBUFFET) { Item(item); };
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TACKLE, dynamax: TRUE); }
TURN { MOVE(player, MOVE_ARM_THRUST, dynamax: TRUE); }
TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); }
TURN { MOVE(player, MOVE_ARM_THRUST); }
} SCENE {
MESSAGE("Wobbuffet used Max Strike!");
HP_BAR(opponent, captureDamage: &results[i].damage);
@ -524,7 +525,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon cannot use Max Guard while holdi
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_ASSAULT_VEST); };
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TACKLE, dynamax: TRUE); }
TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); }
TURN { MOVE(player, MOVE_PROTECT, allowed: FALSE); MOVE(player, MOVE_TACKLE); }
} SCENE {
MESSAGE("Wobbuffet used Max Strike!");
@ -540,15 +541,15 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon cannot use Max Guard while holdi
SINGLE_BATTLE_TEST("(DYNAMAX) Endeavor uses a Pokemon's non-Dynamax HP", s16 damage)
{
bool32 dynamax;
PARAMETRIZE { dynamax = TRUE; }
PARAMETRIZE { dynamax = FALSE; }
u32 dynamax;
PARAMETRIZE { dynamax = GIMMICK_NONE; }
PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; }
GIVEN {
ASSUME(gMovesInfo[MOVE_ENDEAVOR].effect == EFFECT_ENDEAVOR);
PLAYER(SPECIES_WOBBUFFET) { Speed(50); }
OPPONENT(SPECIES_WOBBUFFET) { HP(1); Speed(100); }
} WHEN {
TURN { MOVE(opponent, MOVE_ENDEAVOR); MOVE(player, MOVE_TACKLE, dynamax: dynamax); }
TURN { MOVE(opponent, MOVE_ENDEAVOR); MOVE(player, MOVE_TACKLE, gimmick: dynamax); }
} SCENE {
MESSAGE("Foe Wobbuffet used Endeavor!");
HP_BAR(player, captureDamage: &results[i].damage);
@ -559,15 +560,15 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Endeavor uses a Pokemon's non-Dynamax HP", s16 dam
SINGLE_BATTLE_TEST("(DYNAMAX) Super Fang uses a Pokemon's non-Dynamax HP", s16 damage)
{
bool32 dynamax;
PARAMETRIZE { dynamax = TRUE; }
PARAMETRIZE { dynamax = FALSE; }
u32 dynamax;
PARAMETRIZE { dynamax = GIMMICK_NONE; }
PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; }
GIVEN {
ASSUME(gMovesInfo[MOVE_SUPER_FANG].effect == EFFECT_SUPER_FANG);
PLAYER(SPECIES_WOBBUFFET) { Speed(50); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(100); }
} WHEN {
TURN { MOVE(opponent, MOVE_SUPER_FANG); MOVE(player, MOVE_TACKLE, dynamax: dynamax); }
TURN { MOVE(opponent, MOVE_SUPER_FANG); MOVE(player, MOVE_TACKLE, gimmick: dynamax); }
} SCENE {
MESSAGE("Foe Wobbuffet used Super Fang!");
HP_BAR(player, captureDamage: &results[i].damage);
@ -578,15 +579,15 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Super Fang uses a Pokemon's non-Dynamax HP", s16 d
SINGLE_BATTLE_TEST("(DYNAMAX) Pain Split uses a Pokemon's non-Dynamax HP", s16 damage)
{
bool32 dynamax;
PARAMETRIZE { dynamax = TRUE; }
PARAMETRIZE { dynamax = FALSE; }
u32 dynamax;
PARAMETRIZE { dynamax = GIMMICK_NONE; }
PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; }
GIVEN {
ASSUME(gMovesInfo[MOVE_PAIN_SPLIT].effect == EFFECT_PAIN_SPLIT);
PLAYER(SPECIES_WOBBUFFET) { Speed(50); }
OPPONENT(SPECIES_WOBBUFFET) { HP(1); Speed(100); }
} WHEN {
TURN { MOVE(opponent, MOVE_PAIN_SPLIT); MOVE(player, MOVE_TACKLE, dynamax: dynamax); }
TURN { MOVE(opponent, MOVE_PAIN_SPLIT); MOVE(player, MOVE_TACKLE, gimmick: dynamax); }
} SCENE {
MESSAGE("Foe Wobbuffet used Pain Split!");
HP_BAR(player, captureDamage: &results[i].damage);
@ -597,16 +598,16 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Pain Split uses a Pokemon's non-Dynamax HP", s16 d
SINGLE_BATTLE_TEST("(DYNAMAX) Sitrus Berries heal based on a Pokemon's non-Dynamax HP", s16 damage)
{
bool32 dynamax;
PARAMETRIZE { dynamax = TRUE; }
PARAMETRIZE { dynamax = FALSE; }
u32 dynamax;
PARAMETRIZE { dynamax = GIMMICK_NONE; }
PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; }
GIVEN {
ASSUME(I_SITRUS_BERRY_HEAL >= GEN_4);
ASSUME(gItemsInfo[ITEM_SITRUS_BERRY].holdEffect == HOLD_EFFECT_RESTORE_PCT_HP);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SITRUS_BERRY); }
} WHEN {
TURN { MOVE(opponent, MOVE_FLING); MOVE(player, MOVE_TACKLE, dynamax: dynamax); }
TURN { MOVE(opponent, MOVE_FLING); MOVE(player, MOVE_TACKLE, gimmick: dynamax); }
} SCENE {
MESSAGE("Wobbuffet's Sitrus Berry restored health!");
HP_BAR(player, captureDamage: &results[i].damage);
@ -617,15 +618,15 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Sitrus Berries heal based on a Pokemon's non-Dynam
SINGLE_BATTLE_TEST("(DYNAMAX) Heal Pulse heals based on a Pokemon's non-Dynamax HP", s16 damage)
{
bool32 dynamax;
PARAMETRIZE { dynamax = TRUE; }
PARAMETRIZE { dynamax = FALSE; }
u32 dynamax;
PARAMETRIZE { dynamax = GIMMICK_NONE; }
PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; }
GIVEN {
ASSUME(gMovesInfo[MOVE_HEAL_PULSE].effect == EFFECT_HEAL_PULSE);
PLAYER(SPECIES_WOBBUFFET) { HP(1); Speed(50); }
OPPONENT(SPECIES_WOBBUFFET) { MaxHP(100); Speed(100); }
} WHEN {
TURN { MOVE(opponent, MOVE_HEAL_PULSE); MOVE(player, MOVE_TACKLE, dynamax: dynamax); }
TURN { MOVE(opponent, MOVE_HEAL_PULSE); MOVE(player, MOVE_TACKLE, gimmick: dynamax); }
} SCENE {
MESSAGE("Foe Wobbuffet used Heal Pulse!");
HP_BAR(player, captureDamage: &results[i].damage);
@ -643,7 +644,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Max Strike lowers single opponent's speed")
OPPONENT(SPECIES_WOBBUFFET) { Speed(100); }
PLAYER(SPECIES_WOBBUFFET) { Speed(80); }
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE); MOVE(player, MOVE_TACKLE, dynamax: TRUE); }
TURN { MOVE(opponent, MOVE_TACKLE); MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); }
TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_TACKLE); }
} SCENE {
// turn 1
@ -669,7 +670,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) Max Strike lowers both opponents' speed")
OPPONENT(SPECIES_WOBBUFFET) {Speed(100); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(99); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_TACKLE, target: opponentLeft, dynamax: TRUE); \
TURN { MOVE(playerLeft, MOVE_TACKLE, target: opponentLeft, gimmick: GIMMICK_DYNAMAX); \
MOVE(opponentLeft, MOVE_TACKLE, target: playerLeft); \
MOVE(opponentRight, MOVE_TACKLE, target: playerLeft); }
TURN { MOVE(playerLeft, MOVE_TACKLE, target: opponentLeft); \
@ -708,7 +709,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) Max Knuckle raises both allies' attack")
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(playerLeft, MOVE_CLOSE_COMBAT, target: opponentLeft, dynamax: TRUE); \
TURN { MOVE(playerLeft, MOVE_CLOSE_COMBAT, target: opponentLeft, gimmick: GIMMICK_DYNAMAX); \
MOVE(playerRight, MOVE_TACKLE, target: opponentRight); }
TURN { MOVE(playerLeft, MOVE_CLOSE_COMBAT, target: opponentLeft); \
MOVE(playerRight, MOVE_TACKLE, target: opponentRight); }
@ -746,7 +747,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Max Flare sets up sunlight")
OPPONENT(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_EMBER, dynamax: TRUE); MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_EMBER, gimmick: GIMMICK_DYNAMAX); MOVE(opponent, MOVE_CELEBRATE); }
} SCENE {
MESSAGE("Wobbuffet used Max Flare!");
MESSAGE("The sunlight got bright!");
@ -762,7 +763,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Max Geyser sets up heavy rain")
OPPONENT(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_WATER_GUN, dynamax: TRUE); MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_WATER_GUN, gimmick: GIMMICK_DYNAMAX); MOVE(opponent, MOVE_CELEBRATE); }
} SCENE {
MESSAGE("Wobbuffet used Max Geyser!");
MESSAGE("It started to rain!");
@ -778,7 +779,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Max Hailstorm sets up hail")
OPPONENT(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_POWDER_SNOW, dynamax: TRUE); MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_POWDER_SNOW, gimmick: GIMMICK_DYNAMAX); MOVE(opponent, MOVE_CELEBRATE); }
} SCENE {
MESSAGE("Wobbuffet used Max Hailstorm!");
MESSAGE("It started to hail!");
@ -794,7 +795,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Max Rockfall sets up a sandstorm")
OPPONENT(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_ROCK_THROW, dynamax: TRUE); MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_ROCK_THROW, gimmick: GIMMICK_DYNAMAX); MOVE(opponent, MOVE_CELEBRATE); }
} SCENE {
MESSAGE("Wobbuffet used Max Rockfall!");
MESSAGE("A sandstorm brewed!");
@ -812,7 +813,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Max Overgrowth sets up Grassy Terrain")
OPPONENT(SPECIES_WOBBUFFET) { MaxHP(maxHP); HP(maxHP / 2); };
PLAYER(SPECIES_WOBBUFFET) { MaxHP(maxHP); HP(maxHP / 2); };
} WHEN {
TURN { MOVE(player, MOVE_VINE_WHIP, dynamax: TRUE); MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_VINE_WHIP, gimmick: GIMMICK_DYNAMAX); MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_VINE_WHIP); MOVE(opponent, MOVE_CELEBRATE); }
} SCENE {
MESSAGE("Wobbuffet used Max Overgrowth!");
@ -831,7 +832,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Max Mindstorm sets up Psychic Terrain")
OPPONENT(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_EXTREME_SPEED); MOVE(player, MOVE_PSYCHIC, dynamax: TRUE); }
TURN { MOVE(opponent, MOVE_EXTREME_SPEED); MOVE(player, MOVE_PSYCHIC, gimmick: GIMMICK_DYNAMAX); }
TURN { MOVE(opponent, MOVE_EXTREME_SPEED); MOVE(player, MOVE_PSYCHIC); }
} SCENE {
MESSAGE("Foe Wobbuffet used Extreme Speed!");
@ -848,7 +849,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Max Lightning sets up Electric Terrain")
OPPONENT(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_THUNDERBOLT, dynamax: TRUE); MOVE(opponent, MOVE_SPORE); }
TURN { MOVE(player, MOVE_THUNDERBOLT, gimmick: GIMMICK_DYNAMAX); MOVE(opponent, MOVE_SPORE); }
} SCENE {
MESSAGE("Wobbuffet used Max Lightning!");
MESSAGE("Foe Wobbuffet used Spore!");
@ -863,7 +864,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Max Starfall sets up Misty Terrain")
OPPONENT(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_MOONBLAST, dynamax: TRUE); MOVE(opponent, MOVE_TOXIC); }
TURN { MOVE(player, MOVE_MOONBLAST, gimmick: GIMMICK_DYNAMAX); MOVE(opponent, MOVE_TOXIC); }
} SCENE {
MESSAGE("Wobbuffet used Max Starfall!");
MESSAGE("Foe Wobbuffet used Toxic!");
@ -879,7 +880,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) G-Max Stonesurge sets up Stealth Rocks")
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_LIQUIDATION, dynamax: TRUE); }
TURN { MOVE(player, MOVE_LIQUIDATION, gimmick: GIMMICK_DYNAMAX); }
TURN { SWITCH(opponent, 1); }
} SCENE {
// turn 1
@ -899,7 +900,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) G-Max Steelsurge sets up sharp steel")
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_HATTERENE);
} WHEN {
TURN { MOVE(player, MOVE_IRON_HEAD, dynamax: TRUE); }
TURN { MOVE(player, MOVE_IRON_HEAD, gimmick: GIMMICK_DYNAMAX); }
TURN { SWITCH(opponent, 1); }
TURN { } // wait out Dynamax
TURN { MOVE(opponent, MOVE_DEFOG); }
@ -929,7 +930,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) G-Max Hydrosnipe has fixed power and ignores abili
PLAYER(SPECIES_INTELEON) { GigantamaxFactor(TRUE); }
OPPONENT(SPECIES_ARCTOVISH) { Ability(ABILITY_WATER_ABSORB); }
} WHEN {
TURN { MOVE(player, move, dynamax: TRUE); }
TURN { MOVE(player, move, gimmick: GIMMICK_DYNAMAX); }
} SCENE {
MESSAGE("Inteleon used G-Max Hydrosnipe!");
HP_BAR(opponent, captureDamage: &results[i].damage);
@ -947,7 +948,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Volt Crash paralyzes both opponents")
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(playerLeft, MOVE_THUNDERBOLT, target: opponentLeft, dynamax: TRUE); }
TURN { MOVE(playerLeft, MOVE_THUNDERBOLT, target: opponentLeft, gimmick: GIMMICK_DYNAMAX); }
} SCENE {
MESSAGE("Pikachu used G-Max Volt Crash!");
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, opponentLeft);
@ -974,7 +975,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Stun Shock paralyzes or poisons both opponen
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(playerLeft, MOVE_THUNDERBOLT, target: opponentLeft, dynamax: TRUE, \
TURN { MOVE(playerLeft, MOVE_THUNDERBOLT, target: opponentLeft, gimmick: GIMMICK_DYNAMAX, \
WITH_RNG(RNG_G_MAX_STUN_SHOCK, rng)); }
} SCENE {
MESSAGE("Toxtricity used G-Max Stun Shock!");
@ -1011,7 +1012,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Stun Shock chooses statuses before consideri
OPPONENT(SPECIES_GARBODOR);
OPPONENT(SPECIES_TRUBBISH);
} WHEN {
TURN { MOVE(playerLeft, MOVE_NUZZLE, target: opponentLeft, dynamax: TRUE, \
TURN { MOVE(playerLeft, MOVE_NUZZLE, target: opponentLeft, gimmick: GIMMICK_DYNAMAX, \
WITH_RNG(RNG_G_MAX_STUN_SHOCK, STATUS1_POISON)); }
} SCENE {
MESSAGE("Toxtricity used G-Max Stun Shock!");
@ -1044,7 +1045,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Befuddle paralyzes, poisons, or sleeps both
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(playerLeft, MOVE_BUG_BITE, target: opponentLeft, dynamax: TRUE,
TURN { MOVE(playerLeft, MOVE_BUG_BITE, target: opponentLeft, gimmick: GIMMICK_DYNAMAX,
WITH_RNG(RNG_G_MAX_BEFUDDLE, rng)); }
} SCENE {
MESSAGE("Butterfree used G-Max Befuddle!");
@ -1088,7 +1089,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Gold Rush confuses both opponents and genera
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(playerLeft, MOVE_TACKLE, target: opponentLeft, dynamax: TRUE); }
TURN { MOVE(playerLeft, MOVE_TACKLE, target: opponentLeft, gimmick: GIMMICK_DYNAMAX); }
} SCENE {
MESSAGE("Meowth used G-Max Gold Rush!");
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, opponentLeft);
@ -1108,7 +1109,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Smite confuses both opponents")
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(playerLeft, MOVE_MOONBLAST, target: opponentLeft, dynamax: TRUE); }
TURN { MOVE(playerLeft, MOVE_MOONBLAST, target: opponentLeft, gimmick: GIMMICK_DYNAMAX); }
} SCENE {
MESSAGE("Hatterene used G-Max Smite!");
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, opponentLeft);
@ -1127,7 +1128,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Cuddle infatuates both opponents, if possibl
OPPONENT(SPECIES_WOBBUFFET) { Gender(MON_FEMALE); }
OPPONENT(SPECIES_WOBBUFFET) { Gender(MON_MALE); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_TACKLE, target: opponentLeft, dynamax: TRUE); }
TURN { MOVE(playerLeft, MOVE_TACKLE, target: opponentLeft, gimmick: GIMMICK_DYNAMAX); }
} SCENE {
MESSAGE("Eevee used G-Max Cuddle!");
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_INFATUATION, opponentLeft);
@ -1148,7 +1149,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Terror traps both opponents")
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(playerLeft, MOVE_LICK, target: opponentLeft, dynamax: TRUE); }
TURN { MOVE(playerLeft, MOVE_LICK, target: opponentLeft, gimmick: GIMMICK_DYNAMAX); }
} SCENE {
MESSAGE("Gengar used G-Max Terror!");
MESSAGE("Foe Wobbuffet can't escape now!");
@ -1169,7 +1170,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Meltdown torments both opponents for 3 turns
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_SPLASH, MOVE_CELEBRATE); }
OPPONENT(SPECIES_WYNAUT) { Moves(MOVE_SPLASH, MOVE_CELEBRATE); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_IRON_HEAD, target: opponentLeft, dynamax: TRUE); \
TURN { MOVE(playerLeft, MOVE_IRON_HEAD, target: opponentLeft, gimmick: GIMMICK_DYNAMAX); \
MOVE(opponentLeft, MOVE_SPLASH); MOVE(opponentRight, MOVE_SPLASH); }
TURN { MOVE(playerLeft, MOVE_CELEBRATE, target: opponentLeft); \
MOVE(opponentLeft, MOVE_SPLASH, allowed: FALSE); \
@ -1207,7 +1208,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Wildfire sets a field effect that damages no
OPPONENT(SPECIES_WYNAUT);
OPPONENT(SPECIES_ARCANINE);
} WHEN {
TURN { MOVE(playerLeft, MOVE_EMBER, target: opponentLeft, dynamax: TRUE); }
TURN { MOVE(playerLeft, MOVE_EMBER, target: opponentLeft, gimmick: GIMMICK_DYNAMAX); }
TURN { }
TURN { SWITCH(opponentLeft, 2); }
TURN { }
@ -1256,7 +1257,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Replenish recycles allies' berries 50\% of t
MOVE(playerRight, MOVE_STUFF_CHEEKS); \
MOVE(opponentLeft, MOVE_STUFF_CHEEKS); \
MOVE(opponentRight, MOVE_STUFF_CHEEKS); }
TURN { MOVE(playerLeft, MOVE_TACKLE, target: opponentLeft, dynamax: TRUE); }
TURN { MOVE(playerLeft, MOVE_TACKLE, target: opponentLeft, gimmick: GIMMICK_DYNAMAX); }
} SCENE {
// turn 1
MESSAGE("Using Apicot Berry, the Sp. Def of Snorlax rose!");
@ -1281,7 +1282,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Snooze makes only the target drowsy")
OPPONENT(SPECIES_BLISSEY);
OPPONENT(SPECIES_CHANSEY);
} WHEN {
TURN { MOVE(playerLeft, MOVE_DARK_PULSE, target: opponentLeft, dynamax: TRUE); }
TURN { MOVE(playerLeft, MOVE_DARK_PULSE, target: opponentLeft, gimmick: GIMMICK_DYNAMAX); }
TURN { }
} SCENE {
// turn 1
@ -1304,7 +1305,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Finale heals allies by 1/6 of their health")
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(playerLeft, MOVE_MOONBLAST, target: opponentLeft, dynamax: TRUE); }
TURN { MOVE(playerLeft, MOVE_MOONBLAST, target: opponentLeft, gimmick: GIMMICK_DYNAMAX); }
} SCENE {
MESSAGE("Alcremie used G-Max Finale!");
HP_BAR(playerLeft, captureDamage: &damage1);
@ -1324,7 +1325,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Sweetness cures allies' status conditions")
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(playerLeft, MOVE_VINE_WHIP, target: opponentLeft, dynamax: TRUE); }
TURN { MOVE(playerLeft, MOVE_VINE_WHIP, target: opponentLeft, gimmick: GIMMICK_DYNAMAX); }
} SCENE {
MESSAGE("Appletun used G-Max Sweetness!");
STATUS_ICON(playerLeft, none: TRUE);
@ -1345,7 +1346,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Centiferno traps both opponents in Fire Spin
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(playerLeft, MOVE_FLAME_CHARGE, target: opponentLeft, dynamax: TRUE); }
TURN { MOVE(playerLeft, MOVE_FLAME_CHARGE, target: opponentLeft, gimmick: GIMMICK_DYNAMAX); }
TURN { SWITCH(playerLeft, 2); }
} SCENE {
// turn 1
@ -1373,7 +1374,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Chi Strike boosts allies' crit chance")
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(playerLeft, MOVE_FORCE_PALM, target: opponentLeft, dynamax: TRUE); }
TURN { MOVE(playerLeft, MOVE_FORCE_PALM, target: opponentLeft, gimmick: GIMMICK_DYNAMAX); }
TURN { MOVE(playerLeft, MOVE_FORCE_PALM, target: opponentLeft); }
TURN { MOVE(playerLeft, MOVE_FORCE_PALM, target: opponentLeft); \
MOVE(playerRight, MOVE_FOCUS_ENERGY); }
@ -1406,7 +1407,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Depletion takes away 2 PP from the target's
OPPONENT(SPECIES_SABLEYE) { Ability(ABILITY_PRANKSTER); }
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(playerLeft, MOVE_DRAGON_CLAW, target: opponentLeft, dynamax: TRUE); }
TURN { MOVE(playerLeft, MOVE_DRAGON_CLAW, target: opponentLeft, gimmick: GIMMICK_DYNAMAX); }
} SCENE {
MESSAGE("Foe Sableye used Celebrate!");
MESSAGE("Duraludon used G-Max Depletion!");
@ -1428,11 +1429,11 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max One Blow bypasses Max Guard for full damage"
OPPONENT(SPECIES_WYNAUT);
} WHEN {
if (protect)
TURN { MOVE(playerLeft, MOVE_WICKED_BLOW, target: opponentLeft, dynamax: TRUE); \
MOVE(opponentLeft, MOVE_PROTECT, dynamax: TRUE); }
TURN { MOVE(playerLeft, MOVE_WICKED_BLOW, target: opponentLeft, gimmick: GIMMICK_DYNAMAX); \
MOVE(opponentLeft, MOVE_PROTECT, gimmick: GIMMICK_DYNAMAX); }
else
TURN { MOVE(playerLeft, MOVE_WICKED_BLOW, target: opponentLeft, dynamax: TRUE); \
MOVE(opponentLeft, MOVE_PSYCHIC, target: playerLeft, dynamax: TRUE); }
TURN { MOVE(playerLeft, MOVE_WICKED_BLOW, target: opponentLeft, gimmick: GIMMICK_DYNAMAX); \
MOVE(opponentLeft, MOVE_PSYCHIC, target: playerLeft, gimmick: GIMMICK_DYNAMAX); }
} SCENE {
if (protect)
MESSAGE("Foe Wobbuffet used Max Guard!");
@ -1453,8 +1454,8 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) Max Flare doesn't softlock the game when fainting
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(playerLeft, MOVE_PROTECT, dynamax: TRUE);
MOVE(opponentLeft, MOVE_V_CREATE, target: playerRight, dynamax: TRUE);
TURN { MOVE(playerLeft, MOVE_PROTECT, gimmick: GIMMICK_DYNAMAX);
MOVE(opponentLeft, MOVE_V_CREATE, target: playerRight, gimmick: GIMMICK_DYNAMAX);
SEND_OUT(playerRight, 2); }
TURN { }
}
@ -1466,7 +1467,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Max Moves don't execute effects on fainted battler
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { HP(1); };
} WHEN {
TURN { MOVE(player, MOVE_TACKLE, dynamax: TRUE); }
TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_DYNAMAX_GROWTH, player);
MESSAGE("Wobbuffet used Max Strike!");
@ -1483,7 +1484,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Moxie clones can be triggered by Max Moves faintin
OPPONENT(SPECIES_WOBBUFFET) { HP(1); }
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_WATERFALL, dynamax: TRUE); SEND_OUT(opponent, 1); }
TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_WATERFALL, gimmick: GIMMICK_DYNAMAX); SEND_OUT(opponent, 1); }
} SCENE {
MESSAGE("Foe Wobbuffet fainted!");
ABILITY_POPUP(player, ABILITY_MOXIE);

View File

@ -6,13 +6,13 @@
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; }
PARAMETRIZE { tera = GIMMICK_NONE; }
PARAMETRIZE { tera = GIMMICK_TERA; }
GIVEN {
PLAYER(SPECIES_BULBASAUR) { TeraType(TYPE_NORMAL); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_VINE_WHIP, tera: tera); }
TURN { MOVE(player, MOVE_VINE_WHIP, gimmick: tera); }
TURN { MOVE(player, MOVE_SLUDGE_BOMB); }
} SCENE {
MESSAGE("Bulbasaur used Vine Whip!");
@ -30,13 +30,13 @@ SINGLE_BATTLE_TEST("(TERA) Terastallizing into a different type preserves other
SINGLE_BATTLE_TEST("(TERA) Terastallizing does not affect the power of non-STAB moves", s16 damage)
{
bool32 tera;
PARAMETRIZE { tera = FALSE; }
PARAMETRIZE { tera = TRUE; }
PARAMETRIZE { tera = GIMMICK_NONE; }
PARAMETRIZE { tera = GIMMICK_TERA; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_PSYCHIC); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_HEADBUTT, tera: tera); }
TURN { MOVE(player, MOVE_HEADBUTT, gimmick: tera); }
} SCENE {
MESSAGE("Wobbuffet used Headbutt!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_HEADBUTT, player);
@ -49,13 +49,13 @@ SINGLE_BATTLE_TEST("(TERA) Terastallizing does not affect the power of non-STAB
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; }
PARAMETRIZE { tera = GIMMICK_NONE; }
PARAMETRIZE { tera = GIMMICK_TERA; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_NORMAL); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_HEADBUTT, tera: tera); }
TURN { MOVE(player, MOVE_HEADBUTT, gimmick: tera); }
} SCENE {
MESSAGE("Wobbuffet used Headbutt!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_HEADBUTT, player);
@ -69,13 +69,13 @@ SINGLE_BATTLE_TEST("(TERA) Terastallizing into a different type gives that type
SINGLE_BATTLE_TEST("(TERA) Terastallizing into the same type gives that type 2x STAB", s16 damage)
{
bool32 tera;
PARAMETRIZE { tera = FALSE; }
PARAMETRIZE { tera = TRUE; }
PARAMETRIZE { tera = GIMMICK_NONE; }
PARAMETRIZE { tera = GIMMICK_TERA; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_PSYCHIC); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_PSYCHIC, tera: tera); }
TURN { MOVE(player, MOVE_PSYCHIC, gimmick: tera); }
} SCENE {
MESSAGE("Wobbuffet used Psychic!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYCHIC, player);
@ -89,13 +89,13 @@ SINGLE_BATTLE_TEST("(TERA) Terastallizing into the same type gives that type 2x
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; }
PARAMETRIZE { tera = GIMMICK_NONE; }
PARAMETRIZE { tera = GIMMICK_TERA; }
GIVEN {
PLAYER(SPECIES_CRAWDAUNT) { Ability(ABILITY_ADAPTABILITY); TeraType(TYPE_NORMAL); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_HEADBUTT, tera: tera); }
TURN { MOVE(player, MOVE_HEADBUTT, gimmick: tera); }
} SCENE {
MESSAGE("Crawdaunt used Headbutt!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_HEADBUTT, player);
@ -109,13 +109,13 @@ SINGLE_BATTLE_TEST("(TERA) Terastallizing into a different type with Adaptabilit
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; }
PARAMETRIZE { tera = GIMMICK_NONE; }
PARAMETRIZE { tera = GIMMICK_TERA; }
GIVEN {
PLAYER(SPECIES_CRAWDAUNT) { Ability(ABILITY_ADAPTABILITY); TeraType(TYPE_WATER); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_WATER_PULSE, tera: tera); }
TURN { MOVE(player, MOVE_WATER_PULSE, gimmick: tera); }
} SCENE {
MESSAGE("Crawdaunt used Water Pulse!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_PULSE, player);
@ -129,14 +129,14 @@ SINGLE_BATTLE_TEST("(TERA) Terastallizing into the same type with Adaptability g
SINGLE_BATTLE_TEST("(TERA) Terastallizing boosts moves of the same type to 60 BP", s16 damage)
{
bool32 tera;
PARAMETRIZE { tera = FALSE; }
PARAMETRIZE { tera = TRUE; }
PARAMETRIZE { tera = GIMMICK_NONE; }
PARAMETRIZE { tera = GIMMICK_TERA; }
GIVEN {
ASSUME(gMovesInfo[MOVE_ABSORB].power == 20);
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_GRASS); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_ABSORB, tera: tera); }
TURN { MOVE(player, MOVE_ABSORB, gimmick: tera); }
} SCENE {
MESSAGE("Wobbuffet used Absorb!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_ABSORB, player);
@ -150,14 +150,14 @@ SINGLE_BATTLE_TEST("(TERA) Terastallizing boosts moves of the same type to 60 BP
SINGLE_BATTLE_TEST("(TERA) Terastallization's 60 BP floor occurs after Technician", s16 damage)
{
bool32 tera;
PARAMETRIZE { tera = FALSE; }
PARAMETRIZE { tera = TRUE; }
PARAMETRIZE { tera = GIMMICK_NONE; }
PARAMETRIZE { tera = GIMMICK_TERA; }
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); }
TURN { MOVE(player, MOVE_MEGA_DRAIN, gimmick: tera); }
} SCENE {
MESSAGE("Mr. Mime used Mega Drain!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_MEGA_DRAIN, player);
@ -171,13 +171,13 @@ SINGLE_BATTLE_TEST("(TERA) Terastallization's 60 BP floor occurs after Technicia
SINGLE_BATTLE_TEST("(TERA) Terastallization's 60 BP floor occurs after Technician", s16 damage)
{
bool32 tera;
PARAMETRIZE { tera = FALSE; }
PARAMETRIZE { tera = TRUE; }
PARAMETRIZE { tera = GIMMICK_NONE; }
PARAMETRIZE { tera = GIMMICK_TERA; }
GIVEN {
PLAYER(SPECIES_MR_MIME) { Ability(ABILITY_TECHNICIAN); TeraType(TYPE_PSYCHIC); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_STORED_POWER, tera: tera); }
TURN { MOVE(player, MOVE_STORED_POWER, gimmick: tera); }
} SCENE {
MESSAGE("Mr. Mime used Stored Power!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_STORED_POWER, player);
@ -191,13 +191,13 @@ SINGLE_BATTLE_TEST("(TERA) Terastallization's 60 BP floor occurs after Technicia
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; }
PARAMETRIZE { tera = GIMMICK_NONE; }
PARAMETRIZE { tera = GIMMICK_TERA; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_NORMAL); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_FURY_SWIPES, tera: tera); }
TURN { MOVE(player, MOVE_FURY_SWIPES, gimmick: tera); }
} SCENE {
MESSAGE("Wobbuffet used Fury Swipes!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_FURY_SWIPES, player);
@ -210,13 +210,13 @@ SINGLE_BATTLE_TEST("(TERA) Terastallization's 60 BP floor does not apply to mult
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; }
PARAMETRIZE { tera = GIMMICK_NONE; }
PARAMETRIZE { tera = GIMMICK_TERA; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_NORMAL); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_QUICK_ATTACK, tera: tera); }
TURN { MOVE(player, MOVE_QUICK_ATTACK, gimmick: tera); }
} SCENE {
MESSAGE("Wobbuffet used Quick Attack!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, player);
@ -231,13 +231,13 @@ SINGLE_BATTLE_TEST("(TERA) Terastallization's 60 BP floor does not apply to prio
SINGLE_BATTLE_TEST("(TERA) Terastallization changes type effectiveness", s16 damage)
{
bool32 tera;
PARAMETRIZE { tera = FALSE; }
PARAMETRIZE { tera = TRUE; }
PARAMETRIZE { tera = GIMMICK_NONE; }
PARAMETRIZE { tera = GIMMICK_TERA; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_GRASS); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, tera: tera); MOVE(opponent, MOVE_WATER_GUN); }
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: tera); MOVE(opponent, MOVE_WATER_GUN); }
} SCENE {
MESSAGE("Foe Wobbuffet used Water Gun!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, opponent);
@ -253,7 +253,7 @@ SINGLE_BATTLE_TEST("(TERA) Terastallization changes type effectiveness")
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_FLYING); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, tera: TRUE); MOVE(opponent, MOVE_EARTHQUAKE); }
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); MOVE(opponent, MOVE_EARTHQUAKE); }
} SCENE {
MESSAGE("Foe Wobbuffet used Earthquake!");
MESSAGE("It doesn't affect Wobbuffet…");
@ -268,7 +268,7 @@ SINGLE_BATTLE_TEST("(TERA) Terastallization persists across switches")
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, tera: TRUE); MOVE(opponent, MOVE_EARTHQUAKE); }
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); MOVE(opponent, MOVE_EARTHQUAKE); }
TURN { SWITCH(player, 1); }
TURN { SWITCH(player, 0); }
TURN { MOVE(opponent, MOVE_EARTHQUAKE); }
@ -292,7 +292,7 @@ SINGLE_BATTLE_TEST("(TERA) Terastallization changes the effect of Curse")
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_GHOST); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CURSE, tera: TRUE); }
TURN { MOVE(player, MOVE_CURSE, gimmick: GIMMICK_TERA); }
} SCENE {
MESSAGE("Wobbuffet used Curse!");
HP_BAR(player);
@ -307,7 +307,7 @@ SINGLE_BATTLE_TEST("(TERA) Roost does not remove the user's Flying type while Te
PLAYER(SPECIES_ZAPDOS) { HP(1); TeraType(TYPE_FLYING); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_ROOST, tera: TRUE); MOVE(opponent, MOVE_ICE_BEAM); }
TURN { MOVE(player, MOVE_ROOST, gimmick: GIMMICK_TERA); MOVE(opponent, MOVE_ICE_BEAM); }
} SCENE {
MESSAGE("Zapdos used Roost!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROOST, player);
@ -327,7 +327,7 @@ SINGLE_BATTLE_TEST("(TERA) Type-changing moves fail against a Terastallized Poke
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_PSYCHIC); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, tera: TRUE); MOVE(opponent, move); }
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); MOVE(opponent, move); }
} SCENE {
if (move != MOVE_SOAK)
NOT { ANIMATION(ANIM_TYPE_MOVE, move, opponent); }
@ -341,7 +341,7 @@ SINGLE_BATTLE_TEST("(TERA) Reflect Type fails if used by a Terastallized Pokemon
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_PSYCHIC); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_REFLECT_TYPE, tera: TRUE); }
TURN { MOVE(player, MOVE_REFLECT_TYPE, gimmick: GIMMICK_TERA); }
} SCENE {
MESSAGE("Wobbuffet used Reflect Type!");
MESSAGE("But it failed!");
@ -354,7 +354,7 @@ SINGLE_BATTLE_TEST("(TERA) Conversion fails if used by a Terastallized Pokemon")
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_PSYCHIC); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CONVERSION, tera: TRUE); }
TURN { MOVE(player, MOVE_CONVERSION, gimmick: GIMMICK_TERA); }
} SCENE {
MESSAGE("Wobbuffet used Conversion!");
MESSAGE("But it failed!");
@ -368,7 +368,7 @@ SINGLE_BATTLE_TEST("(TERA) Conversion2 fails if used by a Terastallized Pokemon"
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE); }
TURN { MOVE(player, MOVE_CONVERSION_2, tera: TRUE); }
TURN { MOVE(player, MOVE_CONVERSION_2, gimmick: GIMMICK_TERA); }
} SCENE {
MESSAGE("Wobbuffet used Conversion 2!");
MESSAGE("But it failed!");
@ -381,7 +381,7 @@ SINGLE_BATTLE_TEST("(TERA) Reflect Type copies a Terastallized Pokemon's Tera Ty
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_CELEBRATE); MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); }
TURN { MOVE(opponent, MOVE_REFLECT_TYPE); }
TURN { MOVE(player, MOVE_TACKLE); }
} SCENE {
@ -401,8 +401,8 @@ SINGLE_BATTLE_TEST("(TERA) Synchronoise uses a Terastallized Pokemon's Tera Type
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); }
TURN { MOVE(opponent, MOVE_SYNCHRONOISE); MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); }
TURN { MOVE(opponent, MOVE_SYNCHRONOISE, gimmick: GIMMICK_TERA); }
} SCENE {
// turn 1
MESSAGE("Foe Wobbuffet used Synchronoise!");
@ -420,7 +420,7 @@ SINGLE_BATTLE_TEST("(TERA) Revelation Dance uses a Terastallized Pokemon's Tera
PLAYER(SPECIES_ORICORIO) { TeraType(TYPE_NORMAL); }
OPPONENT(SPECIES_GENGAR);
} WHEN {
TURN { MOVE(player, MOVE_REVELATION_DANCE, tera: TRUE); }
TURN { MOVE(player, MOVE_REVELATION_DANCE, gimmick: GIMMICK_TERA); }
} SCENE {
MESSAGE("Oricorio used Revelation Dance!");
MESSAGE("It doesn't affect Foe Gengar…");
@ -439,7 +439,7 @@ SINGLE_BATTLE_TEST("(TERA) Double Shock does not remove the user's Electric type
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, gimmick: GIMMICK_TERA); 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); }
@ -479,9 +479,9 @@ SINGLE_BATTLE_TEST("(TERA) Transform does not copy the target's Tera Type, and i
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_CELEBRATE, gimmick: GIMMICK_TERA); MOVE(opponent, MOVE_TRANSFORM); }
TURN { MOVE(player, MOVE_EARTHQUAKE); }
// TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_TACKLE, target: player, tera: TRUE); }
// TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_TACKLE, target: player, gimmick: GIMMICK_TERA); }
} SCENE {
// turn 2
MESSAGE("Wobbuffet used Earthquake!");
@ -498,13 +498,13 @@ SINGLE_BATTLE_TEST("(TERA) Transform does not copy the target's Tera Type, and i
SINGLE_BATTLE_TEST("(TERA) Stellar type does not change the user's defensive profile", s16 damage)
{
bool32 tera;
PARAMETRIZE { tera = FALSE; }
PARAMETRIZE { tera = TRUE; }
PARAMETRIZE { tera = GIMMICK_NONE; }
PARAMETRIZE { tera = GIMMICK_TERA; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, tera: tera); MOVE(opponent, MOVE_PSYCHIC); }
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: tera); MOVE(opponent, MOVE_PSYCHIC); }
} SCENE {
MESSAGE("Foe Wobbuffet used Psychic!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYCHIC, opponent);
@ -520,7 +520,7 @@ SINGLE_BATTLE_TEST("(TERA) Reflect Type copies a Stellar-type Pokemon's base typ
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_CELEBRATE); MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); }
TURN { MOVE(opponent, MOVE_REFLECT_TYPE); }
TURN { MOVE(player, MOVE_TACKLE); }
} SCENE {
@ -541,7 +541,7 @@ SINGLE_BATTLE_TEST("(TERA) Revelation Dance uses a Stellar-type Pokemon's base t
PLAYER(SPECIES_ORICORIO_SENSU) { TeraType(TYPE_STELLAR); }
OPPONENT(SPECIES_GUMSHOOS);
} WHEN {
TURN { MOVE(player, MOVE_REVELATION_DANCE, tera: TRUE); }
TURN { MOVE(player, MOVE_REVELATION_DANCE, gimmick: GIMMICK_TERA); }
} SCENE {
MESSAGE("Oricorio used Revelation Dance!");
MESSAGE("It doesn't affect Foe Gumshoos…");
@ -555,7 +555,7 @@ SINGLE_BATTLE_TEST("(TERA) Conversion2 fails if last hit by a Stellar-type move"
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TERA_BLAST, tera: TRUE); }
TURN { MOVE(player, MOVE_TERA_BLAST, gimmick: GIMMICK_TERA); }
TURN { MOVE(opponent, MOVE_CONVERSION_2); }
} SCENE {
// turn 1
@ -573,7 +573,7 @@ SINGLE_BATTLE_TEST("(TERA) Roost does not remove Flying-type ground immunity whe
PLAYER(SPECIES_ZAPDOS) { HP(1); TeraType(TYPE_STELLAR); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_ROOST, tera: TRUE); MOVE(opponent, MOVE_ICE_BEAM); }
TURN { MOVE(player, MOVE_ROOST, gimmick: GIMMICK_TERA); MOVE(opponent, MOVE_ICE_BEAM); }
} SCENE {
MESSAGE("Zapdos used Roost!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROOST, player);
@ -591,7 +591,7 @@ SINGLE_BATTLE_TEST("(TERA) Terastallizing into the Stellar-type provides a one-t
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_EXTRASENSORY); }
TURN { MOVE(player, MOVE_EXTRASENSORY, tera: TRUE); }
TURN { MOVE(player, MOVE_EXTRASENSORY, gimmick: GIMMICK_TERA); }
TURN { MOVE(player, MOVE_EXTRASENSORY); }
} SCENE {
// turn 1
@ -621,7 +621,7 @@ SINGLE_BATTLE_TEST("(TERA) Terastallizing into the Stellar-type provides a one-t
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TAKE_DOWN); }
TURN { MOVE(player, MOVE_TAKE_DOWN, tera: TRUE); }
TURN { MOVE(player, MOVE_TAKE_DOWN, gimmick: GIMMICK_TERA); }
TURN { MOVE(player, MOVE_TAKE_DOWN); }
} SCENE {
// turn 1
@ -652,7 +652,7 @@ SINGLE_BATTLE_TEST("(TERA) Terastallizing into the Stellar type boosts all moves
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_MEGA_DRAIN); }
TURN { MOVE(player, MOVE_MEGA_DRAIN, tera: TRUE); }
TURN { MOVE(player, MOVE_MEGA_DRAIN, gimmick: GIMMICK_TERA); }
TURN { MOVE(player, MOVE_MEGA_DRAIN); }
TURN { MOVE(player, MOVE_BUBBLE); }
} SCENE {
@ -686,7 +686,7 @@ SINGLE_BATTLE_TEST("(TERA) Protean cannot change the type of a Terastallized Pok
PLAYER(SPECIES_GRENINJA) { Ability(ABILITY_PROTEAN); TeraType(TYPE_GRASS); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_BUBBLE, tera: TRUE);
TURN { MOVE(player, MOVE_BUBBLE, gimmick: GIMMICK_TERA);
MOVE(opponent, MOVE_EMBER); }
} SCENE {
MESSAGE("Greninja used Bubble!");
@ -702,7 +702,7 @@ SINGLE_BATTLE_TEST("(TERA) Status moves don't expend Stellar's one-time type boo
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_GROWL, tera: TRUE); }
TURN { MOVE(player, MOVE_GROWL, gimmick: GIMMICK_TERA); }
TURN { MOVE(player, MOVE_TAKE_DOWN); }
TURN { MOVE(player, MOVE_TAKE_DOWN); }
} SCENE {
@ -730,7 +730,7 @@ SINGLE_BATTLE_TEST("(TERA) Stellar type's one-time boost factors in dynamically-
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_WEATHER_BALL, gimmick: GIMMICK_TERA); 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); }
@ -790,7 +790,7 @@ SINGLE_BATTLE_TEST("(TERA) All type indicators function correctly")
PLAYER(SPECIES_WOBBUFFET) { TeraType(type); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, tera: TRUE); }
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); }
}
}
@ -806,7 +806,7 @@ SINGLE_BATTLE_TEST("(TERA) Pokemon with Tera forms change upon Terastallizing")
PLAYER(species);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, tera: TRUE); }
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); }
} THEN {
EXPECT_EQ(player->species, targetSpecies);
}

601
test/battle/gimmick/zmove.c Normal file
View File

@ -0,0 +1,601 @@
#include "global.h"
#include "test/battle.h"
#include "constants/battle_z_move_effects.h"
// Basic Functionality
SINGLE_BATTLE_TEST("(Z-MOVE) Z-Moves do not retain priority")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_QUICK_ATTACK].type == TYPE_NORMAL);
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMALIUM_Z); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE);
MOVE(player, MOVE_QUICK_ATTACK, gimmick: GIMMICK_Z_MOVE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BREAKNECK_BLITZ, player);
}
}
SINGLE_BATTLE_TEST("(Z-MOVE) Z-Moves are not affected by -ate abilities")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_TACKLE].type == TYPE_NORMAL);
ASSUME(gSpeciesInfo[SPECIES_SWELLOW].types[1] == TYPE_FLYING);
PLAYER(SPECIES_AURORUS) { Ability(ABILITY_REFRIGERATE); Item(ITEM_NORMALIUM_Z); }
OPPONENT(SPECIES_SWELLOW);
} WHEN {
TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_Z_MOVE); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BREAKNECK_BLITZ, player);
NOT { MESSAGE("It's super effective!"); }
}
}
SINGLE_BATTLE_TEST("(Z-MOVE) Z-Moves are affected by Ion Deluge")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_TACKLE].type == TYPE_NORMAL);
ASSUME(gMovesInfo[MOVE_ION_DELUGE].effect == EFFECT_ION_DELUGE);
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMALIUM_Z); }
OPPONENT(SPECIES_SWELLOW);
} WHEN {
TURN { MOVE(opponent, MOVE_ION_DELUGE); MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_Z_MOVE); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BREAKNECK_BLITZ, player);
MESSAGE("It's super effective!");
}
}
SINGLE_BATTLE_TEST("(Z-MOVE) Z-Moves deal 1/4 damage through protect", s16 damage)
{
bool32 protected;
PARAMETRIZE { protected = TRUE; }
PARAMETRIZE { protected = FALSE; }
GIVEN {
ASSUME(gMovesInfo[MOVE_TACKLE].type == TYPE_NORMAL);
ASSUME(gMovesInfo[MOVE_PROTECT].effect == EFFECT_PROTECT);
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMALIUM_Z); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
if (protected)
TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_Z_MOVE); MOVE(opponent, MOVE_PROTECT); }
else
TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_Z_MOVE); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(4), results[1].damage);
}
}
// Status Z-Effects
SINGLE_BATTLE_TEST("(Z-MOVE) Z_EFFECT_RESET_STATS clears a battler's negative stat stages")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_LEECH_SEED].zMove.effect == Z_EFFECT_RESET_STATS);
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_GRASSIUM_Z); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SWORDS_DANCE); MOVE(opponent, MOVE_SCREECH); }
TURN { MOVE(player, MOVE_LEECH_SEED, gimmick: GIMMICK_Z_MOVE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_LEECH_SEED, player);
} THEN {
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 2);
EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
}
}
SINGLE_BATTLE_TEST("(Z-MOVE) Z_EFFECT_ALL_STATS_UP raises all of a battler's stat stages by one")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_CELEBRATE].type == TYPE_NORMAL);
ASSUME(gMovesInfo[MOVE_CELEBRATE].zMove.effect == Z_EFFECT_ALL_STATS_UP_1);
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMALIUM_Z); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_Z_MOVE); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
} THEN {
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(player->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(player->statStages[STAT_SPEED], DEFAULT_STAT_STAGE + 1);
}
}
SINGLE_BATTLE_TEST("(Z-MOVE) Z_EFFECT_BOOST_CRITS raises a battler's critical hit ratio")
{
PASSES_RANDOMLY(1, 2, RNG_CRITICAL_HIT);
GIVEN {
ASSUME(gMovesInfo[MOVE_FORESIGHT].type == TYPE_NORMAL);
ASSUME(gMovesInfo[MOVE_FORESIGHT].zMove.effect == Z_EFFECT_BOOST_CRITS);
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMALIUM_Z); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_FORESIGHT, gimmick: GIMMICK_Z_MOVE); }
TURN { MOVE(player, MOVE_TACKLE); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
MESSAGE("A critical hit!");
}
}
DOUBLE_BATTLE_TEST("(Z-MOVE) Z_EFFECT_FOLLOW_ME redirects attacks to the user")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_DESTINY_BOND].type == TYPE_GHOST);
ASSUME(gMovesInfo[MOVE_DESTINY_BOND].zMove.effect == Z_EFFECT_FOLLOW_ME);
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_GHOSTIUM_Z); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(playerLeft, MOVE_DESTINY_BOND, gimmick: GIMMICK_Z_MOVE);
MOVE(opponentLeft, MOVE_TACKLE, target: playerRight); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, playerLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_DESTINY_BOND, playerLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponentLeft);
NOT { HP_BAR(playerRight); }
HP_BAR(playerLeft);
}
}
SINGLE_BATTLE_TEST("(Z-MOVE) Z_EFFECT_RESTORE_REPLACEMENT_HP fully heals the replacement battler's HP")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_PARTING_SHOT].type == TYPE_DARK);
ASSUME(gMovesInfo[MOVE_PARTING_SHOT].zMove.effect == Z_EFFECT_RESTORE_REPLACEMENT_HP);
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_DARKINIUM_Z); }
PLAYER(SPECIES_WYNAUT) { HP(1); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_PARTING_SHOT, gimmick: GIMMICK_Z_MOVE); SEND_OUT(player, 1); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, player);
HP_BAR(player);
} THEN {
EXPECT_EQ(player->species, SPECIES_WYNAUT);
EXPECT_EQ(player->hp, player->maxHP);
}
}
// This tests the functionality of Z_EFFECT_RECOVER_HP and Z_EFFECT_ATK_UP_1 (and thus by extension all stat-up Z-effects)
SINGLE_BATTLE_TEST("(Z-MOVE) Z_EFFECT_CURSE activates Z_EFFECT_RECOVER_HP or Z_EFFECT_ATK_UP_1 depending on the type of the battler")
{
u32 species;
PARAMETRIZE { species = SPECIES_WOBBUFFET; }
PARAMETRIZE { species = SPECIES_DUSCLOPS; }
GIVEN {
ASSUME(gMovesInfo[MOVE_CURSE].type == TYPE_GHOST);
ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[0] != TYPE_GHOST);
ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[1] != TYPE_GHOST);
ASSUME(gSpeciesInfo[SPECIES_DUSCLOPS].types[0] == TYPE_GHOST);
ASSUME(gMovesInfo[MOVE_CURSE].zMove.effect == Z_EFFECT_CURSE);
PLAYER(species) { Item(ITEM_GHOSTIUM_Z); HP(1); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CURSE, gimmick: GIMMICK_Z_MOVE); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
if (species == SPECIES_DUSCLOPS) {
HP_BAR(player);
NOT { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); }
ANIMATION(ANIM_TYPE_MOVE, MOVE_CURSE, player);
HP_BAR(player);
} else {
NOT { HP_BAR(player); }
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CURSE, player);
NOT { HP_BAR(player); }
}
} THEN {
if (species == SPECIES_DUSCLOPS) {
EXPECT_MUL_EQ(player->maxHP, UQ_4_12(0.50), player->hp); // heal to full HP then cut by half
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
} else {
EXPECT_EQ(player->hp, 1);
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 2); // +1 from Curse, +1 from Z-Effect
}
}
}
// Specific Z-Move Interactions
SINGLE_BATTLE_TEST("(Z-MOVE) Z-Mirror Move raises the user's attack by two stages and copies the last used non-status move as a Z-Move")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_MIRROR_MOVE].type == TYPE_FLYING);
ASSUME(gMovesInfo[MOVE_MIRROR_MOVE].zMove.effect == Z_EFFECT_ATK_UP_2);
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_FLYINIUM_Z); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE); MOVE(player, MOVE_MIRROR_MOVE, gimmick: GIMMICK_Z_MOVE); }
TURN { MOVE(player, MOVE_TACKLE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BREAKNECK_BLITZ, player);
// extra turn to make sure that everything resets properly
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
} THEN {
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 2);
}
}
SINGLE_BATTLE_TEST("(Z-MOVE) Z-Mirror Move raises the user's attack by two stages and copies the last used status move regularly")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_MIRROR_MOVE].type == TYPE_FLYING);
ASSUME(gMovesInfo[MOVE_MIRROR_MOVE].zMove.effect == Z_EFFECT_ATK_UP_2);
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_FLYINIUM_Z); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_SCREECH); MOVE(player, MOVE_MIRROR_MOVE, gimmick: GIMMICK_Z_MOVE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCREECH, opponent);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCREECH, player);
} THEN {
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 2); // Z-Screech would cause an additional attack stat stage (reaching +3)
}
}
SINGLE_BATTLE_TEST("(Z-MOVE) Z-Copycat raises the user's accuracy by one stage and copies the last used non-status move as a Z-Move")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_COPYCAT].type == TYPE_NORMAL);
ASSUME(gMovesInfo[MOVE_COPYCAT].zMove.effect == Z_EFFECT_ACC_UP_1);
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMALIUM_Z); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE); MOVE(player, MOVE_COPYCAT, gimmick: GIMMICK_Z_MOVE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BREAKNECK_BLITZ, player);
} THEN {
EXPECT_EQ(player->statStages[STAT_ACC], DEFAULT_STAT_STAGE + 1);
}
}
SINGLE_BATTLE_TEST("(Z-MOVE) Z-Me First raises the user's speed by two stages and copies the last used non-status move as a Z-Move with boosted damage", s16 damage)
{
u32 meFirst;
PARAMETRIZE { meFirst = TRUE; }
PARAMETRIZE { meFirst = FALSE; }
GIVEN {
ASSUME(gMovesInfo[MOVE_ME_FIRST].type == TYPE_NORMAL);
ASSUME(gMovesInfo[MOVE_ME_FIRST].zMove.effect == Z_EFFECT_SPD_UP_2);
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMALIUM_Z); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
if (meFirst)
TURN { MOVE(player, MOVE_ME_FIRST, gimmick: GIMMICK_Z_MOVE); MOVE(opponent, MOVE_TACKLE); }
else
TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_Z_MOVE); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
if (meFirst)
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BREAKNECK_BLITZ, player);
HP_BAR(opponent, captureDamage: &results[i].damage);
} THEN {
if (meFirst)
EXPECT_EQ(player->statStages[STAT_SPEED], DEFAULT_STAT_STAGE + 2);
} FINALLY {
EXPECT_MUL_EQ(results[1].damage, UQ_4_12(1.50), results[0].damage);
}
}
SINGLE_BATTLE_TEST("(Z-MOVE) Z-Nature Power transforms into different Z-Moves based on the current terrain")
{
u32 terrainMove = MOVE_NONE;
u32 zMove = MOVE_NONE;
PARAMETRIZE { terrainMove = MOVE_ELECTRIC_TERRAIN; zMove = gTypesInfo[TYPE_ELECTRIC].zMove; }
PARAMETRIZE { terrainMove = MOVE_PSYCHIC_TERRAIN; zMove = gTypesInfo[TYPE_PSYCHIC].zMove; }
PARAMETRIZE { terrainMove = MOVE_GRASSY_TERRAIN; zMove = gTypesInfo[TYPE_GRASS].zMove; }
PARAMETRIZE { terrainMove = MOVE_MISTY_TERRAIN; zMove = gTypesInfo[TYPE_FAIRY].zMove; }
GIVEN {
ASSUME(gMovesInfo[MOVE_NATURE_POWER].type == TYPE_NORMAL);
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMALIUM_Z); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, terrainMove); }
TURN { MOVE(player, MOVE_NATURE_POWER, gimmick: GIMMICK_Z_MOVE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, terrainMove, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_MOVE, zMove, player);
}
}
SINGLE_BATTLE_TEST("(Z-MOVE) Z-Hidden Power always transforms into Breakneck Blitz")
{
u8 iv;
PARAMETRIZE { iv = 0; } // test different Hidden Power types
PARAMETRIZE { iv = 10; }
PARAMETRIZE { iv = 21; }
PARAMETRIZE { iv = 31; }
GIVEN {
ASSUME(gMovesInfo[MOVE_HIDDEN_POWER].type == TYPE_NORMAL);
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMALIUM_Z); AttackIV(iv); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_HIDDEN_POWER, gimmick: GIMMICK_Z_MOVE); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BREAKNECK_BLITZ, player);
}
}
SINGLE_BATTLE_TEST("(Z-MOVE) Z-Weather Ball transforms into different Z-Moves based on current weather")
{
u32 weatherMove = MOVE_NONE;
u32 zMove = MOVE_NONE;
PARAMETRIZE { weatherMove = MOVE_RAIN_DANCE; zMove = gTypesInfo[TYPE_WATER].zMove; }
PARAMETRIZE { weatherMove = MOVE_SUNNY_DAY; zMove = gTypesInfo[TYPE_FIRE].zMove; }
PARAMETRIZE { weatherMove = MOVE_SANDSTORM; zMove = gTypesInfo[TYPE_ROCK].zMove; }
PARAMETRIZE { weatherMove = MOVE_HAIL; zMove = gTypesInfo[TYPE_ICE].zMove; }
GIVEN {
ASSUME(gMovesInfo[MOVE_WEATHER_BALL].type == TYPE_NORMAL);
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMALIUM_Z); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, weatherMove); }
TURN { MOVE(player, MOVE_WEATHER_BALL, gimmick: GIMMICK_Z_MOVE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, weatherMove, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_MOVE, zMove, player);
}
}
SINGLE_BATTLE_TEST("(Z-MOVE) Z-Sleep Talk transforms a used non-status move into a Z-Move")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_SLEEP_TALK].type == TYPE_NORMAL);
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMALIUM_Z); Status1(STATUS1_SLEEP); Moves(MOVE_SLEEP_TALK, MOVE_ABSORB); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SLEEP_TALK, gimmick: GIMMICK_Z_MOVE); }
TURN { MOVE(player, MOVE_SLEEP_TALK); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BLOOM_DOOM, player);
}
}
SINGLE_BATTLE_TEST("(Z-MOVE) Z-Sleep Talk turns Weather Ball into Breakneck Blitz even under rain")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_SLEEP_TALK].type == TYPE_NORMAL);
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMALIUM_Z); Status1(STATUS1_SLEEP); Moves(MOVE_SLEEP_TALK, MOVE_WEATHER_BALL); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_RAIN_DANCE); MOVE(player, MOVE_SLEEP_TALK, gimmick: GIMMICK_Z_MOVE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_RAIN_DANCE, opponent);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BREAKNECK_BLITZ, player);
}
}
// Miscellaneous Interactions
DOUBLE_BATTLE_TEST("(Z-MOVE) Instruct fails if the target last used a Z-Move")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_TACKLE].type == TYPE_NORMAL);
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMALIUM_Z); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(playerLeft, MOVE_TACKLE, target: opponentLeft, gimmick: GIMMICK_Z_MOVE);
MOVE(playerRight, MOVE_INSTRUCT, target: playerLeft); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, playerLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BREAKNECK_BLITZ, playerLeft);
MESSAGE("Wynaut used Instruct!");
MESSAGE("But it failed!");
}
}
DOUBLE_BATTLE_TEST("(Z-MOVE) Dancer does not use a Z-Move if the battler has used a Z-Move the same turn")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_TACKLE].type == TYPE_NORMAL);
PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_DANCER); Item(ITEM_NORMALIUM_Z); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(playerLeft, MOVE_TACKLE, target: opponentLeft, gimmick: GIMMICK_Z_MOVE);
MOVE(playerRight, MOVE_FIERY_DANCE, target: opponentRight); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, playerLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BREAKNECK_BLITZ, playerLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_FIERY_DANCE, playerRight);
ABILITY_POPUP(playerLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_FIERY_DANCE, playerLeft);
}
}
// Signature Z-Moves
SINGLE_BATTLE_TEST("(Z-MOVE) Light That Burns the Sky uses the battler's highest attacking stat", s16 damage)
{
bool32 useSwordsDance;
PARAMETRIZE { useSwordsDance = FALSE; }
PARAMETRIZE { useSwordsDance = TRUE; }
GIVEN {
ASSUME(gMovesInfo[MOVE_SWORDS_DANCE].effect == EFFECT_ATTACK_UP_2);
PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Item(ITEM_ULTRANECROZIUM_Z); }
OPPONENT(SPECIES_WOBBUFFET) { HP(1000); MaxHP(1000); }; // hits hard lol
} WHEN {
TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_ULTRA_BURST); }
if (useSwordsDance)
TURN { MOVE(player, MOVE_SWORDS_DANCE); }
TURN { MOVE(player, MOVE_PHOTON_GEYSER, gimmick: GIMMICK_Z_MOVE); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ULTRA_BURST, player); // implicitly testing double gimmicks :^)
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_LIGHT_THAT_BURNS_THE_SKY, player);
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, UQ_4_12(2.0), results[1].damage);
}
}
SINGLE_BATTLE_TEST("(Z-MOVE) 10,000,000 Volt Thunderbolt has an increased critical hit ratio")
{
PASSES_RANDOMLY(1, 2, RNG_CRITICAL_HIT);
GIVEN {
ASSUME(B_CRIT_CHANCE >= GEN_6);
ASSUME(gMovesInfo[MOVE_10_000_000_VOLT_THUNDERBOLT].criticalHitStage == 2);
PLAYER(SPECIES_PIKACHU_PARTNER_CAP) { Item(ITEM_PIKASHUNIUM_Z); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_THUNDERBOLT, gimmick: GIMMICK_Z_MOVE); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_10_000_000_VOLT_THUNDERBOLT, player);
MESSAGE("A critical hit!");
}
}
SINGLE_BATTLE_TEST("(Z-MOVE) Stoked Sparksurfer paralyzes the target")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_STOKED_SPARKSURFER].additionalEffects[0].moveEffect == MOVE_EFFECT_PARALYSIS);
PLAYER(SPECIES_RAICHU_ALOLA) { Item(ITEM_ALORAICHIUM_Z); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_THUNDERBOLT, gimmick: GIMMICK_Z_MOVE); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_STOKED_SPARKSURFER, player);
STATUS_ICON(opponent, STATUS1_PARALYSIS);
}
}
SINGLE_BATTLE_TEST("(Z-MOVE) Extreme Evoboost boosts all the user's stats by two stages")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_EXTREME_EVOBOOST].effect == EFFECT_EXTREME_EVOBOOST);
PLAYER(SPECIES_EEVEE) { Item(ITEM_EEVIUM_Z); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_LAST_RESORT, gimmick: GIMMICK_Z_MOVE); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_EXTREME_EVOBOOST, player);
} THEN {
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 2);
EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 2);
EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 2);
EXPECT_EQ(player->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE + 2);
EXPECT_EQ(player->statStages[STAT_SPEED], DEFAULT_STAT_STAGE + 2);
}
}
SINGLE_BATTLE_TEST("(Z-MOVE) Genesis Supernova sets up psychic terrain")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_GENESIS_SUPERNOVA].effect == EFFECT_HIT_SET_REMOVE_TERRAIN);
PLAYER(SPECIES_MEW) { Item(ITEM_MEWNIUM_Z); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_PSYCHIC, gimmick: GIMMICK_Z_MOVE); }
TURN { MOVE(player, MOVE_QUICK_ATTACK); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_GENESIS_SUPERNOVA, player);
NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, player); }
MESSAGE("Mew cannot use Quick Attack!");
}
}
SINGLE_BATTLE_TEST("(Z-MOVE) Splintered Stormshards removes terrain")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_SPLINTERED_STORMSHARDS].effect == EFFECT_HIT_SET_REMOVE_TERRAIN);
PLAYER(SPECIES_LYCANROC_DUSK) { Item(ITEM_LYCANIUM_Z); }
OPPONENT(SPECIES_TAPU_LELE) { Ability(ABILITY_PSYCHIC_SURGE); HP(1000); MaxHP(1000); }
} WHEN {
TURN { MOVE(player, MOVE_STONE_EDGE, gimmick: GIMMICK_Z_MOVE); }
TURN { MOVE(player, MOVE_QUICK_ATTACK); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPLINTERED_STORMSHARDS, player);
MESSAGE("The weirdness disappeared from the battlefield.");
ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, player);
HP_BAR(opponent);
}
}
SINGLE_BATTLE_TEST("(Z-MOVE) Clangorous Soulblaze boosts all the user's stats by one stage")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_CLANGOROUS_SOULBLAZE].additionalEffects[0].moveEffect == MOVE_EFFECT_ALL_STATS_UP);
PLAYER(SPECIES_KOMMO_O) { Item(ITEM_KOMMONIUM_Z); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CLANGING_SCALES, gimmick: GIMMICK_Z_MOVE); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CLANGOROUS_SOULBLAZE, player);
} THEN {
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(player->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(player->statStages[STAT_SPEED], DEFAULT_STAT_STAGE + 1);
}
}
SINGLE_BATTLE_TEST("(Z-MOVE) Guardian of Alola deals 75\% of the target's current HP")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_GUARDIAN_OF_ALOLA].effect == EFFECT_GUARDIAN_OF_ALOLA);
PLAYER(SPECIES_TAPU_FINI) { Item(ITEM_TAPUNIUM_Z); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_NATURES_MADNESS, gimmick: GIMMICK_Z_MOVE); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_GUARDIAN_OF_ALOLA, player);
} THEN {
EXPECT_MUL_EQ(opponent->maxHP, UQ_4_12(0.25), opponent->hp);
}
}
SINGLE_BATTLE_TEST("(Z-MOVE) Searing Sunraze Smash ignores the target's abilities")
{
GIVEN {
PLAYER(SPECIES_SOLGALEO) { Item(ITEM_SOLGANIUM_Z); }
OPPONENT(SPECIES_LAPRAS) { Ability(ABILITY_BATTLE_ARMOR); }
} WHEN {
TURN { MOVE(player, MOVE_SUNSTEEL_STRIKE, gimmick: GIMMICK_Z_MOVE, criticalHit: TRUE); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SEARING_SUNRAZE_SMASH, player);
HP_BAR(opponent);
MESSAGE("A critical hit!");
}
}

View File

@ -342,7 +342,7 @@ SINGLE_BATTLE_TEST("Embargo doesn't prevent Mega Evolution")
} WHEN {
TURN { MOVE(player, MOVE_EMBARGO); }
TURN { MOVE(opponent, MOVE_BATON_PASS); SEND_OUT(opponent, 1); }
TURN { MOVE(opponent, MOVE_CELEBRATE, megaEvolve: TRUE); }
TURN { MOVE(opponent, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); }
} SCENE {
// Turn 1
MESSAGE("Wobbuffet used Embargo!");

View File

@ -53,7 +53,7 @@ SINGLE_BATTLE_TEST("Plasma Fists type-changing effect is applied after Pixilate"
PLAYER(SPECIES_KRABBY) { Speed(300); };
OPPONENT(SPECIES_ALTARIA) { Speed(1); Item(ITEM_ALTARIANITE); }
} WHEN {
TURN { MOVE(player, MOVE_PLASMA_FISTS); MOVE(opponent, MOVE_EMBER, megaEvolve: TRUE); }
TURN { MOVE(player, MOVE_PLASMA_FISTS); MOVE(opponent, MOVE_EMBER, gimmick: GIMMICK_MEGA); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponent);
MESSAGE("Krabby used Plasma Fists!");
@ -88,7 +88,7 @@ SINGLE_BATTLE_TEST("Plasma Fists turns normal type dynamax-moves into electric t
PLAYER(SPECIES_KRABBY) { Speed(100); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(1); }
} WHEN {
TURN { MOVE(player, MOVE_PLASMA_FISTS); MOVE(opponent, MOVE_TACKLE, dynamax: TRUE); }
TURN { MOVE(player, MOVE_PLASMA_FISTS); MOVE(opponent, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); }
} SCENE {
MESSAGE("Krabby used Plasma Fists!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLASMA_FISTS, player);

View File

@ -13,7 +13,7 @@ SINGLE_BATTLE_TEST("Tera Blast changes from Normal-type to the user's Tera Type"
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_DARK); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TERA_BLAST, tera: TRUE); }
TURN { MOVE(player, MOVE_TERA_BLAST, gimmick: GIMMICK_TERA); }
} SCENE {
MESSAGE("Wobbuffet used Tera Blast!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player);
@ -63,7 +63,7 @@ SINGLE_BATTLE_TEST("Tera Blast has correct effectiveness for every Tera Type")
PLAYER(SPECIES_WOBBUFFET) { TeraType(type); }
OPPONENT(species);
} WHEN {
TURN { MOVE(player, MOVE_TERA_BLAST, tera: TRUE); }
TURN { MOVE(player, MOVE_TERA_BLAST, gimmick: GIMMICK_TERA); }
} SCENE {
if (species == SPECIES_GASTLY && type == TYPE_NORMAL)
MESSAGE("It doesn't affect Foe Gastly…");
@ -75,13 +75,13 @@ SINGLE_BATTLE_TEST("Tera Blast has correct effectiveness for every Tera Type")
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; }
PARAMETRIZE { tera = GIMMICK_NONE; }
PARAMETRIZE { tera = GIMMICK_TERA; }
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); }
TURN { MOVE(player, MOVE_TERA_BLAST, gimmick: tera); }
} SCENE {
MESSAGE("Wobbuffet used Tera Blast!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player);
@ -100,7 +100,7 @@ SINGLE_BATTLE_TEST("Stellar-type Tera Blast lowers both offensive stats")
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TERA_BLAST, tera: TRUE); }
TURN { MOVE(player, MOVE_TERA_BLAST, gimmick: GIMMICK_TERA); }
} SCENE {
MESSAGE("Wobbuffet used Tera Blast!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player);
@ -119,7 +119,7 @@ SINGLE_BATTLE_TEST("Stellar-type Tera Blast has 100 BP and a one-time 1.2x boost
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_TERA_BLAST, gimmick: GIMMICK_TERA); }
TURN { MOVE(player, MOVE_WORK_UP); }
TURN { MOVE(player, MOVE_TERA_BLAST); }
} SCENE {
@ -149,7 +149,7 @@ SINGLE_BATTLE_TEST("Stellar-type Tera Blast is super-effective on Stellar-type P
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); }
TURN { MOVE(player, MOVE_TERA_BLAST, gimmick: GIMMICK_TERA); MOVE(opponent, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); }
} SCENE {
MESSAGE("Wobbuffet used Tera Blast!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player);
@ -163,7 +163,7 @@ SINGLE_BATTLE_TEST("Stellar-type Tera Blast activates a Stellar-type Pokemon's W
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); }
TURN { MOVE(player, MOVE_TERA_BLAST, gimmick: GIMMICK_TERA); MOVE(opponent, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); }
} SCENE {
MESSAGE("Wobbuffet used Tera Blast!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player);
@ -178,7 +178,7 @@ SINGLE_BATTLE_TEST("Flying-type Tera Blast does not have its priority boosted by
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); }
TURN { MOVE(player, MOVE_TERA_BLAST, gimmick: GIMMICK_TERA); MOVE(opponent, MOVE_QUICK_ATTACK); }
} SCENE {
MESSAGE("Foe Wobbuffet used Quick Attack!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, opponent);

View File

@ -43,14 +43,14 @@ DOUBLE_BATTLE_TEST("Tera Starstorm targets both opponents in a double battle if
SINGLE_BATTLE_TEST("Tera Starstorm becomes a physical move if the user is Terapagos-Stellar, is Terastallized, and has a higher Attack stat", s16 damage)
{
bool32 tera;
PARAMETRIZE { tera = FALSE; }
PARAMETRIZE { tera = TRUE; }
PARAMETRIZE { tera = GIMMICK_NONE; }
PARAMETRIZE { tera = GIMMICK_TERA; }
GIVEN {
ASSUME(gMovesInfo[MOVE_TERA_STARSTORM].category == DAMAGE_CATEGORY_SPECIAL);
PLAYER(SPECIES_TERAPAGOS_STELLAR) { Attack(100); SpAttack(50); }
OPPONENT(SPECIES_WOBBUFFET) { Defense(200); SpDefense(200); }
} WHEN {
TURN { MOVE(player, MOVE_TERA_STARSTORM, tera: tera); }
TURN { MOVE(player, MOVE_TERA_STARSTORM, gimmick: tera); }
} SCENE {
MESSAGE("Terapagos used Tera Starstorm!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_STARSTORM, player);

View File

@ -52,7 +52,7 @@
.isShiny = TRUE,
#line 18
.dynamaxLevel = 5,
.shouldDynamax = TRUE,
.useGimmick = GIMMICK_DYNAMAX,
.moves = {
#line 19
MOVE_AIR_SLASH,

View File

@ -3,6 +3,8 @@
#include "battle_ai_util.h"
#include "battle_anim.h"
#include "battle_controllers.h"
#include "battle_gimmick.h"
#include "battle_z_move.h"
#include "characters.h"
#include "event_data.h"
#include "fieldmap.h"
@ -2101,17 +2103,31 @@ void MoveGetIdAndSlot(s32 battlerId, struct MoveContext *ctx, u32 *moveId, u32 *
INVALID("No move or moveSlot");
}
if (ctx->explicitMegaEvolve && ctx->megaEvolve)
*moveSlot |= RET_MEGA_EVOLUTION;
if (ctx->explicitGimmick && ctx->gimmick != GIMMICK_NONE)
{
u32 item = GetMonData(mon, MON_DATA_HELD_ITEM);
u32 holdEffect = ItemId_GetHoldEffect(item);
u32 species = GetMonData(mon, MON_DATA_SPECIES);
u32 side = GetBattlerSide(battlerId);
if (ctx->explicitUltraBurst && ctx->ultraBurst)
*moveSlot |= RET_ULTRA_BURST;
// Check invalid item usage.
INVALID_IF(ctx->gimmick == GIMMICK_MEGA && holdEffect != HOLD_EFFECT_MEGA_STONE && species != SPECIES_RAYQUAZA, "Cannot Mega Evolve without a Mega Stone");
INVALID_IF(ctx->gimmick == GIMMICK_Z_MOVE && holdEffect != HOLD_EFFECT_Z_CRYSTAL, "Cannot use a Z-Move without a Z-Crystal");
INVALID_IF(ctx->gimmick == GIMMICK_Z_MOVE && ItemId_GetSecondaryId(item) != gMovesInfo[*moveId].type
&& GetSignatureZMove(*moveId, species, item) == MOVE_NONE
&& *moveId != MOVE_PHOTON_GEYSER, // exception because test won't recognize Ultra Necrozma pre-Burst
"Cannot turn %S into a Z-Move with %S", GetMoveName(ctx->move), ItemId_GetName(item));
INVALID_IF(ctx->gimmick != GIMMICK_MEGA && holdEffect == HOLD_EFFECT_MEGA_STONE, "Cannot use another gimmick while holding a Mega Stone");
INVALID_IF(ctx->gimmick != GIMMICK_Z_MOVE && ctx->gimmick != GIMMICK_ULTRA_BURST && holdEffect == HOLD_EFFECT_Z_CRYSTAL, "Cannot use another gimmick while holding a Z-Crystal");
if (ctx->explicitDynamax && ctx->dynamax)
*moveSlot |= RET_DYNAMAX;
if (ctx->explicitTera && ctx->tera)
*moveSlot |= RET_TERASTAL;
// Check multiple gimmick use.
INVALID_IF(DATA.chosenGimmick[side][DATA.currentMonIndexes[battlerId]] != GIMMICK_NONE
&& !(DATA.chosenGimmick[side][DATA.currentMonIndexes[battlerId]] == GIMMICK_ULTRA_BURST
&& ctx->gimmick == GIMMICK_Z_MOVE), "Cannot use multiple gimmicks on the same battler");
DATA.chosenGimmick[side][DATA.currentMonIndexes[battlerId]] = ctx->gimmick;
*moveSlot |= RET_GIMMICK;
}
}
void Move(u32 sourceLine, struct BattlePokemon *battler, struct MoveContext ctx)
@ -2675,6 +2691,11 @@ u32 TestRunner_Battle_GetForcedAbility(u32 side, u32 partyIndex)
return DATA.forcedAbilities[side][partyIndex];
}
u32 TestRunner_Battle_GetChosenGimmick(u32 side, u32 partyIndex)
{
return DATA.chosenGimmick[side][partyIndex];
}
// TODO: Consider storing the last successful i and searching from i+1
// to improve performance.
struct AILogLine *GetLogLine(u32 battlerId, u32 moveIndex)

View File

@ -1824,16 +1824,15 @@ static void fprint_trainers(const char *output_path, FILE *f, struct Parsed *par
if (pokemon->dynamax_level_line || pokemon->gigantamax_factor_line)
{
fprintf(f, " .shouldDynamax = TRUE,\n");
fprintf(f, " .useGimmick = GIMMICK_DYNAMAX,\n");
}
if (pokemon->tera_type_line)
else if (pokemon->tera_type_line)
{
fprintf(f, "#line %d\n", pokemon->tera_type_line);
fprintf(f, " .teraType = ");
fprint_constant(f, "TYPE", pokemon->tera_type);
fprintf(f, ",\n");
fprintf(f, " .shouldTerastal = TRUE,\n");
fprintf(f, " .useGimmick = GIMMICK_TERA,\n");
}
if (pokemon->moves_n > 0)