From 0fba9e93d3c96222405e24a231c1d3838adf636a Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Thu, 25 Apr 2024 23:47:22 -0700 Subject: [PATCH 01/33] consolidated gimmick checks, triggers, communication, and activation; updated test runner --- data/battle_scripts_1.s | 18 +- include/battle.h | 44 +-- include/battle_controllers.h | 7 +- include/battle_dynamax.h | 23 +- include/battle_gimmick.h | 41 +++ include/battle_interface.h | 13 +- include/battle_terastal.h | 9 +- include/battle_util.h | 2 + include/data.h | 3 +- include/test/battle.h | 1 + include/test_runner.h | 3 + src/battle_ai_main.c | 30 +- src/battle_ai_util.c | 22 +- src/battle_controller_opponent.c | 15 +- src/battle_controller_player.c | 160 ++++------- src/battle_controller_player_partner.c | 8 +- src/battle_dynamax.c | 333 +++++----------------- src/battle_gfx_sfx_util.c | 4 +- src/battle_gimmick.c | 252 +++++++++++++++++ src/battle_interface.c | 376 +------------------------ src/battle_main.c | 99 ++----- src/battle_script_commands.c | 44 ++- src/battle_terastal.c | 223 ++------------- src/battle_util.c | 110 ++++---- src/data/gimmicks.h | 146 ++++++++++ test/battle/trainer_control.h | 1 + test/test_runner_battle.c | 25 +- tools/trainerproc/main.c | 4 +- 28 files changed, 770 insertions(+), 1246 deletions(-) create mode 100644 include/battle_gimmick.h create mode 100644 src/battle_gimmick.c create mode 100644 src/data/gimmicks.h diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 9da79c38d877..ca13f3dd5f03 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -24,7 +24,7 @@ BattleScript_Terastallization:: @ TODO: no string prints in S/V, but right now this helps with clarity printstring STRINGID_PKMNTERASTALLIZEDINTO @ TODO: replace this animation - playanimation BS_ATTACKER, B_ANIM_TOTEM_FLARE + playanimation BS_SCRIPTING, B_ANIM_TOTEM_FLARE waitanimation end3 @@ -7001,13 +7001,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:: @@ -7044,13 +7044,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:: diff --git a/include/battle.h b/include/battle.h index c5d08bb58481..1e8403e8009d 100644 --- a/include/battle.h +++ b/include/battle.h @@ -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. @@ -364,8 +365,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 @@ -552,24 +551,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; @@ -600,11 +581,6 @@ struct ZMoveData 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; @@ -616,15 +592,20 @@ struct DynamaxData struct TeraData { - bool8 toTera; // flags using gBitTable - bool8 isTerastallized[NUM_BATTLE_SIDES]; // stored as a bitfield for each side's party members - bool8 alreadyTerastallized[MAX_BATTLERS_COUNT]; - bool8 playerSelect; u32 stellarBoostFlags[NUM_BATTLE_SIDES]; // stored as a bitfield of flags for all types for each side - u8 triggerSpriteId; u8 indicatorSpriteId[MAX_BATTLERS_COUNT]; }; +struct BattleGimmickData +{ + 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 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 { u16 originalItem:15; @@ -743,11 +724,10 @@ 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; diff --git a/include/battle_controllers.h b/include/battle_controllers.h index 523434d57ac6..9302a68874b3 100644 --- a/include/battle_controllers.h +++ b/include/battle_controllers.h @@ -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; }; diff --git a/include/battle_dynamax.h b/include/battle_dynamax.h index 1e3f3265b741..ad47fe5a8878 100644 --- a/include/battle_dynamax.h +++ b/include/battle_dynamax.h @@ -56,19 +56,18 @@ 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); +void ActivateDynamax(u32 battler); +u16 GetNonDynamaxHP(u32 battler); +u16 GetNonDynamaxMaxHP(u32 battler); +void UndoDynamax(u32 battler); bool32 IsMoveBlockedByMaxGuard(u16 move); bool32 IsMoveBlockedByDynamax(u16 move); -bool32 ShouldUseMaxMove(u16 battlerId, u16 baseMove); -u16 GetMaxMove(u16 battlerId, u16 baseMove); +bool32 ShouldUseMaxMove(u16 battler, u16 baseMove); +u16 GetMaxMove(u16 battler, u16 baseMove); u8 GetMaxMovePower(u16 move); bool32 IsMaxMove(u16 move); void ChooseDamageNonTypesString(u8 type); @@ -83,10 +82,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 diff --git a/include/battle_gimmick.h b/include/battle_gimmick.h new file mode 100644 index 000000000000..078b8c1a9524 --- /dev/null +++ b/include/battle_gimmick.h @@ -0,0 +1,41 @@ +#ifndef GUARD_BATTLE_GIMMICK_H +#define GUARD_BATTLE_GIMMICK_H + +enum Gimmick +{ + GIMMICK_NONE, + GIMMICK_MEGA, + GIMMICK_Z_MOVE, + GIMMICK_ULTRA_BURST, + GIMMICK_DYNAMAX, + GIMMICK_TERA, + GIMMICKS_COUNT, +}; + +struct GimmickInfo +{ + const struct SpritePalette * triggerPal; // trigger gfx data + const struct SpriteSheet * triggerSheet; + const struct SpriteTemplate * triggerTemplate; + 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); + +extern const struct GimmickInfo gGimmicksInfo[]; + +#endif diff --git a/include/battle_interface.h b/include/battle_interface.h index f32017745f2c..dcd89fab85ce 100644 --- a/include/battle_interface.h +++ b/include/battle_interface.h @@ -48,6 +48,7 @@ enum #define TAG_HEALTHBAR_PAL TAG_HEALTHBAR_PLAYER1_TILE #define TAG_HEALTHBOX_PAL TAG_HEALTHBOX_PLAYER1_TILE +#define TAG_GIMMICK_TRIGGER_TILE 0xD777 #define TAG_MEGA_TRIGGER_TILE 0xD777 #define TAG_MEGA_INDICATOR_TILE 0xD778 #define TAG_ALPHA_INDICATOR_TILE 0xD779 @@ -79,6 +80,7 @@ enum #define TAG_STELLAR_INDICATOR_TILE 0xD792 #define TAG_TERA_TRIGGER_TILE 0xD793 +#define TAG_GIMMICK_TRIGGER_PAL 0xD777 #define TAG_MEGA_TRIGGER_PAL 0xD777 #define TAG_MEGA_INDICATOR_PAL 0xD778 #define TAG_MISC_INDICATOR_PAL 0xD779 // Alpha, Omega, and Dynamax indicators use the same palette as each of them only uses 4 different colors. @@ -116,16 +118,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); @@ -136,7 +128,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); diff --git a/include/battle_terastal.h b/include/battle_terastal.h index 078bf39079fa..532a4028f3dd 100644 --- a/include/battle_terastal.h +++ b/include/battle_terastal.h @@ -1,22 +1,15 @@ #ifndef GUARD_BATTLE_TERASTAL_H #define GUARD_BATTLE_TERASTAL_H -void PrepareBattlerForTera(u32 battler); +void ActivateTera(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); diff --git a/include/battle_util.h b/include/battle_util.h index 27ecb26e82cb..369a07844897 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -185,6 +185,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); diff --git a/include/data.h b/include/data.h index 0549f29c4c1a..673f4f2e8f8c 100644 --- a/include/data.h +++ b/include/data.h @@ -73,8 +73,7 @@ struct TrainerMon u8 dynamaxLevel:4; u8 teraType:5; bool8 gigantamaxFactor:1; - bool8 shouldDynamax:1; - bool8 shouldTerastal:1; + u8 useGimmick:3; }; #define TRAINER_PARTY(partyArray) partyArray, .partySize = ARRAY_COUNT(partyArray) diff --git a/include/test/battle.h b/include/test/battle.h index c4bd89f91b11..8e3a26cd9093 100644 --- a/include/test/battle.h +++ b/include/test/battle.h @@ -662,6 +662,7 @@ struct BattleTestData u8 gender; u8 nature; u16 forcedAbilities[NUM_BATTLE_SIDES][PARTY_SIZE]; + u8 chosenGimmick[MAX_BATTLERS_COUNT]; u8 currentMonIndexes[MAX_BATTLERS_COUNT]; u8 turnState; diff --git a/include/test_runner.h b/include/test_runner.h index 248a0463e549..b22a7fece60f 100644 --- a/include/test_runner.h +++ b/include/test_runner.h @@ -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 battler); #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 diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index da8b3764a336..1e1c3970b137 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -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; @@ -485,7 +468,6 @@ void SetAiLogicDataForTurn(struct AiLogicData *aiData) continue; SetBattlerAiData(battlerAtk, aiData); - SetBattlerAiGimmickData(battlerAtk, aiData); SetBattlerAiMovesData(aiData, battlerAtk, battlersCount); } } @@ -2395,11 +2377,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; } @@ -2957,8 +2939,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); } @@ -3177,7 +3159,7 @@ static u32 AI_CalcMoveScore(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 @@ -3939,7 +3921,7 @@ static u32 AI_CalcMoveScore(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 diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 3458830a1283..360cc20b2039 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -431,7 +431,7 @@ bool32 IsDamageMoveUsable(u32 move, u32 battlerAtk, u32 battlerDef) return TRUE; break; case EFFECT_LOW_KICK: - if (IsDynamaxed(battlerDef)) + if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX) return TRUE; break; case EFFECT_FAIL_IF_NOT_ARG_TYPE: @@ -448,8 +448,7 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes s32 dmg, moveType; uq4_12_t effectivenessMultiplier; bool32 isDamageMoveUnusable = FALSE; - bool32 toggledDynamax = FALSE; - bool32 toggledTera = FALSE; + bool32 toggledGimmick = FALSE; struct AiLogicData *aiData = AI_DATA; SetBattlerData(battlerAtk); @@ -468,15 +467,10 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes move = GetNaturePowerMove(); // Temporarily enable other gimmicks for damage calcs if planned - if (AI_DATA->shouldDynamax[battlerAtk]) + if (gBattleStruct->gimmick.usableGimmick[battlerAtk] && GetActiveGimmick(battlerAtk) == GIMMICK_NONE) { - toggledDynamax = TRUE; - gBattleStruct->dynamax.dynamaxed[battlerAtk] = TRUE; - } - if (AI_DATA->shouldTerastal[battlerAtk]) - { - toggledTera = TRUE; - gBattleStruct->tera.isTerastallized[GetBattlerSide(battlerAtk)] |= gBitTable[gBattlerPartyIndexes[battlerAtk]]; + toggledGimmick = TRUE; + SetActiveGimmick(battlerAtk, gBattleStruct->gimmick.usableGimmick[battlerAtk]); } gBattleStruct->dynamicMoveType = 0; @@ -596,10 +590,8 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes gBattleStruct->swapDamageCategory = FALSE; gBattleStruct->zmove.active = FALSE; gBattleStruct->zmove.baseMoves[battlerAtk] = MOVE_NONE; - if (toggledDynamax) - gBattleStruct->dynamax.dynamaxed[battlerAtk] = FALSE; - if (toggledTera) - gBattleStruct->tera.isTerastallized[GetBattlerSide(battlerAtk)] &= ~(gBitTable[gBattlerPartyIndexes[battlerAtk]]); + if (toggledGimmick) + SetActiveGimmick(battlerAtk, GIMMICK_NONE); return dmg; } diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index 1622b94e9d8e..10da422bd7ca 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -554,18 +554,9 @@ static void OpponentHandleChooseMove(u32 battler) } 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 != GIMMICK_NONE) + BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_GIMMICK) | (gBattlerTarget << 8)); else BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (gBattlerTarget << 8)); } diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index 10a91a14853a..8315b370bc59 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -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); } @@ -716,7 +699,7 @@ static void HandleInputChooseMove(u32 battler) } // 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) @@ -772,17 +755,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; @@ -811,17 +788,14 @@ static void HandleInputChooseMove(u32 battler) PlaySE(SE_SELECT); if (gBattleStruct->zmove.viewing) { + gBattleStruct->gimmick.playerSelect = FALSE; ReloadMoveNames(battler); } else { - gBattleStruct->mega.playerSelect = FALSE; - gBattleStruct->burst.playerSelect = FALSE; - gBattleStruct->dynamax.playerSelect = FALSE; - gBattleStruct->tera.playerSelect = FALSE; - gBattleStruct->zmove.viable = FALSE; + gBattleStruct->gimmick.playerSelect = FALSE; BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, 0xFFFF); - HideTriggerSprites(); + HideGimmickTriggerSprite(); PlayerBufferExecCompleted(battler); } } @@ -897,52 +871,28 @@ 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); + gBattleStruct->gimmick.playerSelect ^= 1; + ReloadMoveNames(battler); + ChangeGimmickTriggerSprite(gBattleStruct->gimmick.triggerSpriteId, gBattleStruct->gimmick.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); + // } } } 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); @@ -1471,7 +1421,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; @@ -1555,7 +1505,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; @@ -1708,8 +1658,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])); @@ -1768,6 +1717,12 @@ static void MoveSelectionDisplayMoveType(u32 battler) else type = gMovesInfo[MOVE_IVY_CUDGEL].type; } + // 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 type = gMovesInfo[moveInfo->moves[gMoveSelectionCursor[battler]]].type; @@ -2069,35 +2024,18 @@ static void PlayerHandleChooseMove(u32 battler) } else { - struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]); + // 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; - GetUsableZMoves(battler, moveInfo->moves); - gBattleStruct->zmove.viable = IsZMoveUsable(battler, gMoveSelectionCursor[battler]); - CreateZMoveTriggerSprite(battler, gBattleStruct->zmove.viable); + if (!IsGimmickTriggerSpriteActive()) + gBattleStruct->gimmick.triggerSpriteId = 0xFF; + CreateGimmickTriggerSprite(battler); + + // GetUsableZMoves(battler, moveInfo->moves); + // gBattleStruct->zmove.viable = IsZMoveUsable(battler, gMoveSelectionCursor[battler]); + // CreateZMoveTriggerSprite(battler, gBattleStruct->zmove.viable); gBattlerControllerFuncs[battler] = HandleChooseMoveAfterDma3; } } diff --git a/src/battle_controller_player_partner.c b/src/battle_controller_player_partner.c index 5c3fd4176240..57e6f7cfec53 100644 --- a/src/battle_controller_player_partner.c +++ b/src/battle_controller_player_partner.c @@ -372,11 +372,9 @@ static void PlayerPartnerHandleChooseMove(u32 battler) 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 opponent can and should use a gimmick (considering trainer data), do it + if (gBattleStruct->gimmick.usableGimmick != GIMMICK_NONE) + BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_GIMMICK) | (gBattlerTarget << 8)); else BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (gBattlerTarget << 8)); } diff --git a/src/battle_dynamax.c b/src/battle_dynamax.c index 5075e18ea648..b3c525c2bc5d 100644 --- a/src/battle_dynamax.c +++ b/src/battle_dynamax.c @@ -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" @@ -69,33 +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); + u16 species = gBattleMons[battler].species; + u16 holdEffect = ItemId_GetHoldEffect(gBattleMons[battler].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; + // #if B_FLAG_DYNAMAX_BATTLE != 0 + // if (!FlagGet(B_FLAG_DYNAMAX_BATTLE)) + // #endif + // return 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)) + if ((GetBattlerPosition(battler) == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && GetBattlerPosition(battler) == B_POSITION_PLAYER_RIGHT)) && !CheckBagHasItem(ITEM_DYNAMAX_BAND, 1)) return FALSE; @@ -105,18 +94,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; + + // 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; - // 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 battler is holding a Z-Crystal or Mega Stone. + if (holdEffect == HOLD_EFFECT_Z_CRYSTAL || holdEffect == HOLD_EFFECT_MEGA_STONE) 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 +119,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,74 +143,75 @@ 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); } // 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. @@ -251,23 +247,23 @@ bool32 IsMoveBlockedByDynamax(u16 move) } // Returns whether a move should be converted into a Max Move. -bool32 ShouldUseMaxMove(u16 battlerId, u16 baseMove) +bool32 ShouldUseMaxMove(u16 battler, 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]; + // if (IsRaidBoss(battler)) + // return !IsRaidBossUsingRegularMove(battler, baseMove); + return GetActiveGimmick(battler) == GIMMICK_DYNAMAX || IsGimmickSelected(battler, GIMMICK_DYNAMAX); } -static u16 GetTypeBasedMaxMove(u16 battlerId, u16 type) +static u16 GetTypeBasedMaxMove(u16 battler, u16 type) { // Gigantamax check u32 i; - u16 species = gBattleMons[battlerId].species; + u16 species = gBattleMons[battler].species; u16 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; @@ -288,7 +284,7 @@ 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(u16 battler, u16 baseMove) { u16 move = baseMove; if (baseMove == MOVE_NONE) // for move display @@ -305,13 +301,13 @@ 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); + gBattleStruct->dynamax.categories[battler] = gMovesInfo[baseMove].category; } else { - move = GetTypeBasedMaxMove(battlerId, gMovesInfo[baseMove].type); - gBattleStruct->dynamax.categories[battlerId] = gMovesInfo[baseMove].category; + move = GetTypeBasedMaxMove(battler, gMovesInfo[baseMove].type); + gBattleStruct->dynamax.categories[battler] = gMovesInfo[baseMove].category; } return move; @@ -1048,201 +1044,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] diff --git a/src/battle_gfx_sfx_util.c b/src/battle_gfx_sfx_util.c index 0a4eaa07dd66..11d0a66b79ad 100644 --- a/src/battle_gfx_sfx_util.c +++ b/src/battle_gfx_sfx_util.c @@ -636,7 +636,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) @@ -647,7 +647,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)); diff --git a/src/battle_gimmick.c b/src/battle_gimmick.c new file mode 100644 index 000000000000..fcce6c10c0a7 --- /dev/null +++ b/src/battle_gimmick.c @@ -0,0 +1,252 @@ +#include "global.h" +#include "battle.h" +#include "battle_anim.h" +#include "battle_controllers.h" +#include "battle_interface.h" +#include "battle_gimmick.h" +#include "battle_setup.h" +#include "item.h" +#include "palette.h" +#include "pokemon.h" +#include "sprite.h" +#include "util.h" +#include "test_runner.h" + +static void SpriteCb_GimmickTrigger(struct Sprite *sprite); + +#include "data/gimmicks.h" + +// Populates gBattleStruct->gimmick.usableGimmick for each battler. +void AssignUsableGimmicks(void) +{ + u32 battler, gimmick; + #ifdef TESTING + for (battler = 0; battler < gBattlersCount; ++battler) + { + gimmick = TestRunner_Battle_GetChosenGimmick(battler); + if (GetActiveGimmick(battler) == GIMMICK_NONE) + gBattleStruct->gimmick.usableGimmick[battler] = gimmick; + else + gBattleStruct->gimmick.usableGimmick[battler] = GIMMICK_NONE; + } + #else + 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; + } + } + } + #endif +} + +// 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) +{ + #ifdef TESTING + return (GetActiveGimmick(battler) == GIMMICK_NONE + && !HasTrainerUsedGimmick(battler, gimmick) + && (gBattleStruct->gimmick.toActivate & gBitTable[battler]) + && gBattleStruct->gimmick.usableGimmick[battler] == gimmick); + #else + return gBattleStruct->gimmick.usableGimmick[battler] == gimmick && gBattleStruct->gimmick.playerSelect; + #endif +} + +// 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) +{ + if (GetBattlerSide(battler) == B_SIDE_PLAYER) + { + return TRUE; // the player can do whatever they want + } + 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; + } +} diff --git a/src/battle_interface.c b/src/battle_interface.c index 6637fa4c4e4c..8546239e7994 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -194,8 +194,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); @@ -619,122 +617,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. @@ -1050,14 +932,14 @@ static void UpdateLvlInHealthbox(u8 healthboxSpriteId, u8 lvl) 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)) + if (IsBattlerMegaEvolved(battler) || IsBattlerPrimalReverted(battler) || GetActiveGimmick(battler) == GIMMICK_DYNAMAX) { 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)) + else if (GetActiveGimmick(battler) == GIMMICK_TERA) { objVram = ConvertIntToDecimalStringN(text, lvl, STR_CONV_MODE_LEFT_ALIGN, 3); xPos = 5 * (3 - (objVram - (text + 2))) - 1; @@ -1372,258 +1254,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 { @@ -1709,7 +1339,7 @@ static bool32 MegaIndicator_ShouldBeInvisible(u32 battlerId, struct Sprite *spri { bool32 megaEvolved = IsBattlerMegaEvolved(battlerId); bool32 primalReverted = IsBattlerPrimalReverted(battlerId); - bool32 dynamaxed = IsDynamaxed(battlerId); + bool32 dynamaxed = GetActiveGimmick(battlerId) == GIMMICK_DYNAMAX; if (!megaEvolved && !primalReverted && !dynamaxed) return TRUE; diff --git a/src/battle_main.c b/src/battle_main.c index eba4916ef468..e7bd610c4ca9 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3367,9 +3367,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; @@ -3515,9 +3512,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. @@ -4147,6 +4141,7 @@ static void TryDoEventsBeforeFirstTurn(void) SpecialStatusesClear(); *(&gBattleStruct->absentBattlerFlags) = gAbsentBattlerFlags; BattlePutTextOnWindow(gText_EmptyString3, B_WIN_MSG); + AssignUsableGimmicks(); gBattleMainFunc = HandleTurnActionSelectionState; ResetSentPokesToOpponentValue(); @@ -4264,6 +4259,7 @@ void BattleTurnPassed(void) *(&gBattleStruct->absentBattlerFlags) = gAbsentBattlerFlags; BattlePutTextOnWindow(gText_EmptyString3, B_WIN_MSG); + AssignUsableGimmicks(); SetAiLogicDataForTurn(AI_DATA); // get assumed abilities, hold effects, etc of all battlers gBattleMainFunc = HandleTurnActionSelectionState; @@ -4487,7 +4483,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; @@ -4612,10 +4607,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->gimmick.toActivate &= ~(gBitTable[BATTLE_PARTNER(GetBattlerPosition(battler))]); gBattleStruct->dynamax.usingMaxMove[BATTLE_PARTNER(GetBattlerPosition(battler))] = FALSE; gBattleStruct->zmove.toBeUsed[BATTLE_PARTNER(GetBattlerPosition(battler))] = MOVE_NONE; BtlController_EmitEndBounceEffect(battler, BUFFER_A); @@ -4705,19 +4697,13 @@ 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])) @@ -4999,7 +4985,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; @@ -5334,9 +5320,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]; @@ -5345,49 +5329,16 @@ static bool32 TryDoGimmicksBeforeMoves(void) SortBattlersBySpeed(order, FALSE); for (i = 0; i < gBattlersCount; i++) { - // Tera Check - if (gBattleStruct->tera.toTera & gBitTable[order[i]]) - { - gBattlerAttacker = order[i]; - gBattleScripting.battler = gBattlerAttacker; - gBattleStruct->tera.toTera &= ~(gBitTable[gBattlerAttacker]); - PrepareBattlerForTera(gBattlerAttacker); - PREPARE_TYPE_BUFFER(gBattleTextBuff1, GetBattlerTeraType(gBattlerAttacker)); - BattleScriptExecute(BattleScript_Terastallization); - return TRUE; - } - // Dynamax Check - if (gBattleStruct->dynamax.toDynamax & gBitTable[order[i]]) + // 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->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; + } } } } @@ -5774,10 +5725,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++) @@ -6018,7 +5969,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; @@ -6062,7 +6013,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; } @@ -6074,7 +6025,7 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk) && gMovesInfo[move].effect != EFFECT_WEATHER_BALL && gMovesInfo[move].effect != EFFECT_CHANGE_TYPE_ON_ITEM && gMovesInfo[move].effect != EFFECT_NATURAL_GIFT - && !(gMovesInfo[move].effect == EFFECT_TERA_BLAST && IsTerastallized(battlerAtk)) + && !(gMovesInfo[move].effect == EFFECT_TERA_BLAST && GetActiveGimmick(battlerAtk) == GIMMICK_TERA) && ((attackerAbility == ABILITY_PIXILATE && (ateType = TYPE_FAIRY)) || (attackerAbility == ABILITY_REFRIGERATE && (ateType = TYPE_ICE)) || (attackerAbility == ABILITY_AERILATE && (ateType = TYPE_FLYING)) @@ -6083,7 +6034,7 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk) ) { gBattleStruct->dynamicMoveType = ateType | F_DYNAMIC_TYPE_SET; - if (!IsDynamaxed(battlerAtk)) + if (GetActiveGimmick(battlerAtk) != GIMMICK_DYNAMAX) gBattleStruct->ateBoost[battlerAtk] = 1; } else if (gMovesInfo[move].type != TYPE_NORMAL @@ -6092,7 +6043,7 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk) && attackerAbility == ABILITY_NORMALIZE) { 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) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 21dcf57f6b5c..244fa5efb5b4 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1216,7 +1216,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; @@ -1238,7 +1238,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; @@ -3161,7 +3161,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++; @@ -3808,7 +3808,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()) { @@ -3985,7 +3985,7 @@ static void Cmd_tryfaintmon(void) gSideTimers[B_SIDE_OPPONENT].retaliateTimer = 2; } if ((gHitMarker & HITMARKER_DESTINYBOND) && gBattleMons[gBattlerAttacker].hp != 0 - && !IsDynamaxed(gBattlerAttacker)) + && !(GetActiveGimmick(gBattlerAttacker) == GIMMICK_DYNAMAX)) { gHitMarker &= ~HITMARKER_DESTINYBOND; BattleScriptPush(gBattlescriptCurrInstr); @@ -8667,7 +8667,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. @@ -8695,9 +8694,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); } } @@ -9571,7 +9570,7 @@ static void Cmd_various(void) else { if (gBattleMons[gBattlerTarget].ability == gBattleMons[gBattlerAttacker].ability - || IsDynamaxed(gBattlerTarget)) + || (GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX)) { gBattlescriptCurrInstr = cmd->failInstr; } @@ -9660,7 +9659,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; } @@ -9757,7 +9756,7 @@ 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)) { gBattlescriptCurrInstr = cmd->failInstr; } @@ -9993,7 +9992,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; } @@ -12080,7 +12079,7 @@ static void Cmd_tryconversiontypechange(void) u8 moveChecked = 0; u8 moveType = 0; - if (IsTerastallized(gBattlerAttacker)) + if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA) { gBattlescriptCurrInstr = cmd->failInstr; return; @@ -12224,7 +12223,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; @@ -12771,7 +12770,7 @@ 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++) { @@ -12850,7 +12849,7 @@ static void Cmd_settypetorandomresistance(void) { gBattlescriptCurrInstr = cmd->failInstr; } - else if (IsTerastallized(gBattlerAttacker)) + else if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA) { gBattlescriptCurrInstr = cmd->failInstr; } @@ -13952,7 +13951,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; } @@ -14341,7 +14340,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; } @@ -14819,7 +14818,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); @@ -16376,7 +16375,7 @@ void BS_TryReflectType(void) { gBattlescriptCurrInstr = cmd->failInstr; } - else if (IsTerastallized(gBattlerAttacker)) + else if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA) { gBattlescriptCurrInstr = cmd->failInstr; } @@ -16606,9 +16605,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); } @@ -16740,7 +16738,7 @@ void BS_AllySwitchFailChance(void) void BS_SetPhotonGeyserCategory(void) { NATIVE_ARGS(); - if (!(gMovesInfo[gCurrentMove].effect == EFFECT_TERA_BLAST && !IsTerastallized(gBattlerAttacker))) + if (!(gMovesInfo[gCurrentMove].effect == EFFECT_TERA_BLAST && GetActiveGimmick(gBattlerAttacker) != GIMMICK_TERA)) gBattleStruct->swapDamageCategory = (GetCategoryBasedOnStats(gBattlerAttacker) == DAMAGE_CATEGORY_PHYSICAL); gBattlescriptCurrInstr = cmd->nextInstr; } diff --git a/src/battle_terastal.c b/src/battle_terastal.c index 8cc03aca5716..d1b8ea47d023 100644 --- a/src/battle_terastal.c +++ b/src/battle_terastal.c @@ -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" @@ -15,15 +17,15 @@ #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); struct Pokemon *party = GetBattlerParty(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 @@ -39,6 +41,10 @@ void PrepareBattlerForTera(u32 battler) UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], &party[index], HEALTHBOX_ALL); BlendPalette(OBJ_PLTT_ID(battler), 16, 8, GetTeraTypeRGB(GetBattlerTeraType(battler))); CpuCopy32(gPlttBufferFaded + OBJ_PLTT_ID(battler), gPlttBufferUnfaded + OBJ_PLTT_ID(battler), PLTT_SIZEOF(16)); + + // Execute battle script. + PREPARE_TYPE_BUFFER(gBattleTextBuff1, GetBattlerTeraType(battler)); + BattleScriptExecute(BattleScript_Terastallization); } // Returns whether a battler can Terastallize. @@ -56,15 +62,19 @@ bool32 CanTerastallize(u32 battler) } // Check if Trainer has already Terastallized. - if (gBattleStruct->tera.alreadyTerastallized[battler]) + if (HasTrainerUsedGimmick(battler, GIMMICK_TERA)) + { + return FALSE; + } + + // Check if AI battler is intended to Terastallize. + if (!ShouldTrainerBattlerUseGimmick(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 battler has another gimmick active. + if (GetActiveGimmick(battler) != GIMMICK_NONE) { return FALSE; } @@ -86,13 +96,6 @@ u32 GetBattlerTeraType(u32 battler) return GetMonData(mon, MON_DATA_TERA_TYPE); } -// Returns whether a battler is terastallized. -bool32 IsTerastallized(u32 battler) -{ - return gBattleStruct->tera.isTerastallized[GetBattlerSide(battler)] & gBitTable[gBattlerPartyIndexes[battler]]; -} - - // Uses up a type's Stellar boost. void ExpendTypeStellarBoost(u32 battler, u32 type) { @@ -117,7 +120,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. @@ -189,190 +192,6 @@ u16 GetTeraTypeRGB(u32 type) return sTeraTypeRGBValues[type]; } -// TERASTAL TRIGGER: -static const u8 ALIGNED(4) sTeraTriggerGfx[] = INCBIN_U8("graphics/battle_interface/tera_trigger.4bpp"); -static const u16 sTeraTriggerPal[] = INCBIN_U16("graphics/battle_interface/tera_trigger.gbapal"); - -static const struct SpriteSheet sSpriteSheet_TeraTrigger = -{ - sTeraTriggerGfx, sizeof(sTeraTriggerGfx), TAG_TERA_TRIGGER_TILE -}; -static const struct SpritePalette sSpritePalette_TeraTrigger = -{ - sTeraTriggerPal, TAG_TERA_TRIGGER_PAL -}; - -static const struct OamData sOamData_TeraTrigger = -{ - .y = 0, - .affineMode = 0, - .objMode = 0, - .mosaic = 0, - .bpp = 0, - .shape = ST_OAM_SQUARE, - .x = 0, - .matrixNum = 0, - .size = 2, - .tileNum = 0, - .priority = 1, - .paletteNum = 0, - .affineParam = 0, -}; - -static const union AnimCmd sSpriteAnim_TeraTriggerOff[] = -{ - ANIMCMD_FRAME(0, 0), - ANIMCMD_END -}; - -static const union AnimCmd sSpriteAnim_TeraTriggerOn[] = -{ - ANIMCMD_FRAME(16, 0), - ANIMCMD_END -}; - -static const union AnimCmd *const sSpriteAnimTable_TeraTrigger[] = -{ - sSpriteAnim_TeraTriggerOff, - sSpriteAnim_TeraTriggerOn, -}; - -static void SpriteCb_TeraTrigger(struct Sprite *sprite); -static const struct SpriteTemplate sSpriteTemplate_TeraTrigger = -{ - .tileTag = TAG_TERA_TRIGGER_TILE, - .paletteTag = TAG_TERA_TRIGGER_PAL, - .oam = &sOamData_TeraTrigger, - .anims = sSpriteAnimTable_TeraTrigger, - .images = NULL, - .affineAnims = gDummySpriteAffineAnimTable, - .callback = SpriteCb_TeraTrigger -}; - -// Tera Evolution Trigger icon functions. -void ChangeTeraTriggerSprite(u8 spriteId, u8 animId) -{ - StartSpriteAnim(&gSprites[spriteId], animId); -} - -#define SINGLES_TERA_TRIGGER_POS_X_OPTIMAL (30) -#define SINGLES_TERA_TRIGGER_POS_X_PRIORITY (31) -#define SINGLES_TERA_TRIGGER_POS_X_SLIDE (15) -#define SINGLES_TERA_TRIGGER_POS_Y_DIFF (-11) - -#define DOUBLES_TERA_TRIGGER_POS_X_OPTIMAL (30) -#define DOUBLES_TERA_TRIGGER_POS_X_PRIORITY (31) -#define DOUBLES_TERA_TRIGGER_POS_X_SLIDE (15) -#define DOUBLES_TERA_TRIGGER_POS_Y_DIFF (-4) - -#define tBattler data[0] -#define tHide data[1] - -void CreateTeraTriggerSprite(u8 battler, u8 palId) -{ - LoadSpritePalette(&sSpritePalette_TeraTrigger); - if (GetSpriteTileStartByTag(TAG_TERA_TRIGGER_TILE) == 0xFFFF) - { - LoadSpriteSheet(&sSpriteSheet_TeraTrigger); - } - if (gBattleStruct->tera.triggerSpriteId == 0xFF) - { - if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) - gBattleStruct->tera.triggerSpriteId = CreateSprite(&sSpriteTemplate_TeraTrigger, - gSprites[gHealthboxSpriteIds[battler]].x - DOUBLES_TERA_TRIGGER_POS_X_SLIDE, - gSprites[gHealthboxSpriteIds[battler]].y - DOUBLES_TERA_TRIGGER_POS_Y_DIFF, 0); - else - gBattleStruct->tera.triggerSpriteId = CreateSprite(&sSpriteTemplate_TeraTrigger, - gSprites[gHealthboxSpriteIds[battler]].x - SINGLES_TERA_TRIGGER_POS_X_SLIDE, - gSprites[gHealthboxSpriteIds[battler]].y - SINGLES_TERA_TRIGGER_POS_Y_DIFF, 0); - } - gSprites[gBattleStruct->tera.triggerSpriteId].tBattler = battler; - gSprites[gBattleStruct->tera.triggerSpriteId].tHide = FALSE; - - ChangeTeraTriggerSprite(gBattleStruct->tera.triggerSpriteId, palId); -} - -static void SpriteCb_TeraTrigger(struct Sprite *sprite) -{ - s32 xSlide, xPriority, xOptimal; - s32 yDiff; - - if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) - { - xSlide = DOUBLES_TERA_TRIGGER_POS_X_SLIDE; - xPriority = DOUBLES_TERA_TRIGGER_POS_X_PRIORITY; - xOptimal = DOUBLES_TERA_TRIGGER_POS_X_OPTIMAL; - yDiff = DOUBLES_TERA_TRIGGER_POS_Y_DIFF; - } - else - { - xSlide = SINGLES_TERA_TRIGGER_POS_X_SLIDE; - xPriority = SINGLES_TERA_TRIGGER_POS_X_PRIORITY; - xOptimal = SINGLES_TERA_TRIGGER_POS_X_OPTIMAL; - yDiff = SINGLES_TERA_TRIGGER_POS_Y_DIFF; - } - - if (sprite->tHide) - { - if (sprite->x != gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xSlide) - sprite->x++; - - if (sprite->x >= gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xPriority) - sprite->oam.priority = 2; - else - sprite->oam.priority = 1; - - sprite->y = gSprites[gHealthboxSpriteIds[sprite->tBattler]].y - yDiff; - sprite->y2 = gSprites[gHealthboxSpriteIds[sprite->tBattler]].y2 - yDiff; - if (sprite->x == gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xSlide) - DestroyTeraTriggerSprite(); - } - else - { - if (sprite->x != gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xOptimal) - sprite->x--; - - if (sprite->x >= gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xPriority) - sprite->oam.priority = 2; - else - sprite->oam.priority = 1; - - sprite->y = gSprites[gHealthboxSpriteIds[sprite->tBattler]].y - yDiff; - sprite->y2 = gSprites[gHealthboxSpriteIds[sprite->tBattler]].y2 - yDiff; - } -} - -bool32 IsTeraTriggerSpriteActive(void) -{ - if (GetSpriteTileStartByTag(TAG_TERA_TRIGGER_TILE) == 0xFFFF) - return FALSE; - else if (IndexOfSpritePaletteTag(TAG_TERA_TRIGGER_PAL) != 0xFF) - return TRUE; - else - return FALSE; -} - -void HideTeraTriggerSprite(void) -{ - if (gBattleStruct->tera.triggerSpriteId != 0xFF) - { - ChangeTeraTriggerSprite(gBattleStruct->tera.triggerSpriteId, 0); - gSprites[gBattleStruct->tera.triggerSpriteId].tHide = TRUE; - } -} - -void DestroyTeraTriggerSprite(void) -{ - FreeSpritePaletteByTag(TAG_TERA_TRIGGER_PAL); - FreeSpriteTilesByTag(TAG_TERA_TRIGGER_TILE); - if (gBattleStruct->tera.triggerSpriteId != 0xFF) - DestroySprite(&gSprites[gBattleStruct->tera.triggerSpriteId]); - gBattleStruct->tera.triggerSpriteId = 0xFF; -} - -#undef tBattler -#undef tHide - // TERA INDICATOR: static const u16 sTeraIndicatorPal[] = INCBIN_U16("graphics/battle_interface/tera_indicator.gbapal"); static const u8 ALIGNED(4) sNormalIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/normal_indicator.4bpp"); @@ -701,7 +520,7 @@ void TeraIndicator_LoadSpriteGfx(void) bool32 TeraIndicator_ShouldBeInvisible(u32 battler) { - return !IsTerastallized(battler); + return GetActiveGimmick(battler) != GIMMICK_TERA; } u8 TeraIndicator_GetSpriteId(u32 healthboxSpriteId) diff --git a/src/battle_util.c b/src/battle_util.c index e11ddc27eaf4..5d5b701114e0 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -423,6 +423,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 +722,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)) @@ -1282,12 +1284,12 @@ static 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]; @@ -1325,7 +1327,7 @@ u32 TrySetCantSelectMoveBattleScript(u32 battler) if (gBattleStruct->zmove.toBeUsed[gBattlerAttacker] == MOVE_NONE && gDisableStructs[battler].tauntTimer != 0 && IS_MOVE_STATUS(move)) { - if (IsDynamaxed(gBattlerAttacker)) + if ((GetActiveGimmick(gBattlerAttacker) == GIMMICK_DYNAMAX)) gCurrentMove = MOVE_MAX_GUARD; else gCurrentMove = move; @@ -1465,7 +1467,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; @@ -2948,7 +2950,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; @@ -5281,7 +5283,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; @@ -5323,7 +5325,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) @@ -8800,7 +8802,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: @@ -8855,7 +8857,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: @@ -9228,7 +9230,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 @@ -9428,11 +9430,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; } @@ -9941,7 +9943,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)); @@ -10225,7 +10227,7 @@ uq4_12_t CalcTypeEffectivenessMultiplier(u32 move, u32 moveType, u32 battlerAtk, } else if (moveType == TYPE_STELLAR) { - modifier = IsTerastallized(battlerDef) ? UQ_4_12(2.0) : UQ_4_12(1.0); + modifier = GetActiveGimmick(battlerDef) == GIMMICK_TERA ? UQ_4_12(2.0) : UQ_4_12(1.0); } if (recordAbilities) @@ -10369,33 +10371,25 @@ 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); - // Check if Player has a Mega Ring + // 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)) 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]) - return FALSE; - - if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE - && IsPartnerMonFromSameTrainer(battler) - && (mega->alreadyEvolved[partnerPosition] || (mega->toEvolve & 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 + // Check if battler is currently held by Sky Drop. if (gStatuses3[battler] & STATUS3_SKY_DROPPED) return FALSE; - // Gets mon data. + // Gets battler data. if (GetBattlerSide(battler) == B_SIDE_OPPONENT) mon = &gEnemyParty[gBattlerPartyIndexes[battler]]; else @@ -10408,7 +10402,11 @@ bool32 CanMegaEvolve(u32 battler) else holdEffect = ItemId_GetHoldEffect(itemId); - // Check if there is an entry in the evolution table for regular Mega Evolution. + // Check if battler is holding a Z-Crystal. + if (holdEffect == HOLD_EFFECT_Z_CRYSTAL) + return FALSE; + + // Check if there is an entry in the form change table for regular Mega Evolution. if (GetBattleFormChangeTargetSpecies(battler, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM) != SPECIES_NONE) { // Can Mega Evolve via Mega Stone. @@ -10416,12 +10414,10 @@ bool32 CanMegaEvolve(u32 battler) 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. @@ -10432,25 +10428,22 @@ bool32 CanUltraBurst(u32 battler) { u32 itemId, holdEffect; struct Pokemon *mon; - u32 battlerPosition = GetBattlerPosition(battler); - u8 partnerPosition = GetBattlerPosition(BATTLE_PARTNER(battler)); - // Check if Player has a Z Ring + // 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)) 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]) + // Check if battler has another gimmick active. + if (GetActiveGimmick(battler) != GIMMICK_NONE) return FALSE; - if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE - && IsPartnerMonFromSameTrainer(battler) - && (gBattleStruct->burst.alreadyBursted[partnerPosition] || (gBattleStruct->burst.toBurst & gBitTable[BATTLE_PARTNER(battler)]))) + // Cannot use Z-move and Ultra Burst on same turn + if (gBattleStruct->zmove.toBeUsed[battler]) return FALSE; // Check if mon is currently held by Sky Drop @@ -10465,7 +10458,7 @@ bool32 CanUltraBurst(u32 battler) itemId = GetMonData(mon, MON_DATA_HELD_ITEM); - // Check if there is an entry in the evolution table for Ultra Burst. + // Check if there is an entry in the form change table for Ultra Burst. if (GetBattleFormChangeTargetSpecies(battler, FORM_CHANGE_BATTLE_ULTRA_BURST) != SPECIES_NONE) { if (itemId == ITEM_ENIGMA_BERRY_E_READER) @@ -10482,6 +10475,21 @@ bool32 CanUltraBurst(u32 battler) return FALSE; } +void ActivateMegaEvolution(u32 battler) +{ + gLastUsedItem = gBattleMons[battler].item; + 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; + BattleScriptExecute(BattleScript_UltraBurst); +} + bool32 IsBattlerMegaEvolved(u32 battler) { // While Transform does copy stats and visuals, it shouldn't be counted as true Mega Evolution. @@ -11183,7 +11191,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; @@ -11222,7 +11230,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); @@ -11426,14 +11434,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; @@ -11448,7 +11456,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++) { diff --git a/src/data/gimmicks.h b/src/data/gimmicks.h new file mode 100644 index 000000000000..d54008ed7e84 --- /dev/null +++ b/src/data/gimmicks.h @@ -0,0 +1,146 @@ +// General trigger data + +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 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, +}; + +// Mega trigger data +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_GIMMICK_TRIGGER_TILE +}; +static const struct SpritePalette sSpritePalette_MegaTrigger = +{ + sMegaTriggerPal, TAG_GIMMICK_TRIGGER_TILE +}; + +// Ultra Burst trigger data +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_GIMMICK_TRIGGER_TILE +}; +static const struct SpritePalette sSpritePalette_BurstTrigger = +{ + sBurstTriggerPal, TAG_GIMMICK_TRIGGER_TILE +}; + +// Dynamax trigger data + +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_GIMMICK_TRIGGER_TILE +}; +static const struct SpritePalette sSpritePalette_DynamaxTrigger = +{ + sDynamaxTriggerPal, TAG_GIMMICK_TRIGGER_PAL +}; + +// Tera trigger data +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_GIMMICK_TRIGGER_TILE +}; +static const struct SpritePalette sSpritePalette_TeraTrigger = +{ + sTeraTriggerPal, TAG_GIMMICK_TRIGGER_TILE +}; + +// Gimmick data + +const struct GimmickInfo gGimmicksInfo[GIMMICKS_COUNT] = +{ + [GIMMICK_NONE] = {0}, + [GIMMICK_MEGA] = + { + .triggerSheet = &sSpriteSheet_MegaTrigger, + .triggerPal = &sSpritePalette_MegaTrigger, + .triggerTemplate = &sSpriteTemplate_GimmickTrigger, + .CanActivate = CanMegaEvolve, + .ActivateGimmick = ActivateMegaEvolution, + }, + [GIMMICK_Z_MOVE] = + { + .triggerSheet = &sSpriteSheet_DynamaxTrigger, + .triggerPal = &sSpritePalette_DynamaxTrigger, + .triggerTemplate = &sSpriteTemplate_GimmickTrigger, + .CanActivate = NULL, + }, + [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, + .CanActivate = CanDynamax, + .ActivateGimmick = ActivateDynamax, + }, + [GIMMICK_TERA] = + { + .triggerSheet = &sSpriteSheet_TeraTrigger, + .triggerPal = &sSpritePalette_TeraTrigger, + .triggerTemplate = &sSpriteTemplate_GimmickTrigger, + .CanActivate = CanTerastallize, + .ActivateGimmick = ActivateTera, + } +}; diff --git a/test/battle/trainer_control.h b/test/battle/trainer_control.h index b0f417bb4d29..09c4a73d6429 100644 --- a/test/battle/trainer_control.h +++ b/test/battle/trainer_control.h @@ -52,6 +52,7 @@ .isShiny = TRUE, #line 18 .dynamaxLevel = 5, + .useGimmick = GIMMICK_DYNAMAX, .moves = { #line 19 MOVE_AIR_SLASH, diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index aaeba97479ea..bd4169e3ec45 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -2068,16 +2068,28 @@ void MoveGetIdAndSlot(s32 battlerId, struct MoveContext *ctx, u32 *moveId, u32 * } if (ctx->explicitMegaEvolve && ctx->megaEvolve) - *moveSlot |= RET_MEGA_EVOLUTION; + { + DATA.chosenGimmick[battlerId] = GIMMICK_MEGA; + *moveSlot |= RET_GIMMICK; + } if (ctx->explicitUltraBurst && ctx->ultraBurst) - *moveSlot |= RET_ULTRA_BURST; + { + DATA.chosenGimmick[battlerId] = GIMMICK_ULTRA_BURST; + *moveSlot |= RET_GIMMICK; + } if (ctx->explicitDynamax && ctx->dynamax) - *moveSlot |= RET_DYNAMAX; + { + DATA.chosenGimmick[battlerId] = GIMMICK_DYNAMAX; + *moveSlot |= RET_GIMMICK; + } if (ctx->explicitTera && ctx->tera) - *moveSlot |= RET_TERASTAL; + { + DATA.chosenGimmick[battlerId] = GIMMICK_TERA; + *moveSlot |= RET_GIMMICK; + } } void Move(u32 sourceLine, struct BattlePokemon *battler, struct MoveContext ctx) @@ -2641,6 +2653,11 @@ u32 TestRunner_Battle_GetForcedAbility(u32 side, u32 partyIndex) return DATA.forcedAbilities[side][partyIndex]; } +u32 TestRunner_Battle_GetChosenGimmick(u32 battler) +{ + return DATA.chosenGimmick[battler]; +} + // TODO: Consider storing the last successful i and searching from i+1 // to improve performance. struct AILogLine *GetLogLine(u32 battlerId, u32 moveIndex) diff --git a/tools/trainerproc/main.c b/tools/trainerproc/main.c index de8b7786c4e8..41ffb8bcef10 100644 --- a/tools/trainerproc/main.c +++ b/tools/trainerproc/main.c @@ -1824,7 +1824,7 @@ 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) @@ -1833,7 +1833,7 @@ static void fprint_trainers(const char *output_path, FILE *f, struct Parsed *par 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) From bafc3f254397198b11b4645190b3f34456ae1dda Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Fri, 26 Apr 2024 00:16:08 -0700 Subject: [PATCH 02/33] fixed improper use of .usableGimmick --- src/battle_controller_opponent.c | 2 +- src/battle_controller_player_partner.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index 10da422bd7ca..3dda57d3cfe8 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -555,7 +555,7 @@ static void OpponentHandleChooseMove(u32 battler) if (ShouldUseZMove(battler, gBattlerTarget, chosenMove)) QueueZMove(battler, chosenMove); // If opponent can and should use a gimmick (considering trainer data), do it - if (gBattleStruct->gimmick.usableGimmick != GIMMICK_NONE) + if (gBattleStruct->gimmick.usableGimmick[battler] != GIMMICK_NONE) BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_GIMMICK) | (gBattlerTarget << 8)); else BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (gBattlerTarget << 8)); diff --git a/src/battle_controller_player_partner.c b/src/battle_controller_player_partner.c index 57e6f7cfec53..9ffe599f1327 100644 --- a/src/battle_controller_player_partner.c +++ b/src/battle_controller_player_partner.c @@ -373,7 +373,7 @@ static void PlayerPartnerHandleChooseMove(u32 battler) QueueZMove(battler, moveInfo->moves[chosenMoveId]); // If opponent can and should use a gimmick (considering trainer data), do it - if (gBattleStruct->gimmick.usableGimmick != GIMMICK_NONE) + if (gBattleStruct->gimmick.usableGimmick[battler] != GIMMICK_NONE) BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_GIMMICK) | (gBattlerTarget << 8)); else BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (gBattlerTarget << 8)); From 927f15c304509542f1130e741ad44fe98ac3ab66 Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Fri, 26 Apr 2024 14:36:37 -0700 Subject: [PATCH 03/33] cleaning up battle_dynamax.c, changing function args to u32s --- include/battle_dynamax.h | 12 ++++++------ src/battle_dynamax.c | 34 ++++++++++++++++------------------ 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/include/battle_dynamax.h b/include/battle_dynamax.h index ad47fe5a8878..7f823396ba01 100644 --- a/include/battle_dynamax.h +++ b/include/battle_dynamax.h @@ -63,13 +63,13 @@ void ActivateDynamax(u32 battler); u16 GetNonDynamaxHP(u32 battler); u16 GetNonDynamaxMaxHP(u32 battler); void UndoDynamax(u32 battler); -bool32 IsMoveBlockedByMaxGuard(u16 move); -bool32 IsMoveBlockedByDynamax(u16 move); +bool32 IsMoveBlockedByMaxGuard(u32 move); +bool32 IsMoveBlockedByDynamax(u32 move); -bool32 ShouldUseMaxMove(u16 battler, u16 baseMove); -u16 GetMaxMove(u16 battler, u16 baseMove); -u8 GetMaxMovePower(u16 move); -bool32 IsMaxMove(u16 move); +bool32 ShouldUseMaxMove(u32 battler, u32 baseMove); +u16 GetMaxMove(u32 battler, u32 baseMove); +u8 GetMaxMovePower(u32 move); +bool32 IsMaxMove(u32 move); void ChooseDamageNonTypesString(u8 type); void BS_UpdateDynamax(void); diff --git a/src/battle_dynamax.c b/src/battle_dynamax.c index b3c525c2bc5d..2e5a6a863cfe 100644 --- a/src/battle_dynamax.c +++ b/src/battle_dynamax.c @@ -23,7 +23,7 @@ #include "constants/items.h" #include "constants/moves.h" -static u8 GetMaxPowerTier(u16 move); +static u8 GetMaxPowerTier(u32 move); struct GMaxMove { @@ -77,10 +77,8 @@ bool32 CanDynamax(u32 battler) u16 holdEffect = ItemId_GetHoldEffect(gBattleMons[battler].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; + if (B_FLAG_DYNAMAX_BATTLE == 0 || (B_FLAG_DYNAMAX_BATTLE != 0 && !FlagGet(B_FLAG_DYNAMAX_BATTLE))) + return FALSE; // Check if Player has a Dynamax Band. @@ -215,7 +213,7 @@ void UndoDynamax(u32 battler) } // Certain moves are blocked by Max Guard that normally ignore protection. -bool32 IsMoveBlockedByMaxGuard(u16 move) +bool32 IsMoveBlockedByMaxGuard(u32 move) { switch (move) { @@ -234,7 +232,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) @@ -247,7 +245,7 @@ bool32 IsMoveBlockedByDynamax(u16 move) } // Returns whether a move should be converted into a Max Move. -bool32 ShouldUseMaxMove(u16 battler, u16 baseMove) +bool32 ShouldUseMaxMove(u32 battler, u32 baseMove) { // TODO: Raid bosses do not always use Max Moves. // if (IsRaidBoss(battler)) @@ -255,12 +253,12 @@ bool32 ShouldUseMaxMove(u16 battler, u16 baseMove) return GetActiveGimmick(battler) == GIMMICK_DYNAMAX || IsGimmickSelected(battler, GIMMICK_DYNAMAX); } -static u16 GetTypeBasedMaxMove(u16 battler, u16 type) +static u16 GetTypeBasedMaxMove(u32 battler, u32 type) { // Gigantamax check u32 i; - u16 species = gBattleMons[battler].species; - u16 targetSpecies = SPECIES_NONE; + u32 species = gBattleMons[battler].species; + u32 targetSpecies = SPECIES_NONE; if (!gSpeciesInfo[species].isGigantamax) targetSpecies = GetBattleFormChangeTargetSpecies(battler, FORM_CHANGE_BATTLE_GIGANTAMAX); @@ -284,9 +282,9 @@ static u16 GetTypeBasedMaxMove(u16 battler, u16 type) } // Returns the appropriate Max Move or G-Max Move for a battler to use. -u16 GetMaxMove(u16 battler, u16 baseMove) +u16 GetMaxMove(u32 battler, u32 baseMove) { - u16 move = baseMove; + u32 move = baseMove; if (baseMove == MOVE_NONE) // for move display { return MOVE_NONE; @@ -327,7 +325,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. @@ -378,7 +376,7 @@ u8 GetMaxMovePower(u16 move) } } -static u8 GetMaxPowerTier(u16 move) +static u8 GetMaxPowerTier(u32 move) { if (gMovesInfo[move].strikeCount >= 2 && gMovesInfo[move].strikeCount <= 5) { @@ -454,7 +452,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; } @@ -480,7 +478,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) @@ -519,7 +517,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. From 8c5b70fdbf9bf7fedd3993f0ff89929610c86b12 Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Tue, 30 Apr 2024 12:59:04 -0700 Subject: [PATCH 04/33] fixed '#ifdef TESTING' causing errors --- src/battle_gimmick.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/battle_gimmick.c b/src/battle_gimmick.c index fcce6c10c0a7..bb05bfdb5129 100644 --- a/src/battle_gimmick.c +++ b/src/battle_gimmick.c @@ -20,7 +20,7 @@ static void SpriteCb_GimmickTrigger(struct Sprite *sprite); void AssignUsableGimmicks(void) { u32 battler, gimmick; - #ifdef TESTING + #if TESTING for (battler = 0; battler < gBattlersCount; ++battler) { gimmick = TestRunner_Battle_GetChosenGimmick(battler); @@ -54,7 +54,7 @@ bool32 CanActivateGimmick(u32 battler, enum Gimmick gimmick) // Returns whether the player has a gimmick selected while in the move selection menu. bool32 IsGimmickSelected(u32 battler, enum Gimmick gimmick) { - #ifdef TESTING + #if TESTING return (GetActiveGimmick(battler) == GIMMICK_NONE && !HasTrainerUsedGimmick(battler, gimmick) && (gBattleStruct->gimmick.toActivate & gBitTable[battler]) From 2289f588b9b1a3c91ca7ae92b650f8ad287f5997 Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Thu, 2 May 2024 10:17:48 -0700 Subject: [PATCH 05/33] updated z-moves to use gimmick interface, pared down redundancies; no AI/tests --- graphics/battle_interface/z_move_trigger.png | Bin 275 -> 7461 bytes include/battle.h | 14 +- include/battle_gimmick.h | 2 +- include/battle_interface.h | 9 - include/battle_z_move.h | 21 +- src/battle_ai_util.c | 29 +- src/battle_controller_opponent.c | 3 +- src/battle_controller_player.c | 45 ++- src/battle_controller_player_partner.c | 4 +- src/battle_gimmick.c | 1 + src/battle_main.c | 11 +- src/battle_script_commands.c | 18 +- src/battle_util.c | 64 ++-- src/battle_z_move.c | 295 +++++-------------- src/data/gimmicks.h | 19 +- src/data/moves_info.h | 2 +- 16 files changed, 185 insertions(+), 352 deletions(-) diff --git a/graphics/battle_interface/z_move_trigger.png b/graphics/battle_interface/z_move_trigger.png index d719494d9f0a64b59552862bed58adfc2c8056b5..9ba651b34b9c3221985ed18c2e29e81ecef9abf9 100644 GIT binary patch literal 7461 zcmeHLXH-*J*A56GO+W-h6k7;hXF0Pr4tk%3EE zMwW(sBl8hhXY?VF2afg0FV1bUJoBn89H_n%WvPTImNFKFC+aN9ZSPFQ?I;KKQ|%yg zqZcH?ch7$uY;m|_x7kxp2s*R#D0f}1Z7Mge?t=o4+|iNtogNF}VdeOpVZbJ~{QA^E z*JV$~K&3trqeClQD)C~P*@>%b9V)vpV~$PMZ^sL+&b zvIdE~R+0$q#MX)5Hb%dCk1t4ch#Ww&zZ*+_?W?#w@UC8B`*GI78ltY2JZnFp6P}RP zaVtmffg&U;Okva`+-Od=;_}MCwiJt>K*E}vU2@OH1d9VgX?rD7+1|bGi(`zKPLaF1 z)v3`3{BDb=wyqGf{&?k^?ZPy7heyG(B&quOnU&fJ;cEZaPsYfYb&mPk({j6?3I2g6 z7f%$_Z(7#99y}@{A|;j{0lG6~%MARg;FAJw?&%p8X_F3hcm$Q@mn-$*f>i_&^?Z(U zEw+AEIw{bQ=7ke76&zyijB?<` zirPEAbuzO=5v>XUsN=}_-Vbuk;-}o#?-U-4)QUsQW$BgH&gE!FpGmd7P^{TT7_>5b zQ8j3DX@y$(%CxJbax~Me;hM0o#tE`S!>1uXkt>mhwS>nNYaC()v01ozYizd1Cj|co zYuwyL3%eI3jqQi!@f64FcC@hou6K;7qPl6Iiy6)c_jd>F ze4!40;jMDeSqlFce!P0<^NFYTvYgsDTGcjy_IwP7P9Ckex1ADdwn%@nJOr^|Gx)Mr z_+Z@nGwMoT!zWlsxkR$qu69lMIg;Vc9RhIS=;O-?J8@%NMII$G%Q4;D{ZfN@_K6?l z#o~Ik(wDg}k9pYSY`8Fohzq>V&Ri&dN*t)ZIFp0d8(ZH+zIW_%jte;3V`tstH$vJN zWtzS3-QD*bu`K-YEffQ*u0*L>b(COeY;$)#-ODRQcU$3xtUg1?7AoXq=#wpek*CfUWUtc=JQETZ=kKEM}R7d1#5w7BYs|pfErIY zj5+Q>?btBS%F^&brc>hf>8c*2=x1@-{z?y{D%=}N%P$+IGY@uWz|m7#juX@MmxMBs z-!T;@9^)CX(W^ehS=T_a>}y~xz^?dcSw@X@2C&}I5c9rR#$w2sDuv-*XLl}@$uo{e z7&V!F`qCA4+GkALHCMjOBf?Wa<@v@_xRV7!FL#yI@shs7QyHsvb6IQJqZcndO4R&> z!V=;f7G7x~1fR{HVM~Adk@4!wfQaRgG`}l)HwDSWh6qXik!5oo3RPtTcf_6X9K##) zSKYrb4TJa|(rAaM>pY0j7Iy6A3x}T$(z+WW%xQ9NJh%t$0wCPJ~tHp`^yJ*th!P zo;O7ed|!d;lJyQhG(G+4cX$|YvZ8k62>DY?ofa_Cl2c=j=T%=UcT!rkZS;0~(lhy6 zhj4&CwN6m+sk+1FC5HKC2acfGdrv*tNEiye4dO8YUW#pyzuCNcRVsV7`8V-k^9&nx z>y@-2hl*pVv39aOG{FLK-iJ!N^C9v(NG+y01_9;>-}O92A8xKKiFx+{^*+M@W>SE-!I z_L=Svn&+mb7$&DccsBY+RPU_jgjsX4i~v49xvg)XP^-%)EX}Nhz4i3I6=y4xwWvgQRjM8L z?Ab^?rYOfE6Sd$q+x*p`nlAT_wf2TrY4s>-=lDT>%7yY1vZ|?lQOZuO%5Jr7%7)W+ zyqEHb-=5NmuN}0;nXy~hPS5re;iY}|D^KjWT3So-XLQ@j5tFi z%X5XE7FsFWZTwxuWc&(g4V$xJxWuvo>c4L>fGzeH04Ev}@=31W5>g#cY8F@?d)ml1 z^=9WD4tqmKrJZ{5Pb-XaYYg+e#cWlQb>+j*_ zrbF$ao0<)nfxsf1P5(II+}Rwf-QSwgf`I!%ERGCXSI#B%4)-6YEvE^Aikbv`)I|DEE^5 zaDrZI@NbtOR$S$eC1jSTCcicb{ZiL7eQeinjc7OZI1+5`v1w_$%T*ZYP+|RUu2|C(RJi}|ins6>A@5Rg3J1&N9WtEIEp1%fU$D@lMv`V>NQ!y;kCJ7no z7G58GAKToYfD(Q`yGl%gFwlBC!>{I)^qx%g4dftL`w7RzM3$jJ{>ce9SGc8i5iGth7-<;O# zcdlpW7@I3Sp0U*9_|j8)Oh8w~u%T=1r|CWOEXfLwaRX?kBwz!Hlo+5ue%>y@L zpYd@l&rFC9c1^MS*@;zChPHX(6{8OY!^K(!_cHO6fVSztyAGir0N1uc-n{KUTqx8g zFD=`OcC)i*pm}|u#(pBNcNe2BwKJrwTscrC(~aGBpLGZcDp%PV)01bs@hVh1^!ad0 z>;3oV&#p@a>cFd}p|M{->&j0Ryw^zba%_ru z5LzS7zT7YtWNK4aTERxJc>yKA}~3~T>r@-V{&JYQMYSP2q7 z1ZYE+I%Nmj-Q^6D^D3@rtQ|{DwczUV$kD54*Dt&rZ!dtIU`=dgs|QbRgdPMMMt%cO zMT^4jM;U49xO8Q`N{&^o!l{~ty&rmucRX+-{gHdafEn$9^k<7((~JyBaGCN9A>YF4 zPciuI8v{+(zKT~iTTl7dyni>-V(-@*C(0)J^@uiyvhmWz`_C?Llx^%}*u2RYrwxRa z1QIw!vTxdyITNfp`m0Z#hq@Z5Ub68_oJ{*=Al4y|DBlWyB-}(=bMtwoi1R;D!=0~uxNZptA+S&4E$rddcMWYjE`St96rNPb%iNBNbG&hlc zEa~E|-jNPvN=wjS133+vsPgeUR=V9z*BS5`Z&7}`Wj34C zd6D6C2cxU;DMaA9@|NPI3OVVev&^qmrrRDVYlTeO<-IIlSwoNEvjJb7r!OpTxfwWo zO8%gweN~v@%2;z7RXcezxa%Zvo&%Yh|F1L=!w9C?|p=2IB2O zq_?^NfP%6&5ruZcP=JmYXB=J;v{u&y0^*z$L6*`6Py?bm#s#P2OTw7>8k(bh-Ovap zkg^h+f;W;5;DMo_fZiVNcrwyk5wwqsq`&WpB|yM^6^ff8$jZPNs7@eZfKm`C2o$X8 zjq`$ol-Pg@BquD=R72|r1bw6ka-mR&NC^oVjRv7fLI@;h2^a!_kbuG^;BYWq15Eb8 zQ&8SuJo)q<#CHr03>i(r5h*wV9=L~zawJeGiXaeu9{5Lm9z+9!KjHD@A1u)Mknlzk zC14P!golU3&lY5grWYOLM?nA8f^1IzM?k_9Lncs3XpE*822VNtGlUcRPkSPjHHiB-TY77 zziI!G`@S+=%fJAsK|oXY!qe4I1nv1pIuXz~C*=Ms1}h6gNJ0@{X%yTEEQOXsgXN^L zl3*DuN=g!rK_Osp>`zd-crpcrM`QM&=-?0>9R~}SgG-?iP_V2tRE`dTMuQP(ge+KE z29AMB!4NPsOztNLBNC2Yl_>Y0quPUVqC-hSC8Z=W7$-0W0YiYL;IddSN(LhdmX(y0 zb;LM9p^{M9eJCe1Qj0+HK+%^I=Yev@ND%SP`xASFBUO!c6+v(a^e>6AJBos(8_@Rv z4(~*uk^h=9$9Z7PD5yO?VX`nO87LeoEhi%@D=qsy>U~5D42evy#XVFQ6atspH{V+r zBt0EEwWz&Hr338C>DeIFNf;D`Kr$x~+!aB45Wqdl{Q?Ind|wtF9GR}+vsd#!s@@Fa z`F-?#6S(8{r+~oy!bPIc-<^|6?EEiM~E-3@U zV!?13lr&gM28IG79Hr#I7$_W$kd={v$)LZd_a{1;fThq-B#f#vJyUu%^b*?72KdVl zGR6KL4b26!#}pJU1%^t4p)hlpG!hO)O3Q&Hek3cgx2ONeRzc$b@S(7;@Y5kcxBD)m zA1?H>O5)GM>IYwY6#fsdAM5Zx^gxIH*U3NP_g}jHrRyIt@Q;-Lt*(FR`bP}>Bjtaq z>;H`|w!hjP44(c4M58w|?U#IT^hOA3tZ%Ma+mmmALoF^EZ*94FyEsI7yZBv?Y;Fc@ zZEdZuJM8sKf4?$$gKp6!432u*8h|L~lRESPD^bUq3;?k6?%fQ4%&cQ{Aqz#)ezEGq!C#E-2DFB#=g*uc*><)=K+V&q;m9Sz$T#%2_*?}B&tB+6 UYEk@YdRzcqO+$@RHHXmu03FCjg@J{DS|ZfwPL6rT`T+dAc};SoFS~=q=Qs zz~j7ftLyvv#LHdrhgfCqx4GWqQJGS7n01Pw-sweFW)u7qGPcxYyxHj?^+a{1_awH{ zPh0LbwN-7}v}t2w)>_36rU^5a9(rDMM67MrMX|D{4O?a~_e^+h@<;5`^mTi7ABz4P vb$G(NiSB$(FI%GTRdDICY`T1&{RV@8Ez^ZZ87;Sg&SCI$^>bP0l+XkK0JLHp diff --git a/include/battle.h b/include/battle.h index 1e8403e8009d..bbea348e76cc 100644 --- a/include/battle.h +++ b/include/battle.h @@ -562,21 +562,11 @@ 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 diff --git a/include/battle_gimmick.h b/include/battle_gimmick.h index 078b8c1a9524..2eb1d9bca016 100644 --- a/include/battle_gimmick.h +++ b/include/battle_gimmick.h @@ -5,8 +5,8 @@ enum Gimmick { GIMMICK_NONE, GIMMICK_MEGA, - GIMMICK_Z_MOVE, GIMMICK_ULTRA_BURST, + GIMMICK_Z_MOVE, GIMMICK_DYNAMAX, GIMMICK_TERA, GIMMICKS_COUNT, diff --git a/include/battle_interface.h b/include/battle_interface.h index dcd89fab85ce..558ff1b0e0f8 100644 --- a/include/battle_interface.h +++ b/include/battle_interface.h @@ -49,13 +49,9 @@ enum #define TAG_HEALTHBOX_PAL TAG_HEALTHBOX_PLAYER1_TILE #define TAG_GIMMICK_TRIGGER_TILE 0xD777 -#define TAG_MEGA_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_NORMAL_INDICATOR_TILE 0xD77F @@ -81,14 +77,9 @@ enum #define TAG_TERA_TRIGGER_TILE 0xD793 #define TAG_GIMMICK_TRIGGER_PAL 0xD777 -#define TAG_MEGA_TRIGGER_PAL 0xD777 #define TAG_MEGA_INDICATOR_PAL 0xD778 #define TAG_MISC_INDICATOR_PAL 0xD779 // Alpha, Omega, and Dynamax indicators use the same palette as each of them only uses 4 different colors. -#define TAG_ZMOVE_TRIGGER_PAL 0xD77B -#define TAG_BURST_TRIGGER_PAL 0xD77C -#define TAG_DYNAMAX_TRIGGER_PAL 0xD77D #define TAG_TERA_INDICATOR_PAL 0xD77E -#define TAG_TERA_TRIGGER_PAL 0xD77F enum { diff --git a/include/battle_z_move.h b/include/battle_z_move.h index 92fb685b2f19..0350a0f31de2 100644 --- a/include/battle_z_move.h +++ b/include/battle_z_move.h @@ -13,19 +13,16 @@ 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 TryChangeZIndicator(u32 battler, u32 moveIndex); +u32 GetTypeBasedZMove(u32 move, u32 battler); 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 diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 360cc20b2039..508518b1d2c2 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -454,28 +454,26 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes SetBattlerData(battlerAtk); SetBattlerData(battlerDef); - // Temporarily enable Z-Moves for damage calcs - if (considerZPower && IsViableZMove(battlerAtk, move)) - { - gBattleStruct->zmove.baseMoves[battlerAtk] = move; - gBattleStruct->zmove.active = TRUE; - } - else if (gMovesInfo[move].effect == EFFECT_PHOTON_GEYSER) + if (gMovesInfo[move].effect == EFFECT_PHOTON_GEYSER) gBattleStruct->swapDamageCategory = (GetCategoryBasedOnStats(gBattlerAttacker) == DAMAGE_CATEGORY_PHYSICAL); if (gMovesInfo[move].effect == EFFECT_NATURE_POWER) move = GetNaturePowerMove(); - // Temporarily enable other gimmicks for damage calcs if planned - if (gBattleStruct->gimmick.usableGimmick[battlerAtk] && GetActiveGimmick(battlerAtk) == GIMMICK_NONE) + // 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)) { + // 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]); } gBattleStruct->dynamicMoveType = 0; - SetTypeBeforeUsingMove(move, battlerAtk); GET_MOVE_TYPE(move, moveType); @@ -523,7 +521,7 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes dmg = LowestRollDmg(normalDmg); } - if (!gBattleStruct->zmove.active) + if (GetActiveGimmick(battlerAtk) != GIMMICK_Z_MOVE) { // Handle dynamic move damage switch (gMovesInfo[move].effect) @@ -587,8 +585,8 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes // convert multiper to AI_EFFECTIVENESS_xX *typeEffectiveness = AI_GetEffectiveness(effectivenessMultiplier); + // Undo temporary settings gBattleStruct->swapDamageCategory = FALSE; - gBattleStruct->zmove.active = FALSE; gBattleStruct->zmove.baseMoves[battlerAtk] = MOVE_NONE; if (toggledGimmick) SetActiveGimmick(battlerAtk, GIMMICK_NONE); @@ -3633,7 +3631,7 @@ 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]) + if (HasTrainerUsedGimmick(battlerAtk, GIMMICK_Z_MOVE)) return FALSE; //cant use z move twice if (IsViableZMove(battlerAtk, chosenMove)) @@ -3646,11 +3644,6 @@ bool32 ShouldUseZMove(u32 battlerAtk, u32 battlerDef, u32 chosenMove) if (gBattleMons[battlerDef].ability == ABILITY_ICE_FACE && 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)) - return FALSE; - else if (!IS_MOVE_STATUS(chosenMove) && IS_MOVE_STATUS(gBattleStruct->zmove.chosenZMove)) - return FALSE; - if (!IS_MOVE_STATUS(chosenMove) && AI_CalcDamageSaveBattlers(chosenMove, battlerAtk, battlerDef, &effectiveness, FALSE) >= gBattleMons[battlerDef].hp) return FALSE; // don't waste damaging z move if can otherwise faint target diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index 3dda57d3cfe8..7c504f08ac3a 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -552,8 +552,7 @@ 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 and should use a gimmick (considering trainer data), do it if (gBattleStruct->gimmick.usableGimmick[battler] != GIMMICK_NONE) BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_GIMMICK) | (gBattlerTarget << 8)); diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index 2d09451c15e3..ceb9570580ca 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -690,9 +690,6 @@ 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 @@ -878,26 +875,25 @@ static void HandleInputChooseMove(u32 battler) ChangeGimmickTriggerSprite(gBattleStruct->gimmick.triggerSpriteId, gBattleStruct->gimmick.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); - // } } } static void ReloadMoveNames(u32 battler) { - 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) @@ -1050,7 +1046,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)) { @@ -2022,18 +2018,19 @@ static void PlayerHandleChooseMove(u32 battler) } else { - // struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]); + struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]); InitMoveSelectionsVarsAndStrings(battler); 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; - CreateGimmickTriggerSprite(battler); + 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; } } diff --git a/src/battle_controller_player_partner.c b/src/battle_controller_player_partner.c index 9ffe599f1327..f131449a631a 100644 --- a/src/battle_controller_player_partner.c +++ b/src/battle_controller_player_partner.c @@ -369,8 +369,8 @@ static void PlayerPartnerHandleChooseMove(u32 battler) gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); } - if (ShouldUseZMove(battler, gBattlerTarget, moveInfo->moves[chosenMoveId])) - QueueZMove(battler, moveInfo->moves[chosenMoveId]); + // if (ShouldUseZMove(battler, gBattlerTarget, moveInfo->moves[chosenMoveId])) + // QueueZMove(battler, moveInfo->moves[chosenMoveId]); // If opponent can and should use a gimmick (considering trainer data), do it if (gBattleStruct->gimmick.usableGimmick[battler] != GIMMICK_NONE) diff --git a/src/battle_gimmick.c b/src/battle_gimmick.c index bb05bfdb5129..0421bb1ccd45 100644 --- a/src/battle_gimmick.c +++ b/src/battle_gimmick.c @@ -4,6 +4,7 @@ #include "battle_controllers.h" #include "battle_interface.h" #include "battle_gimmick.h" +#include "battle_z_move.h" #include "battle_setup.h" #include "item.h" #include "palette.h" diff --git a/src/battle_main.c b/src/battle_main.c index 9d776b599150..c20cba6e7111 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -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" @@ -3685,10 +3687,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; return result; } @@ -4615,7 +4613,6 @@ static void HandleTurnActionSelectionState(void) gBattleStruct->gimmick.toActivate &= ~(gBitTable[BATTLE_PARTNER(GetBattlerPosition(battler))]); gBattleStruct->dynamax.usingMaxMove[BATTLE_PARTNER(GetBattlerPosition(battler))] = FALSE; - gBattleStruct->zmove.toBeUsed[BATTLE_PARTNER(GetBattlerPosition(battler))] = MOVE_NONE; BtlController_EmitEndBounceEffect(battler, BUFFER_A); MarkBattlerForControllerExec(battler); return; @@ -5037,8 +5034,8 @@ 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; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index f74e654f5873..06fce7ff0b8f 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1292,7 +1292,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; @@ -1398,7 +1398,7 @@ 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); @@ -1547,7 +1547,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; @@ -6316,16 +6316,14 @@ 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->enduredDamage = 0; gBattleStruct->additionalEffectsCounter = 0; gBattleStruct->poisonPuppeteerConfusion = FALSE; + if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE) + SetActiveGimmick(gBattlerAttacker, GIMMICK_NONE); gBattleScripting.moveendState++; break; case MOVEEND_COUNT: @@ -11028,10 +11026,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) && !IS_MOVE_STATUS(move)) { - gCurrentMove = gBattleStruct->zmove.chosenZMove = GetTypeBasedZMove(move, gBattlerAttacker); - QueueZMove(gBattlerAttacker, move); + gBattleStruct->zmove.baseMoves[gBattlerAttacker] = move; + gCurrentMove = GetTypeBasedZMove(move, gBattlerAttacker); } else { diff --git a/src/battle_util.c b/src/battle_util.c index 30da1b3acf5d..cc5188e5785a 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -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; @@ -186,9 +187,9 @@ void HandleAction_UseMove(void) } // check z move used - if (gBattleStruct->zmove.toBeUsed[gBattlerAttacker] != MOVE_NONE && !IS_MOVE_STATUS(gCurrentMove)) + if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !IS_MOVE_STATUS(gCurrentMove) && !IsZMove(gCurrentMove)) { - gCurrentMove = gBattleStruct->zmove.toBeUsed[gBattlerAttacker]; + gCurrentMove = GetUsableZMove(gBattlerAttacker, gCurrentMove); } if (gBattleMons[gBattlerAttacker].hp != 0) @@ -1284,7 +1285,7 @@ static bool32 IsBelchPreventingMove(u32 battler, u32 move) } // Dynamax bypasses all selection prevention except Taunt and Assault Vest. -#define DYNAMAX_BYPASS_CHECK (!IsGimmickSelected(gBattlerAttacker, GIMMICK_DYNAMAX) && !(GetActiveGimmick(gBattlerAttacker) == GIMMICK_DYNAMAX)) +#define DYNAMAX_BYPASS_CHECK (!IsGimmickSelected(gBattlerAttacker, GIMMICK_DYNAMAX) && GetActiveGimmick(gBattlerAttacker) != GIMMICK_DYNAMAX) u32 TrySetCantSelectMoveBattleScript(u32 battler) { @@ -1294,7 +1295,7 @@ u32 TrySetCantSelectMoveBattleScript(u32 battler) 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; @@ -1310,7 +1311,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) @@ -1325,7 +1326,7 @@ 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 ((GetActiveGimmick(gBattlerAttacker) == GIMMICK_DYNAMAX)) gCurrentMove = MOVE_MAX_GUARD; @@ -1343,7 +1344,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) @@ -1358,7 +1359,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) @@ -1373,7 +1374,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) @@ -1388,7 +1389,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) @@ -1403,7 +1404,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) @@ -3326,7 +3327,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; @@ -3338,7 +3339,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; @@ -3362,7 +3363,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); @@ -3373,7 +3374,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); @@ -3540,7 +3541,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); @@ -3551,23 +3552,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(); @@ -8331,7 +8327,7 @@ bool32 IsBattlerProtected(u32 battler, u32 move) } // Z-Moves and Max Moves bypass protection (except Max Guard). - if ((IsMaxMove(move) || gBattleStruct->zmove.active) + if ((IsZMove(move) || IsMaxMove(move)) && (!gProtectStructs[battler].maxGuarded || gMovesInfo[move].argument == MAX_EFFECT_BYPASS_PROTECT)) return FALSE; @@ -8628,7 +8624,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) @@ -9705,7 +9701,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); } @@ -10445,10 +10441,6 @@ bool32 CanUltraBurst(u32 battler) if (GetActiveGimmick(battler) != GIMMICK_NONE) return FALSE; - // Cannot use Z-move and Ultra Burst on same turn - if (gBattleStruct->zmove.toBeUsed[battler]) - return FALSE; - // Check if mon is currently held by Sky Drop if (gStatuses3[battler] & STATUS3_SKY_DROPPED) return FALSE; @@ -10815,8 +10807,6 @@ 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 diff --git a/src/battle_z_move.c b/src/battle_z_move.c index 203185cfdc85..46e1afbf578e 100644 --- a/src/battle_z_move.c +++ b/src/battle_z_move.c @@ -45,11 +45,9 @@ #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); @@ -108,257 +106,128 @@ 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) -{ - 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; -} - -bool32 IsViableZMove(u8 battler, u16 move) +bool32 CanUseZMove(u32 battler) { - u32 item; - u16 holdEffect; - int moveSlotIndex; - - item = gBattleMons[battler].item; + u32 holdEffect = GetBattlerHoldEffect(battler, FALSE); - for (moveSlotIndex = 0; moveSlotIndex < MAX_MON_MOVES; moveSlotIndex++) + // 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)) { - if (gBattleMons[battler].moves[moveSlotIndex] == move && gBattleMons[battler].pp[moveSlotIndex] == 0) - return FALSE; - } - - if (gBattleStruct->zmove.used[battler]) 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)) + // Check if Trainer has already used a Z-Move. + if (HasTrainerUsedGimmick(battler, GIMMICK_Z_MOVE)) return FALSE; - if (item == ITEM_ENIGMA_BERRY_E_READER) - return FALSE; // HoldEffect = gEnigmaBerries[battler].holdEffect; - else - holdEffect = ItemId_GetHoldEffect(item); + // Check if battler has another gimmick active. + if (GetActiveGimmick(battler) != GIMMICK_NONE) + return FALSE; + + // Check if battler isn't holding a Z-Crystal. + if (holdEffect != HOLD_EFFECT_Z_CRYSTAL) + return FALSE; + + // All checks passed! + return TRUE; +} + +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) - { - gBattleStruct->zmove.chosenZMove = zMove; // Signature z move exists - return TRUE; - } + return zMove; // Signature z move exists if (move != MOVE_NONE && zMove != MOVE_Z_STATUS && gMovesInfo[move].type == ItemId_GetSecondaryId(item)) - { - gBattleStruct->zmove.chosenZMove = GetTypeBasedZMove(move, battler); - return TRUE; - } + return GetTypeBasedZMove(move, battler); } - return FALSE; -} - -void GetUsableZMoves(u8 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); - } + return MOVE_NONE; } -bool32 IsZMoveUsable(u8 battler, u16 moveIndex) +void ActivateZMove(u32 battler) { - 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; + gBattleStruct->zmove.baseMoves[battler] = gBattleMons[battler].moves[gBattleStruct->chosenMovePositions[battler]]; + SetActiveGimmick(battler, GIMMICK_Z_MOVE); } -bool32 TryChangeZIndicator(u8 battler, u8 moveIndex) +bool32 IsViableZMove(u32 battler, u32 move) { - bool32 viableZMove = IsZMoveUsable(battler, moveIndex); - - if (gBattleStruct->zmove.viable && !viableZMove) - HideZMoveTriggerSprite(); // 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 - - 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] + u32 item; + u32 holdEffect = GetBattlerHoldEffect(battler, FALSE); + int moveSlotIndex; -void CreateZMoveTriggerSprite(u8 battler, bool8 viable) -{ - s16 x, y; + item = gBattleMons[battler].item; - LoadSpritePalette(&sSpritePalette_ZMoveTrigger); - if (GetSpriteTileStartByTag(TAG_ZMOVE_TRIGGER_TILE) == 0xFFFF) - LoadCompressedSpriteSheetUsingHeap(&sSpriteSheet_ZMoveTrigger); + if (gBattleStruct->gimmick.usableGimmick[battler] != GIMMICK_Z_MOVE) + return FALSE; - 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 + for (moveSlotIndex = 0; moveSlotIndex < MAX_MON_MOVES; moveSlotIndex++) { - x = gSprites[gHealthboxSpriteIds[battler]].x - SINGLES_Z_TRIGGER_POS_X_SLIDE; - y = gSprites[gHealthboxSpriteIds[battler]].y - SINGLES_Z_TRIGGER_POS_Y_DIFF; + if (gBattleMons[battler].moves[moveSlotIndex] == move && gBattleMons[battler].pp[moveSlotIndex] == 0) + return FALSE; } - 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 + // 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)) { - 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; + return FALSE; } - if (sprite->tHide) + // Check for signature Z-Move or type-based Z-Move. + if (holdEffect == HOLD_EFFECT_Z_CRYSTAL) { - 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; + u16 zMove = GetSignatureZMove(move, gBattleMons[battler].species, item); + if (zMove != MOVE_NONE) + return TRUE; - 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; + if (move != MOVE_NONE && gMovesInfo[move].type == ItemId_GetSecondaryId(item)) + return TRUE; } -} -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; + return FALSE; } -void HideZMoveTriggerSprite(void) +void AssignUsableZMoves(u32 battler, u16 *moves) { - struct Sprite *sprite; - gBattleStruct->zmove.viable = FALSE; - if (gBattleStruct->zmove.triggerSpriteId >= MAX_SPRITES) - return; - sprite = &gSprites[gBattleStruct->zmove.triggerSpriteId]; - sprite->tHide = TRUE; + 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] |= gBitTable[i]; + } } -static void ShowZMoveTriggerSprite(u8 battler) +bool32 TryChangeZIndicator(u32 battler, u32 moveIndex) { - gBattleStruct->zmove.viable = TRUE; - CreateZMoveTriggerSprite(battler, TRUE); -} + bool32 viableZMove = (gBattleStruct->zmove.possibleZMoves[battler] & gBitTable[moveIndex]) != 0; + DebugPrintf("%d: %d", moveIndex, viableZMove); + if (gBattleStruct->zmove.viable && !viableZMove) + HideGimmickTriggerSprite(); // Was a viable z move, now is not -> slide out + else if (!gBattleStruct->zmove.viable && viableZMove) + CreateGimmickTriggerSprite(battler); // Was not a viable z move, now is -> slide back in -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.viable = viableZMove; - gBattleStruct->zmove.triggerSpriteId = 0xFF; + return viableZMove; } static u16 GetSignatureZMove(u16 move, u16 species, u16 item) @@ -375,9 +244,9 @@ static u16 GetSignatureZMove(u16 move, u16 species, u16 item) return MOVE_NONE; } -u16 GetTypeBasedZMove(u16 move, u8 battler) +u32 GetTypeBasedZMove(u32 move, u32 battler) { - u8 moveType = gMovesInfo[move].type; + u32 moveType = gMovesInfo[move].type; if (moveType >= NUMBER_OF_MON_TYPES) moveType = TYPE_MYSTERY; @@ -564,20 +433,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++) @@ -646,17 +515,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,8 +533,6 @@ void SetZEffect(void) gBattlescriptCurrInstr += Z_EFFECT_BS_LENGTH; break; } - - gBattleStruct->zmove.zStatusActive = FALSE; } static bool32 AreStatsMaxed(u8 battler, u8 n) @@ -679,7 +546,7 @@ static bool32 AreStatsMaxed(u8 battler, u8 n) return TRUE; } -u16 GetZMovePower(u16 move) +u32 GetZMovePower(u32 move) { if (gMovesInfo[move].category == DAMAGE_CATEGORY_STATUS) return 0; diff --git a/src/data/gimmicks.h b/src/data/gimmicks.h index d54008ed7e84..838070cd7e98 100644 --- a/src/data/gimmicks.h +++ b/src/data/gimmicks.h @@ -59,6 +59,18 @@ static const struct SpritePalette sSpritePalette_MegaTrigger = sMegaTriggerPal, TAG_GIMMICK_TRIGGER_TILE }; +// Z-Move trigger data +static const u8 ALIGNED(4) sZMoveTriggerGfx[] = INCBIN_U8("graphics/battle_interface/z_move_trigger.4bpp"); +static const u16 sZMoveTriggerPal[] = INCBIN_U16("graphics/battle_interface/z_move_trigger.gbapal"); + +static const struct SpriteSheet sSpriteSheet_ZMoveTrigger = { + sZMoveTriggerGfx, sizeof(sZMoveTriggerGfx), TAG_GIMMICK_TRIGGER_TILE +}; + +static const struct SpritePalette sSpritePalette_ZMoveTrigger = { + sZMoveTriggerPal, TAG_GIMMICK_TRIGGER_PAL +}; + // Ultra Burst trigger data 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"); @@ -114,10 +126,11 @@ const struct GimmickInfo gGimmicksInfo[GIMMICKS_COUNT] = }, [GIMMICK_Z_MOVE] = { - .triggerSheet = &sSpriteSheet_DynamaxTrigger, - .triggerPal = &sSpritePalette_DynamaxTrigger, + .triggerSheet = &sSpriteSheet_ZMoveTrigger, + .triggerPal = &sSpritePalette_ZMoveTrigger, .triggerTemplate = &sSpriteTemplate_GimmickTrigger, - .CanActivate = NULL, + .CanActivate = CanUseZMove, + .ActivateGimmick = ActivateZMove, }, [GIMMICK_ULTRA_BURST] = { diff --git a/src/data/moves_info.h b/src/data/moves_info.h index ed7b80ce95ca..37af976d0f91 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -20268,7 +20268,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, From 45c09290d816cf6d5defe2178971626535ca542d Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Thu, 2 May 2024 10:52:43 -0700 Subject: [PATCH 06/33] added support for z-moves in tests, consolidated gimmick fields --- include/data.h | 2 +- include/test/battle.h | 23 +-- test/battle/ability/parental_bond.c | 10 +- test/battle/ability/prankster.c | 4 +- test/battle/form_change/mega_evolution.c | 16 +- test/battle/form_change/ultra_burst.c | 12 +- test/battle/gimmick/dynamax.c | 198 +++++++++++------------ test/battle/gimmick/terastal.c | 130 +++++++-------- test/battle/move_effect/embargo.c | 2 +- test/battle/move_effect/plasma_fists.c | 4 +- test/battle/move_effect/tera_blast.c | 18 +-- test/test_runner_battle.c | 22 +-- 12 files changed, 208 insertions(+), 233 deletions(-) diff --git a/include/data.h b/include/data.h index 2912003ecfa4..c9fa51202f8b 100644 --- a/include/data.h +++ b/include/data.h @@ -73,7 +73,7 @@ struct TrainerMon u8 dynamaxLevel:4; u8 teraType:5; bool8 gigantamaxFactor:1; - u8 useGimmick:3; + u8 useGimmick:4; }; #define TRAINER_PARTY(partyArray) partyArray, .partySize = ARRAY_COUNT(partyArray) diff --git a/include/test/battle.h b/include/test/battle.h index 8e3a26cd9093..2e28f52b1db2 100644 --- a/include/test/battle.h +++ b/include/test/battle.h @@ -328,12 +328,12 @@ * * MOVE(battler, move | moveSlot:, [megaEvolve:], [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. @@ -929,15 +929,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 diff --git a/test/battle/ability/parental_bond.c b/test/battle/ability/parental_bond.c index 5ff56bcef254..2cd9958f0159 100644 --- a/test/battle/ability/parental_bond.c +++ b/test/battle/ability/parental_bond.c @@ -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); diff --git a/test/battle/ability/prankster.c b/test/battle/ability/prankster.c index d6c5a924629a..8483a6b3c0c9 100644 --- a/test/battle/ability/prankster.c +++ b/test/battle/ability/prankster.c @@ -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); diff --git a/test/battle/form_change/mega_evolution.c b/test/battle/form_change/mega_evolution.c index b2426933f767..4a2c053057b5 100644 --- a/test/battle/form_change/mega_evolution.c +++ b/test/battle/form_change/mega_evolution.c @@ -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); diff --git a/test/battle/form_change/ultra_burst.c b/test/battle/form_change/ultra_burst.c index f2c7b1da2ba6..5c6f4a2ce439 100644 --- a/test/battle/form_change/ultra_burst.c +++ b/test/battle/form_change/ultra_burst.c @@ -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); diff --git a/test/battle/gimmick/dynamax.c b/test/battle/gimmick/dynamax.c index a9ee2fe4d01e..d3f4bf6648d4 100644 --- a/test/battle/gimmick/dynamax.c +++ b/test/battle/gimmick/dynamax.c @@ -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); @@ -199,7 +199,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon can be switched out by Eject But 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 +216,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 +232,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 +248,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 +264,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 +284,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 +300,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 +319,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 +334,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 +351,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 +369,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 +378,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 +404,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 +420,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 +434,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 +455,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 +475,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 +488,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 +506,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, gimmick: GIMMICK_DYNAMAX); } } SCENE { MESSAGE("Wobbuffet used Max Strike!"); HP_BAR(opponent, captureDamage: &results[i].damage); @@ -524,7 +524,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 +540,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 +559,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 +578,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 +597,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 +617,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 +643,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 +669,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 +708,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 +746,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 +762,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 +778,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 +794,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 +812,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 +831,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 +848,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 +863,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 +879,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 +899,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 +929,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 +947,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 +974,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 +1011,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 +1044,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 +1088,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 +1108,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 +1127,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 +1148,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!"); @@ -1167,7 +1167,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); \ @@ -1205,7 +1205,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 { } @@ -1254,7 +1254,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!"); @@ -1279,7 +1279,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 @@ -1302,7 +1302,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); @@ -1322,7 +1322,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); @@ -1343,7 +1343,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 @@ -1371,7 +1371,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); } @@ -1402,7 +1402,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!"); @@ -1424,11 +1424,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!"); @@ -1449,8 +1449,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 { } } @@ -1462,7 +1462,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!"); diff --git a/test/battle/gimmick/terastal.c b/test/battle/gimmick/terastal.c index 18eab992afc5..d0200e5ea219 100644 --- a/test/battle/gimmick/terastal.c +++ b/test/battle/gimmick/terastal.c @@ -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); } @@ -789,6 +789,6 @@ 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); } } } diff --git a/test/battle/move_effect/embargo.c b/test/battle/move_effect/embargo.c index 0d69496efca2..64632065ed30 100644 --- a/test/battle/move_effect/embargo.c +++ b/test/battle/move_effect/embargo.c @@ -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!"); diff --git a/test/battle/move_effect/plasma_fists.c b/test/battle/move_effect/plasma_fists.c index a7b1ac5f6ae8..ec9dd371893e 100644 --- a/test/battle/move_effect/plasma_fists.c +++ b/test/battle/move_effect/plasma_fists.c @@ -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); diff --git a/test/battle/move_effect/tera_blast.c b/test/battle/move_effect/tera_blast.c index 4592cf32a5ee..4e48690ba5b1 100644 --- a/test/battle/move_effect/tera_blast.c +++ b/test/battle/move_effect/tera_blast.c @@ -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); @@ -24,13 +24,13 @@ SINGLE_BATTLE_TEST("Tera Blast changes from Normal-type to the user's 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); @@ -49,7 +49,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); @@ -68,7 +68,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 { @@ -98,7 +98,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); @@ -112,7 +112,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); @@ -127,7 +127,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); diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index bd4169e3ec45..2860c8e99731 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -2067,27 +2067,9 @@ void MoveGetIdAndSlot(s32 battlerId, struct MoveContext *ctx, u32 *moveId, u32 * INVALID("No move or moveSlot"); } - if (ctx->explicitMegaEvolve && ctx->megaEvolve) + if (ctx->explicitGimmick && ctx->gimmick != GIMMICK_NONE) { - DATA.chosenGimmick[battlerId] = GIMMICK_MEGA; - *moveSlot |= RET_GIMMICK; - } - - if (ctx->explicitUltraBurst && ctx->ultraBurst) - { - DATA.chosenGimmick[battlerId] = GIMMICK_ULTRA_BURST; - *moveSlot |= RET_GIMMICK; - } - - if (ctx->explicitDynamax && ctx->dynamax) - { - DATA.chosenGimmick[battlerId] = GIMMICK_DYNAMAX; - *moveSlot |= RET_GIMMICK; - } - - if (ctx->explicitTera && ctx->tera) - { - DATA.chosenGimmick[battlerId] = GIMMICK_TERA; + DATA.chosenGimmick[battlerId] = ctx->gimmick; *moveSlot |= RET_GIMMICK; } } From 01ce4e577d1fc5c0c40a990fd029b172769c9998 Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Thu, 2 May 2024 11:33:02 -0700 Subject: [PATCH 07/33] removed ShouldUseMaxMove and .usingMaxMove --- include/battle.h | 1 - include/battle_dynamax.h | 1 - src/battle_dynamax.c | 9 --------- src/battle_main.c | 6 ++---- src/battle_util.c | 3 +-- 5 files changed, 3 insertions(+), 17 deletions(-) diff --git a/include/battle.h b/include/battle.h index 483592b2456c..bdec77c3e4b0 100644 --- a/include/battle.h +++ b/include/battle.h @@ -572,7 +572,6 @@ struct ZMoveData struct DynamaxData { 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 diff --git a/include/battle_dynamax.h b/include/battle_dynamax.h index 7f823396ba01..b5223f271b4b 100644 --- a/include/battle_dynamax.h +++ b/include/battle_dynamax.h @@ -66,7 +66,6 @@ void UndoDynamax(u32 battler); bool32 IsMoveBlockedByMaxGuard(u32 move); bool32 IsMoveBlockedByDynamax(u32 move); -bool32 ShouldUseMaxMove(u32 battler, u32 baseMove); u16 GetMaxMove(u32 battler, u32 baseMove); u8 GetMaxMovePower(u32 move); bool32 IsMaxMove(u32 move); diff --git a/src/battle_dynamax.c b/src/battle_dynamax.c index 2e5a6a863cfe..2b994dc45f88 100644 --- a/src/battle_dynamax.c +++ b/src/battle_dynamax.c @@ -244,15 +244,6 @@ bool32 IsMoveBlockedByDynamax(u32 move) return FALSE; } -// Returns whether a move should be converted into a Max Move. -bool32 ShouldUseMaxMove(u32 battler, u32 baseMove) -{ - // TODO: Raid bosses do not always use Max Moves. - // if (IsRaidBoss(battler)) - // return !IsRaidBossUsingRegularMove(battler, baseMove); - return GetActiveGimmick(battler) == GIMMICK_DYNAMAX || IsGimmickSelected(battler, GIMMICK_DYNAMAX); -} - static u16 GetTypeBasedMaxMove(u32 battler, u32 type) { // Gigantamax check diff --git a/src/battle_main.c b/src/battle_main.c index d5d043821f0d..c897e9ed5f63 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -4631,7 +4631,6 @@ static void HandleTurnActionSelectionState(void) } gBattleStruct->gimmick.toActivate &= ~(gBitTable[BATTLE_PARTNER(GetBattlerPosition(battler))]); - gBattleStruct->dynamax.usingMaxMove[BATTLE_PARTNER(GetBattlerPosition(battler))] = FALSE; BtlController_EmitEndBounceEffect(battler, BUFFER_A); MarkBattlerForControllerExec(battler); return; @@ -4728,10 +4727,9 @@ static void HandleTurnActionSelectionState(void) 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; } gBattleCommunication[battler]++; @@ -5059,7 +5057,7 @@ s8 GetMovePriority(u32 battler, u16 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 diff --git a/src/battle_util.c b/src/battle_util.c index 94715409d01e..f5fbbf76d11e 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -205,7 +205,7 @@ void HandleAction_UseMove(void) GET_MOVE_TYPE(gChosenMove, moveType); // check max move used - if (gBattleStruct->dynamax.usingMaxMove[gBattlerAttacker]) + if (GetActiveGImmick(gBattlerAttacker) == GIMMICK_DYNAMAX) { gCurrentMove = gChosenMove = GetMaxMove(gBattlerAttacker, gCurrentMove); gBattleStruct->dynamax.activeCategory = gBattleStruct->dynamax.categories[gBattlerAttacker]; @@ -744,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) { From addd03a239ec7b29a2cf06760e1e83ba8b63c6f7 Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Thu, 2 May 2024 11:46:13 -0700 Subject: [PATCH 08/33] renamed TryChangeZIndicator, updated z move display --- include/battle_z_move.h | 2 +- src/battle_controller_player.c | 8 ++++---- src/battle_main.c | 2 +- src/battle_message.c | 7 +++++-- src/battle_util.c | 2 +- src/battle_z_move.c | 7 ++++--- 6 files changed, 16 insertions(+), 12 deletions(-) diff --git a/include/battle_z_move.h b/include/battle_z_move.h index 0350a0f31de2..1f842d04cbb0 100644 --- a/include/battle_z_move.h +++ b/include/battle_z_move.h @@ -18,7 +18,7 @@ bool32 CanUseZMove(u32 battler); u32 GetUsableZMove(u32 battler, u32 move); void ActivateZMove(u32 battler); bool32 IsViableZMove(u32 battler, u32 move); -bool32 TryChangeZIndicator(u32 battler, u32 moveIndex); +bool32 TryChangeZTrigger(u32 battler, u32 moveIndex); u32 GetTypeBasedZMove(u32 move, u32 battler); bool32 MoveSelectionDisplayZMove(u16 zmove, u32 battler); void SetZEffect(void); diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index 9bcdc82c0c61..d274a0481412 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -797,7 +797,7 @@ static void HandleInputChooseMove(u32 battler) MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0); MoveSelectionDisplayPpNumber(battler); MoveSelectionDisplayMoveType(battler); - TryChangeZIndicator(battler, gMoveSelectionCursor[battler]); + TryChangeZTrigger(battler, gMoveSelectionCursor[battler]); } } else if (JOY_NEW(DPAD_RIGHT) && !gBattleStruct->zmove.viewing) @@ -811,7 +811,7 @@ static void HandleInputChooseMove(u32 battler) MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0); MoveSelectionDisplayPpNumber(battler); MoveSelectionDisplayMoveType(battler); - TryChangeZIndicator(battler, gMoveSelectionCursor[battler]); + TryChangeZTrigger(battler, gMoveSelectionCursor[battler]); } } else if (JOY_NEW(DPAD_UP) && !gBattleStruct->zmove.viewing) @@ -824,7 +824,7 @@ static void HandleInputChooseMove(u32 battler) MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0); MoveSelectionDisplayPpNumber(battler); MoveSelectionDisplayMoveType(battler); - TryChangeZIndicator(battler, gMoveSelectionCursor[battler]); + TryChangeZTrigger(battler, gMoveSelectionCursor[battler]); } } else if (JOY_NEW(DPAD_DOWN) && !gBattleStruct->zmove.viewing) @@ -838,7 +838,7 @@ static void HandleInputChooseMove(u32 battler) MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0); MoveSelectionDisplayPpNumber(battler); MoveSelectionDisplayMoveType(battler); - TryChangeZIndicator(battler, gMoveSelectionCursor[battler]); + TryChangeZTrigger(battler, gMoveSelectionCursor[battler]); } } else if (JOY_NEW(SELECT_BUTTON) && !gBattleStruct->zmove.viewing) diff --git a/src/battle_main.c b/src/battle_main.c index c897e9ed5f63..de6e4b4c4c72 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -5057,7 +5057,7 @@ s8 GetMovePriority(u32 battler, u16 move) priority = gMovesInfo[move].priority; // Max Guard check - if (GEtActiveGimmick(battler) == GIMMICK_DYNAMAX && 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 diff --git a/src/battle_message.c b/src/battle_message.c index d63052095364..5cfbe042ee0a 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -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]; diff --git a/src/battle_util.c b/src/battle_util.c index f5fbbf76d11e..579ad147e4c0 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -205,7 +205,7 @@ void HandleAction_UseMove(void) GET_MOVE_TYPE(gChosenMove, moveType); // check max move used - if (GetActiveGImmick(gBattlerAttacker) == GIMMICK_DYNAMAX) + if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_DYNAMAX) { gCurrentMove = gChosenMove = GetMaxMove(gBattlerAttacker, gCurrentMove); gBattleStruct->dynamax.activeCategory = gBattleStruct->dynamax.categories[gBattlerAttacker]; diff --git a/src/battle_z_move.c b/src/battle_z_move.c index 46e1afbf578e..893ba6be31f9 100644 --- a/src/battle_z_move.c +++ b/src/battle_z_move.c @@ -216,7 +216,7 @@ void AssignUsableZMoves(u32 battler, u16 *moves) } } -bool32 TryChangeZIndicator(u32 battler, u32 moveIndex) +bool32 TryChangeZTrigger(u32 battler, u32 moveIndex) { bool32 viableZMove = (gBattleStruct->zmove.possibleZMoves[battler] & gBitTable[moveIndex]) != 0; DebugPrintf("%d: %d", moveIndex, viableZMove); @@ -413,7 +413,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); @@ -423,7 +423,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); } From 1ea39fe6c9509bbd8c6116af5218b99476cd4a77 Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Thu, 2 May 2024 15:42:05 -0700 Subject: [PATCH 09/33] added several z-move tests and fixed various z-move interactions; fixed z-move category calc --- data/battle_scripts_1.s | 3 + include/battle.h | 5 +- include/battle_script_commands.h | 2 +- include/battle_scripts.h | 1 + src/battle_ai_main.c | 2 +- src/battle_ai_util.c | 6 +- src/battle_dynamax.c | 2 - src/battle_main.c | 3 +- src/battle_script_commands.c | 68 ++++-- src/battle_util.c | 21 +- src/battle_z_move.c | 31 ++- test/battle/gimmick/zmove.c | 394 +++++++++++++++++++++++++++++++ 12 files changed, 483 insertions(+), 55 deletions(-) create mode 100644 test/battle/gimmick/zmove.c diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index f1f6c7f803d6..a9ae7c02b4d3 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -6533,6 +6533,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 diff --git a/include/battle.h b/include/battle.h index bdec77c3e4b0..d0663f337077 100644 --- a/include/battle.h +++ b/include/battle.h @@ -572,9 +572,7 @@ struct ZMoveData struct DynamaxData { u8 dynamaxTurns[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; }; @@ -786,6 +784,7 @@ struct BattleStruct u8 quickDrawRandom[MAX_BATTLERS_COUNT]; u8 boosterEnergyActivates; u8 distortedTypeMatchups; + u8 categoryOverride; // for Z-Moves and Max Moves }; // The palaceFlags member of struct BattleStruct contains 1 flag per move to indicate which moves the AI should consider, diff --git a/include/battle_script_commands.h b/include/battle_script_commands.h index fcf69e53dcf3..24b5c6dbacdb 100644 --- a/include/battle_script_commands.h +++ b/include/battle_script_commands.h @@ -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); diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 7ab27aa117e9..7d18362ec4fa 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -83,6 +83,7 @@ extern const u8 BattleScript_DmgHazardsOnTarget[]; 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[]; diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 1e1c3970b137..e8c93ab2643a 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -2061,7 +2061,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)) diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 508518b1d2c2..605f201856e9 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -457,9 +457,6 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes if (gMovesInfo[move].effect == EFFECT_PHOTON_GEYSER) gBattleStruct->swapDamageCategory = (GetCategoryBasedOnStats(gBattlerAttacker) == DAMAGE_CATEGORY_PHYSICAL); - if (gMovesInfo[move].effect == EFFECT_NATURE_POWER) - move = GetNaturePowerMove(); - // 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)) @@ -472,6 +469,9 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes SetActiveGimmick(battlerAtk, gBattleStruct->gimmick.usableGimmick[battlerAtk]); } + if (gMovesInfo[move].effect == EFFECT_NATURE_POWER && GetActiveGimmick(battlerAtk) != GIMMICK_DYNAMAX) + move = GetNaturePowerMove(gBattlerAttacker); + gBattleStruct->dynamicMoveType = 0; SetTypeBeforeUsingMove(move, battlerAtk); diff --git a/src/battle_dynamax.c b/src/battle_dynamax.c index 2b994dc45f88..0a510d827043 100644 --- a/src/battle_dynamax.c +++ b/src/battle_dynamax.c @@ -291,12 +291,10 @@ u16 GetMaxMove(u32 battler, u32 baseMove) else if (gBattleStruct->dynamicMoveType) { move = GetTypeBasedMaxMove(battler, gBattleStruct->dynamicMoveType & DYNAMIC_TYPE_MASK); - gBattleStruct->dynamax.categories[battler] = gMovesInfo[baseMove].category; } else { move = GetTypeBasedMaxMove(battler, gMovesInfo[baseMove].type); - gBattleStruct->dynamax.categories[battler] = gMovesInfo[baseMove].category; } return move; diff --git a/src/battle_main.c b/src/battle_main.c index de6e4b4c4c72..2ad8b0a1c448 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -4729,7 +4729,7 @@ static void HandleTurnActionSelectionState(void) // Max Move check if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX || IsGimmickSelected(battler, GIMMICK_DYNAMAX)) { - gBattleStruct->dynamax.baseMove[battler] = gBattleMons[battler].moves[gBattleStruct->chosenMovePositions[battler]]; + gBattleStruct->dynamax.baseMoves[battler] = gBattleMons[battler].moves[gBattleStruct->chosenMovePositions[battler]]; } gBattleCommunication[battler]++; @@ -6051,6 +6051,7 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk) && gMovesInfo[move].effect != EFFECT_NATURAL_GIFT && !(gMovesInfo[move].effect == EFFECT_TERA_BLAST && GetActiveGimmick(battlerAtk) == GIMMICK_TERA) && !(gMovesInfo[move].effect == EFFECT_TERA_STARSTORM && gBattleMons[battlerAtk].species == SPECIES_TERAPAGOS_STELLAR) + && !IsZMove(move) && ((attackerAbility == ABILITY_PIXILATE && (ateType = TYPE_FAIRY)) || (attackerAbility == ABILITY_REFRIGERATE && (ateType = TYPE_ICE)) || (attackerAbility == ABILITY_AERILATE && (ateType = TYPE_FLYING)) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 6020a85c3ff1..35cf8f3f11f2 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -5841,7 +5841,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]) @@ -9663,7 +9663,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, gBattlerAttacker); + } + else + { + gCalledMove = move; + } gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED; gBattlerTarget = GetMoveTarget(gCalledMove, NO_TARGET_OVERRIDE); gStatuses3[gBattlerAttacker] |= STATUS3_ME_FIRST; @@ -9796,7 +9804,8 @@ 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 - || (GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX)) + || (GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX) + || IsZMove(move) || IsMaxMove(move)) { gBattlescriptCurrInstr = cmd->failInstr; } @@ -11073,7 +11082,7 @@ 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 (GetActiveGimmick(gBattlerAttacker) && !IS_MOVE_STATUS(move)) + if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !IS_MOVE_STATUS(move)) { gBattleStruct->zmove.baseMoves[gBattlerAttacker] = move; gCurrentMove = GetTypeBasedZMove(move, gBattlerAttacker); @@ -12814,7 +12823,7 @@ static void Cmd_trysetencore(void) { 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; } } @@ -13028,7 +13037,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], gBattlerAttacker); + } + else + { + gCalledMove = gBattleMons[gBattlerAttacker].moves[movePosition]; + } gCurrMovePos = movePosition; gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED; gBattlerTarget = GetMoveTarget(gCalledMove, NO_TARGET_OVERRIDE); @@ -13097,7 +13114,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; } } @@ -13947,25 +13964,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, gBattlerAttacker); + } + + return move; } // Refresh @@ -16681,16 +16706,25 @@ void BS_TryCopycat(void) { NATIVE_ARGS(const u8 *failInstr); - if (gLastUsedMove == MOVE_NONE || gLastUsedMove == MOVE_UNAVAILABLE || gMovesInfo[gLastUsedMove].copycatBanned) + if (gLastUsedMove == MOVE_NONE || gLastUsedMove == MOVE_UNAVAILABLE || gMovesInfo[gLastUsedMove].copycatBanned || IsZMove(gLastUsedMove)) { gBattlescriptCurrInstr = cmd->failInstr; } else { - if (IsMaxMove(gLastUsedMove)) + if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !IS_MOVE_STATUS(gLastUsedMove)) + { + gBattleStruct->zmove.baseMoves[gBattlerAttacker] = gLastUsedMove; + gCalledMove = GetTypeBasedZMove(gLastUsedMove, gBattlerAttacker); + } + else if (IsMaxMove(gLastUsedMove)) + { gCalledMove = gBattleStruct->dynamax.lastUsedBaseMove; + } else + { gCalledMove = gLastUsedMove; + } gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED; gBattlerTarget = GetMoveTarget(gCalledMove, NO_TARGET_OVERRIDE); diff --git a/src/battle_util.c b/src/battle_util.c index 579ad147e4c0..e98c019e8431 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -186,12 +186,6 @@ void HandleAction_UseMove(void) gCurrentMove = gChosenMove = gBattleMons[gBattlerAttacker].moves[gCurrMovePos]; } - // check z move used - if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !IS_MOVE_STATUS(gCurrentMove) && !IsZMove(gCurrentMove)) - { - gCurrentMove = GetUsableZMove(gBattlerAttacker, gCurrentMove); - } - if (gBattleMons[gBattlerAttacker].hp != 0) { if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) @@ -204,11 +198,18 @@ void HandleAction_UseMove(void) SetTypeBeforeUsingMove(gChosenMove, gBattlerAttacker); GET_MOVE_TYPE(gChosenMove, moveType); + // 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 if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_DYNAMAX) { gCurrentMove = gChosenMove = GetMaxMove(gBattlerAttacker, gCurrentMove); - gBattleStruct->dynamax.activeCategory = gBattleStruct->dynamax.categories[gBattlerAttacker]; + gBattleStruct->categoryOverride = gMovesInfo[gCurrentMove].category; } moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove); @@ -10828,9 +10829,9 @@ bool32 ShouldGetStatBadgeBoost(u16 badgeFlag, u32 battler) u8 GetBattleMoveCategory(u32 moveId) { - 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 && (IsZMove(moveId) || IsMaxMove(moveId))) // TODO: Might be buggy depending on when this is called. + return gBattleStruct->categoryOverride; + if (gBattleStruct != NULL && gBattleStruct->swapDamageCategory) // Photon Geyser, Shell Side Arm, Light That Burns the Sky, Tera Blast return DAMAGE_CATEGORY_PHYSICAL; if (B_PHYSICAL_SPECIAL_SPLIT >= GEN_4) return gMovesInfo[moveId].category; diff --git a/src/battle_z_move.c b/src/battle_z_move.c index 893ba6be31f9..d1fc5e07ffa2 100644 --- a/src/battle_z_move.c +++ b/src/battle_z_move.c @@ -48,7 +48,6 @@ static u16 GetSignatureZMove(u16 move, u16 species, u16 item); static void ZMoveSelectionDisplayPpNumber(u32 battler); static void ZMoveSelectionDisplayPower(u16 move, u16 zMove); -static bool32 AreStatsMaxed(u8 battler, u8 n); static void ZMoveSelectionDisplayMoveType(u16 zMove, u32 battler); // Const Data @@ -251,6 +250,10 @@ u32 GetTypeBasedZMove(u32 move, u32 battler) 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; @@ -460,22 +463,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)) { @@ -536,17 +544,6 @@ void SetZEffect(void) } } -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; -} - u32 GetZMovePower(u32 move) { if (gMovesInfo[move].category == DAMAGE_CATEGORY_STATUS) diff --git a/test/battle/gimmick/zmove.c b/test/battle/gimmick/zmove.c new file mode 100644 index 000000000000..094080b95aa9 --- /dev/null +++ b/test/battle/gimmick/zmove.c @@ -0,0 +1,394 @@ +#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 { + 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 { + PLAYER(SPECIES_WOBBUFFET) { 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 deal 1/4 damage through protect", s16 damage) +{ + bool32 protected; + PARAMETRIZE { protected = TRUE; } + PARAMETRIZE { protected = FALSE; } + GIVEN { + 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].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].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].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].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].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].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].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].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].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 { + 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") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMALIUM_Z); } + 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 { + 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 { + 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); } + } 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 { + 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_DESTINY_BOND].zMove.effect == Z_EFFECT_FOLLOW_ME); + 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_DESTINY_BOND].zMove.effect == Z_EFFECT_FOLLOW_ME); + 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); + } +} From 6c30bae205184588e11fdad49a4b6ecfe8b08d9e Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Thu, 2 May 2024 15:44:38 -0700 Subject: [PATCH 10/33] fixed useless battler arg in GetTypeBasedZMove --- include/battle_z_move.h | 2 +- src/battle_script_commands.c | 10 +++++----- src/battle_z_move.c | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/battle_z_move.h b/include/battle_z_move.h index 1f842d04cbb0..963d32fb1e6b 100644 --- a/include/battle_z_move.h +++ b/include/battle_z_move.h @@ -19,7 +19,7 @@ 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 battler); +u32 GetTypeBasedZMove(u32 move); bool32 MoveSelectionDisplayZMove(u16 zmove, u32 battler); void SetZEffect(void); void AssignUsableZMoves(u32 battler, u16 *moves); diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 35cf8f3f11f2..a21e00920782 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -9666,7 +9666,7 @@ static void Cmd_various(void) if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !IS_MOVE_STATUS(move)) { gBattleStruct->zmove.baseMoves[gBattlerAttacker] = move; - gCalledMove = GetTypeBasedZMove(move, gBattlerAttacker); + gCalledMove = GetTypeBasedZMove(move); } else { @@ -11085,7 +11085,7 @@ static void SetMoveForMirrorMove(u32 move) if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !IS_MOVE_STATUS(move)) { gBattleStruct->zmove.baseMoves[gBattlerAttacker] = move; - gCurrentMove = GetTypeBasedZMove(move, gBattlerAttacker); + gCurrentMove = GetTypeBasedZMove(move); } else { @@ -13040,7 +13040,7 @@ static void Cmd_trychoosesleeptalkmove(void) 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], gBattlerAttacker); + gCalledMove = GetTypeBasedZMove(gBattleMons[gBattlerAttacker].moves[movePosition]); } else { @@ -13987,7 +13987,7 @@ u32 GetNaturePowerMove(u32 battler) if (GetActiveGimmick(battler) == GIMMICK_Z_MOVE) { gBattleStruct->zmove.baseMoves[gBattlerAttacker] = move; - move = GetTypeBasedZMove(move, gBattlerAttacker); + move = GetTypeBasedZMove(move); } return move; @@ -16715,7 +16715,7 @@ void BS_TryCopycat(void) if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !IS_MOVE_STATUS(gLastUsedMove)) { gBattleStruct->zmove.baseMoves[gBattlerAttacker] = gLastUsedMove; - gCalledMove = GetTypeBasedZMove(gLastUsedMove, gBattlerAttacker); + gCalledMove = GetTypeBasedZMove(gLastUsedMove); } else if (IsMaxMove(gLastUsedMove)) { diff --git a/src/battle_z_move.c b/src/battle_z_move.c index d1fc5e07ffa2..eb8892d20e4b 100644 --- a/src/battle_z_move.c +++ b/src/battle_z_move.c @@ -154,7 +154,7 @@ u32 GetUsableZMove(u32 battler, u32 move) return zMove; // Signature z move exists if (move != MOVE_NONE && zMove != MOVE_Z_STATUS && gMovesInfo[move].type == ItemId_GetSecondaryId(item)) - return GetTypeBasedZMove(move, battler); + return GetTypeBasedZMove(move); } return MOVE_NONE; @@ -243,7 +243,7 @@ static u16 GetSignatureZMove(u16 move, u16 species, u16 item) return MOVE_NONE; } -u32 GetTypeBasedZMove(u32 move, u32 battler) +u32 GetTypeBasedZMove(u32 move) { u32 moveType = gMovesInfo[move].type; From 0c2a48e4a0c02211047a9cc001d2d14e9e92072c Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Thu, 2 May 2024 18:51:28 -0700 Subject: [PATCH 11/33] added basic test check for bad Z-Move or Mega usage --- include/battle_z_move.h | 1 + src/battle_z_move.c | 3 +-- test/battle/gimmick/zmove.c | 1 + test/test_runner_battle.c | 11 ++++++++++- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/include/battle_z_move.h b/include/battle_z_move.h index 963d32fb1e6b..bfa4c8495af3 100644 --- a/include/battle_z_move.h +++ b/include/battle_z_move.h @@ -20,6 +20,7 @@ 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); void AssignUsableZMoves(u32 battler, u16 *moves); diff --git a/src/battle_z_move.c b/src/battle_z_move.c index eb8892d20e4b..18dab005817b 100644 --- a/src/battle_z_move.c +++ b/src/battle_z_move.c @@ -45,7 +45,6 @@ #define STAT_STAGE(battler, stat) (gBattleMons[battler].statStages[stat - 1]) // Function Declarations -static u16 GetSignatureZMove(u16 move, u16 species, u16 item); static void ZMoveSelectionDisplayPpNumber(u32 battler); static void ZMoveSelectionDisplayPower(u16 move, u16 zMove); static void ZMoveSelectionDisplayMoveType(u16 zMove, u32 battler); @@ -229,7 +228,7 @@ bool32 TryChangeZTrigger(u32 battler, u32 moveIndex) return viableZMove; } -static u16 GetSignatureZMove(u16 move, u16 species, u16 item) +u32 GetSignatureZMove(u32 move, u32 species, u32 item) { u32 i; diff --git a/test/battle/gimmick/zmove.c b/test/battle/gimmick/zmove.c index 094080b95aa9..992046ccb549 100644 --- a/test/battle/gimmick/zmove.c +++ b/test/battle/gimmick/zmove.c @@ -333,6 +333,7 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Z-Sleep Talk transforms a used non-status move into OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(player, MOVE_SLEEP_TALK, gimmick: GIMMICK_Z_MOVE); } + TURN { MOVE(player, MOVE_SLEEP_TALK, gimmick: GIMMICK_Z_MOVE); } } SCENE { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player); ANIMATION(ANIM_TYPE_MOVE, MOVE_BLOOM_DOOM, player); diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 2860c8e99731..2b9469a94b11 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -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" @@ -2069,8 +2071,15 @@ void MoveGetIdAndSlot(s32 battlerId, struct MoveContext *ctx, u32 *moveId, u32 * if (ctx->explicitGimmick && ctx->gimmick != GIMMICK_NONE) { + // Do some very simple screening for bad usage. + u32 item = GetMonData(mon, MON_DATA_HELD_ITEM); + u32 species = GetMonData(mon, MON_DATA_SPECIES); + INVALID_IF(ctx->gimmick == GIMMICK_MEGA && ItemId_GetHoldEffect(item) != HOLD_EFFECT_MEGA_STONE, "Cannot Mega Evolve without a Mega Stone"); + INVALID_IF(ctx->gimmick == GIMMICK_Z_MOVE && ItemId_GetHoldEffect(item) != 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, "Cannot turn %S into a Z-Move with %S", GetMoveName(ctx->move), ItemId_GetName(item)); + DATA.chosenGimmick[battlerId] = ctx->gimmick; - *moveSlot |= RET_GIMMICK; + *moveSlot |= RET_GIMMICK; } } From 02de34332949f75de6b36a3a21d162f8ed6569d1 Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Thu, 2 May 2024 20:47:43 -0700 Subject: [PATCH 12/33] reworked test runner gimmick functionality; added support for Ultra Burst + Z-Move to test Light That Burns the Sky --- include/config.h | 2 +- include/test/battle.h | 2 +- include/test_runner.h | 2 +- src/battle_dynamax.c | 7 ++-- src/battle_gimmick.c | 24 ++++--------- src/battle_main.c | 2 +- src/battle_terastal.c | 11 ++---- src/battle_util.c | 69 ++++++++++--------------------------- src/battle_z_move.c | 4 +-- test/battle/gimmick/zmove.c | 24 +++++++++++++ test/test_runner_battle.c | 13 ++++--- 11 files changed, 67 insertions(+), 93 deletions(-) diff --git a/include/config.h b/include/config.h index 0f1b64bd13fe..46aace77b7e9 100644 --- a/include/config.h +++ b/include/config.h @@ -6,7 +6,7 @@ // still has them in the ROM. This is because the developers forgot // to define NDEBUG before release, however this has been changed as // Ruby's actual debug build does not use the AGBPrint features. -#define NDEBUG +// #define NDEBUG // To enable printf debugging, comment out "#define NDEBUG". This allows // the various AGBPrint functions to be used. (See include/gba/isagbprint.h). diff --git a/include/test/battle.h b/include/test/battle.h index 2e28f52b1db2..caccffeec3f4 100644 --- a/include/test/battle.h +++ b/include/test/battle.h @@ -662,7 +662,7 @@ struct BattleTestData u8 gender; u8 nature; u16 forcedAbilities[NUM_BATTLE_SIDES][PARTY_SIZE]; - u8 chosenGimmick[MAX_BATTLERS_COUNT]; + u8 chosenGimmick[NUM_BATTLE_SIDES][PARTY_SIZE]; u8 currentMonIndexes[MAX_BATTLERS_COUNT]; u8 turnState; diff --git a/include/test_runner.h b/include/test_runner.h index b22a7fece60f..f61f26b66ea0 100644 --- a/include/test_runner.h +++ b/include/test_runner.h @@ -24,7 +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 battler); +u32 TestRunner_Battle_GetChosenGimmick(u32 side, u32 partyIndex); #else diff --git a/src/battle_dynamax.c b/src/battle_dynamax.c index 0a510d827043..9ec10f3a0351 100644 --- a/src/battle_dynamax.c +++ b/src/battle_dynamax.c @@ -74,16 +74,15 @@ static const struct GMaxMove sGMaxMoveTable[] = bool32 CanDynamax(u32 battler) { u16 species = gBattleMons[battler].species; - u16 holdEffect = ItemId_GetHoldEffect(gBattleMons[battler].item); + u16 holdEffect = GetBattlerHoldEffect(battler, FALSE); // Check if Dynamax battle flag is set. This needs to be defined in include/config/battle.h - if (B_FLAG_DYNAMAX_BATTLE == 0 || (B_FLAG_DYNAMAX_BATTLE != 0 && !FlagGet(B_FLAG_DYNAMAX_BATTLE))) + if ((B_FLAG_DYNAMAX_BATTLE == 0 || (B_FLAG_DYNAMAX_BATTLE != 0 && !FlagGet(B_FLAG_DYNAMAX_BATTLE))) && !TESTING) return FALSE; - // Check if Player has a Dynamax Band. if ((GetBattlerPosition(battler) == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && GetBattlerPosition(battler) == B_POSITION_PLAYER_RIGHT)) - && !CheckBagHasItem(ITEM_DYNAMAX_BAND, 1)) + && !CheckBagHasItem(ITEM_DYNAMAX_BAND, 1) && !TESTING) return FALSE; // Check if species isn't allowed to Dynamax. diff --git a/src/battle_gimmick.c b/src/battle_gimmick.c index 0421bb1ccd45..9be6b6568045 100644 --- a/src/battle_gimmick.c +++ b/src/battle_gimmick.c @@ -6,6 +6,7 @@ #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" @@ -21,16 +22,6 @@ static void SpriteCb_GimmickTrigger(struct Sprite *sprite); void AssignUsableGimmicks(void) { u32 battler, gimmick; - #if TESTING - for (battler = 0; battler < gBattlersCount; ++battler) - { - gimmick = TestRunner_Battle_GetChosenGimmick(battler); - if (GetActiveGimmick(battler) == GIMMICK_NONE) - gBattleStruct->gimmick.usableGimmick[battler] = gimmick; - else - gBattleStruct->gimmick.usableGimmick[battler] = GIMMICK_NONE; - } - #else for (battler = 0; battler < gBattlersCount; ++battler) { gBattleStruct->gimmick.usableGimmick[battler] = GIMMICK_NONE; @@ -43,7 +34,6 @@ void AssignUsableGimmicks(void) } } } - #endif } // Returns whether a battler is able to use a gimmick. Checks consumption and gimmick specific functions. @@ -56,10 +46,8 @@ bool32 CanActivateGimmick(u32 battler, enum Gimmick gimmick) bool32 IsGimmickSelected(u32 battler, enum Gimmick gimmick) { #if TESTING - return (GetActiveGimmick(battler) == GIMMICK_NONE - && !HasTrainerUsedGimmick(battler, gimmick) - && (gBattleStruct->gimmick.toActivate & gBitTable[battler]) - && gBattleStruct->gimmick.usableGimmick[battler] == gimmick); + // There's no player select in tests, but some gimmicks need to test choice before they are fully activated. + return (gBattleStruct->gimmick.toActivate & gBitTable[battler]) && gBattleStruct->gimmick.usableGimmick[battler] == gimmick; #else return gBattleStruct->gimmick.usableGimmick[battler] == gimmick && gBattleStruct->gimmick.playerSelect; #endif @@ -80,10 +68,10 @@ enum Gimmick GetActiveGimmick(u32 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) { - if (GetBattlerSide(battler) == B_SIDE_PLAYER) - { + if (TESTING) + return gimmick == TestRunner_Battle_GetChosenGimmick(GetBattlerSide(battler), gBattlerPartyIndexes[battler]); + else if (GetBattlerSide(battler) == B_SIDE_PLAYER) return TRUE; // the player can do whatever they want - } else { bool32 isSecondTrainer = (GetBattlerPosition(battler) == B_POSITION_OPPONENT_RIGHT) && (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) && !BATTLE_TWO_VS_ONE_OPPONENT; diff --git a/src/battle_main.c b/src/battle_main.c index 2ad8b0a1c448..4184a0d840da 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -6051,7 +6051,7 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk) && gMovesInfo[move].effect != EFFECT_NATURAL_GIFT && !(gMovesInfo[move].effect == EFFECT_TERA_BLAST && GetActiveGimmick(battlerAtk) == GIMMICK_TERA) && !(gMovesInfo[move].effect == EFFECT_TERA_STARSTORM && gBattleMons[battlerAtk].species == SPECIES_TERAPAGOS_STELLAR) - && !IsZMove(move) + && GetActiveGimmick(battlerAtk) != GIMMICK_Z_MOVE && ((attackerAbility == ABILITY_PIXILATE && (ateType = TYPE_FAIRY)) || (attackerAbility == ABILITY_REFRIGERATE && (ateType = TYPE_ICE)) || (attackerAbility == ABILITY_AERILATE && (ateType = TYPE_FLYING)) diff --git a/src/battle_terastal.c b/src/battle_terastal.c index d1b8ea47d023..5944324e6fe8 100644 --- a/src/battle_terastal.c +++ b/src/battle_terastal.c @@ -56,34 +56,27 @@ bool32 CanTerastallize(u32 battler) if (B_FLAG_TERA_ORB_CHARGED != 0 && (battler == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && battler == B_POSITION_PLAYER_RIGHT)) && !(CheckBagHasItem(ITEM_TERA_ORB, 1) - && FlagGet(B_FLAG_TERA_ORB_CHARGED))) + && FlagGet(B_FLAG_TERA_ORB_CHARGED)) + && !TESTING) { return FALSE; } // Check if Trainer has already Terastallized. if (HasTrainerUsedGimmick(battler, GIMMICK_TERA)) - { return FALSE; - } // 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) - { return FALSE; - } // Every check passed! return TRUE; diff --git a/src/battle_util.c b/src/battle_util.c index e98c019e8431..8b0e7727c664 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -8275,6 +8275,13 @@ u32 GetBattlerHoldEffectIgnoreAbility(u32 battler, bool32 checkNegating) u32 GetBattlerHoldEffectInternal(u32 battler, bool32 checkNegating, bool32 checkAbility) { + if (TESTING) + { + struct Pokemon *mon = GetBattlerParty(battler); + u32 item = GetMonData(mon, MON_DATA_HELD_ITEM); + return ItemId_GetHoldEffect(item); + } + if (checkNegating) { if (gStatuses3[battler] & STATUS3_EMBARGO) @@ -10389,12 +10396,11 @@ bool32 DoesSpeciesUseHoldItemToChangeForm(u16 species, u16 heldItemId) bool32 CanMegaEvolve(u32 battler) { - u32 itemId, holdEffect; - struct Pokemon *mon; + 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)) + && !CheckBagHasItem(ITEM_MEGA_RING, 1) && !TESTING) return FALSE; // Check if Trainer has already Mega Evolved. @@ -10409,36 +10415,17 @@ bool32 CanMegaEvolve(u32 battler) if (gStatuses3[battler] & STATUS3_SKY_DROPPED) return FALSE; - // Gets battler data. - if (GetBattlerSide(battler) == B_SIDE_OPPONENT) - mon = &gEnemyParty[gBattlerPartyIndexes[battler]]; - else - mon = &gPlayerParty[gBattlerPartyIndexes[battler]]; - - itemId = GetMonData(mon, MON_DATA_HELD_ITEM); - - if (itemId == ITEM_ENIGMA_BERRY_E_READER) - holdEffect = gEnigmaBerries[battler].holdEffect; - else - holdEffect = ItemId_GetHoldEffect(itemId); - // Check if battler is holding a Z-Crystal. if (holdEffect == HOLD_EFFECT_Z_CRYSTAL) return FALSE; - // Check if there is an entry in the form change 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 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; // 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) - { return TRUE; - } // No checks passed, the mon CAN'T mega evolve. return FALSE; @@ -10446,12 +10433,11 @@ bool32 CanMegaEvolve(u32 battler) bool32 CanUltraBurst(u32 battler) { - u32 itemId, holdEffect; - struct Pokemon *mon; + 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)) + && !CheckBagHasItem(ITEM_Z_POWER_RING, 1) && !TESTING) return FALSE; // Check if Trainer has already Ultra Bursted. @@ -10466,26 +10452,9 @@ bool32 CanUltraBurst(u32 battler) 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 form change 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; @@ -10829,10 +10798,10 @@ bool32 ShouldGetStatBadgeBoost(u16 badgeFlag, u32 battler) u8 GetBattleMoveCategory(u32 moveId) { - if (gBattleStruct != NULL && (IsZMove(moveId) || IsMaxMove(moveId))) // TODO: Might be buggy depending on when this is called. - return gBattleStruct->categoryOverride; 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; diff --git a/src/battle_z_move.c b/src/battle_z_move.c index 18dab005817b..54a1e137d128 100644 --- a/src/battle_z_move.c +++ b/src/battle_z_move.c @@ -116,10 +116,8 @@ bool32 CanUseZMove(u32 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)) - { + && !CheckBagHasItem(ITEM_Z_POWER_RING, 1) && !TESTING) return FALSE; - } // Add '| BATTLE_TYPE_FRONTIER' to below if issues occur if (gBattleTypeFlags & (BATTLE_TYPE_SAFARI | BATTLE_TYPE_WALLY_TUTORIAL)) diff --git a/test/battle/gimmick/zmove.c b/test/battle/gimmick/zmove.c index 992046ccb549..d0bd7896c4f5 100644 --- a/test/battle/gimmick/zmove.c +++ b/test/battle/gimmick/zmove.c @@ -393,3 +393,27 @@ DOUBLE_BATTLE_TEST("(Z-MOVE) Dancer does not use a Z-Move if the battler has use 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 { + 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); + } +} diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 2b9469a94b11..a08fac3575b2 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -2076,9 +2076,12 @@ void MoveGetIdAndSlot(s32 battlerId, struct MoveContext *ctx, u32 *moveId, u32 * u32 species = GetMonData(mon, MON_DATA_SPECIES); INVALID_IF(ctx->gimmick == GIMMICK_MEGA && ItemId_GetHoldEffect(item) != HOLD_EFFECT_MEGA_STONE, "Cannot Mega Evolve without a Mega Stone"); INVALID_IF(ctx->gimmick == GIMMICK_Z_MOVE && ItemId_GetHoldEffect(item) != 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, "Cannot turn %S into a Z-Move with %S", GetMoveName(ctx->move), ItemId_GetName(item)); - - DATA.chosenGimmick[battlerId] = ctx->gimmick; + 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)); + + DATA.chosenGimmick[GetBattlerSide(battlerId)][gBattlerPartyIndexes[battlerId]] = ctx->gimmick; *moveSlot |= RET_GIMMICK; } } @@ -2644,9 +2647,9 @@ u32 TestRunner_Battle_GetForcedAbility(u32 side, u32 partyIndex) return DATA.forcedAbilities[side][partyIndex]; } -u32 TestRunner_Battle_GetChosenGimmick(u32 battler) +u32 TestRunner_Battle_GetChosenGimmick(u32 side, u32 partyIndex) { - return DATA.chosenGimmick[battler]; + return DATA.chosenGimmick[side][partyIndex]; } // TODO: Consider storing the last successful i and searching from i+1 From dc25f84b6be5a14685535ba7d43ca755aa9b7c0e Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Fri, 17 May 2024 22:05:00 -0700 Subject: [PATCH 13/33] fixed gimmick test logic; fixed damage category override --- src/battle_controller_opponent.c | 3 ++- src/battle_dynamax.c | 2 +- src/battle_gimmick.c | 21 ++++++++++++++------- src/battle_main.c | 2 ++ src/battle_script_commands.c | 1 + src/battle_terastal.c | 2 +- src/battle_util.c | 9 +-------- test/battle/gimmick/dynamax.c | 1 + test/test_runner_battle.c | 11 +++++++---- 9 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index 7c504f08ac3a..404ffc0b65ab 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -552,10 +552,11 @@ static void OpponentHandleChooseMove(u32 battler) if (gAbsentBattlerFlags & gBitTable[gBattlerTarget]) gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); } - // If opponent can and should use a gimmick (considering trainer data), do it if (gBattleStruct->gimmick.usableGimmick[battler] != GIMMICK_NONE) + { BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_GIMMICK) | (gBattlerTarget << 8)); + } else BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (gBattlerTarget << 8)); } diff --git a/src/battle_dynamax.c b/src/battle_dynamax.c index 9ec10f3a0351..dc0a5752f760 100644 --- a/src/battle_dynamax.c +++ b/src/battle_dynamax.c @@ -104,7 +104,7 @@ bool32 CanDynamax(u32 battler) return FALSE; // Check if battler is holding a Z-Crystal or Mega Stone. - if (holdEffect == HOLD_EFFECT_Z_CRYSTAL || holdEffect == HOLD_EFFECT_MEGA_STONE) + if ((holdEffect == HOLD_EFFECT_Z_CRYSTAL || holdEffect == HOLD_EFFECT_MEGA_STONE) && !TESTING) return FALSE; // TODO: Cannot Dynamax in a Max Raid if you don't have Dynamax Energy. diff --git a/src/battle_gimmick.c b/src/battle_gimmick.c index 9be6b6568045..916d1cbea50f 100644 --- a/src/battle_gimmick.c +++ b/src/battle_gimmick.c @@ -45,12 +45,11 @@ bool32 CanActivateGimmick(u32 battler, enum Gimmick gimmick) // Returns whether the player has a gimmick selected while in the move selection menu. bool32 IsGimmickSelected(u32 battler, enum Gimmick gimmick) { - #if TESTING // There's no player select in tests, but some gimmicks need to test choice before they are fully activated. - return (gBattleStruct->gimmick.toActivate & gBitTable[battler]) && gBattleStruct->gimmick.usableGimmick[battler] == gimmick; - #else - return gBattleStruct->gimmick.usableGimmick[battler] == gimmick && gBattleStruct->gimmick.playerSelect; - #endif + 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. @@ -68,10 +67,18 @@ enum Gimmick GetActiveGimmick(u32 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]); - else if (GetBattlerSide(battler) == B_SIDE_PLAYER) - return TRUE; // the player can do whatever they want + } + // 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; diff --git a/src/battle_main.c b/src/battle_main.c index d8569488b456..6a16d8966130 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3414,6 +3414,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 } diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index f8906ae6eec6..747eb0531d23 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -6384,6 +6384,7 @@ static void Cmd_moveend(void) gBattleStruct->hitSwitchTargetFailed = FALSE; gBattleStruct->isAtkCancelerForCalledMove = FALSE; gBattleStruct->swapDamageCategory = FALSE; + gBattleStruct->categoryOverride = FALSE; gBattleStruct->bouncedMoveIsUsed = FALSE; gBattleStruct->enduredDamage = 0; gBattleStruct->additionalEffectsCounter = 0; diff --git a/src/battle_terastal.c b/src/battle_terastal.c index 34c87d7cbe57..6752be457481 100644 --- a/src/battle_terastal.c +++ b/src/battle_terastal.c @@ -83,7 +83,7 @@ bool32 CanTerastallize(u32 battler) return FALSE; // Check if battler is holding a Z-Crystal or Mega Stone. - if (holdEffect == HOLD_EFFECT_Z_CRYSTAL || holdEffect == HOLD_EFFECT_MEGA_STONE) + if ((holdEffect == HOLD_EFFECT_Z_CRYSTAL || holdEffect == HOLD_EFFECT_MEGA_STONE) && !TESTING) return FALSE; // Every check passed! diff --git a/src/battle_util.c b/src/battle_util.c index 726419b198ee..353c7344d628 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -208,8 +208,8 @@ void HandleAction_UseMove(void) // check max move used if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_DYNAMAX) { - gCurrentMove = gChosenMove = GetMaxMove(gBattlerAttacker, gCurrentMove); gBattleStruct->categoryOverride = gMovesInfo[gCurrentMove].category; + gCurrentMove = gChosenMove = GetMaxMove(gBattlerAttacker, gCurrentMove); } moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove); @@ -8289,13 +8289,6 @@ u32 GetBattlerHoldEffectIgnoreAbility(u32 battler, bool32 checkNegating) u32 GetBattlerHoldEffectInternal(u32 battler, bool32 checkNegating, bool32 checkAbility) { - if (TESTING) - { - struct Pokemon *mon = GetBattlerParty(battler); - u32 item = GetMonData(mon, MON_DATA_HELD_ITEM); - return ItemId_GetHoldEffect(item); - } - if (checkNegating) { if (gStatuses3[battler] & STATUS3_EMBARGO) diff --git a/test/battle/gimmick/dynamax.c b/test/battle/gimmick/dynamax.c index d3f4bf6648d4..02c34caccc07 100644 --- a/test/battle/gimmick/dynamax.c +++ b/test/battle/gimmick/dynamax.c @@ -195,6 +195,7 @@ 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); diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index b7e63acd753e..35039d50ee50 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -2063,17 +2063,20 @@ void MoveGetIdAndSlot(s32 battlerId, struct MoveContext *ctx, u32 *moveId, u32 * if (ctx->explicitGimmick && ctx->gimmick != GIMMICK_NONE) { - // Do some very simple screening for bad usage. + // Screen for bad item usage as the usual checks do not function before the battle begins. u32 item = GetMonData(mon, MON_DATA_HELD_ITEM); + u32 holdEffect = ItemId_GetHoldEffect(item); u32 species = GetMonData(mon, MON_DATA_SPECIES); - INVALID_IF(ctx->gimmick == GIMMICK_MEGA && ItemId_GetHoldEffect(item) != HOLD_EFFECT_MEGA_STONE, "Cannot Mega Evolve without a Mega Stone"); - INVALID_IF(ctx->gimmick == GIMMICK_Z_MOVE && ItemId_GetHoldEffect(item) != HOLD_EFFECT_Z_CRYSTAL, "Cannot use a Z-Move without a Z-Crystal"); + INVALID_IF(ctx->gimmick == GIMMICK_MEGA && holdEffect != HOLD_EFFECT_MEGA_STONE, "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"); - DATA.chosenGimmick[GetBattlerSide(battlerId)][gBattlerPartyIndexes[battlerId]] = ctx->gimmick; + DATA.chosenGimmick[GetBattlerSide(battlerId)][DATA.currentMonIndexes[battlerId]] = ctx->gimmick; *moveSlot |= RET_GIMMICK; } } From f3301df73c41c211c9eedaafbbe5ed00cc0c9448 Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Fri, 17 May 2024 22:18:58 -0700 Subject: [PATCH 14/33] fixed mega rayquaza test fail --- test/test_runner_battle.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 35039d50ee50..39eb049e8b7f 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -2067,7 +2067,7 @@ void MoveGetIdAndSlot(s32 battlerId, struct MoveContext *ctx, u32 *moveId, u32 * u32 item = GetMonData(mon, MON_DATA_HELD_ITEM); u32 holdEffect = ItemId_GetHoldEffect(item); u32 species = GetMonData(mon, MON_DATA_SPECIES); - INVALID_IF(ctx->gimmick == GIMMICK_MEGA && holdEffect != HOLD_EFFECT_MEGA_STONE, "Cannot Mega Evolve without a Mega Stone"); + 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 From d197aa780a0889bf22ab19e06a004d1e2964c066 Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Mon, 20 May 2024 12:49:55 -0700 Subject: [PATCH 15/33] consolidated gimmick indicator logic; added graphics to gGimmicksInfo --- include/battle.h | 1 + include/battle_gimmick.h | 11 + include/battle_interface.h | 2 - include/battle_terastal.h | 10 - src/battle_anim_effects_1.c | 4 +- src/battle_gfx_sfx_util.c | 3 +- src/battle_gimmick.c | 153 ++++++++++++- src/battle_interface.c | 223 +------------------ src/battle_terastal.c | 415 ----------------------------------- src/battle_util.c | 2 + src/battle_z_move.c | 4 +- src/data/gimmicks.h | 119 +--------- src/data/graphics/gimmicks.h | 234 ++++++++++++++++++++ 13 files changed, 422 insertions(+), 759 deletions(-) create mode 100644 src/data/graphics/gimmicks.h diff --git a/include/battle.h b/include/battle.h index cbe5898ae5f6..ba7d80ecbfe1 100644 --- a/include/battle.h +++ b/include/battle.h @@ -588,6 +588,7 @@ struct BattleGimmickData 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 diff --git a/include/battle_gimmick.h b/include/battle_gimmick.h index 2eb1d9bca016..86ff0bb1be5a 100644 --- a/include/battle_gimmick.h +++ b/include/battle_gimmick.h @@ -17,6 +17,8 @@ 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); }; @@ -36,6 +38,15 @@ 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); +void DestroyIndicatorSprite(u32 healthboxId); + extern const struct GimmickInfo gGimmicksInfo[]; #endif diff --git a/include/battle_interface.h b/include/battle_interface.h index 558ff1b0e0f8..59093a1af192 100644 --- a/include/battle_interface.h +++ b/include/battle_interface.h @@ -109,8 +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 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); diff --git a/include/battle_terastal.h b/include/battle_terastal.h index 7c8b07a711cc..6b5c38546346 100644 --- a/include/battle_terastal.h +++ b/include/battle_terastal.h @@ -11,14 +11,4 @@ uq4_12_t GetTeraMultiplier(u32 battler, u32 type); u16 GetTeraTypeRGB(u32 type); -void TeraIndicator_LoadSpriteGfx(void); -bool32 TeraIndicator_ShouldBeInvisible(u32 battler); -u8 TeraIndicator_GetSpriteId(u32 healthboxSpriteId); -void TeraIndicator_SetVisibilities(u32 healthboxId, bool32 invisible); -void TeraIndicator_UpdateOamPriorities(u32 healthboxId, u32 oamPriority); -void TeraIndicator_UpdateLevel(u32 healthboxId, u32 level); -void TeraIndicator_CreateSprite(u32 battler, u32 healthboxSpriteId); -void TeraIndicator_DestroySprite(u32 healthboxSpriteId); -void TeraIndicator_UpdateType(u32 battler, u32 healthboxSpriteId); - #endif diff --git a/src/battle_anim_effects_1.c b/src/battle_anim_effects_1.c index 7ce0e0c36cbf..d47c8aeebb15 100644 --- a/src/battle_anim_effects_1.c +++ b/src/battle_anim_effects_1.c @@ -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) diff --git a/src/battle_gfx_sfx_util.c b/src/battle_gfx_sfx_util.c index 3bd6c2dd5fa4..0c4f0dd85e81 100644 --- a/src/battle_gfx_sfx_util.c +++ b/src/battle_gfx_sfx_util.c @@ -706,8 +706,7 @@ bool8 BattleLoadAllHealthBoxesGfx(u8 state) { LoadSpritePalette(&sSpritePalettes_HealthBoxHealthBar[0]); LoadSpritePalette(&sSpritePalettes_HealthBoxHealthBar[1]); - MegaIndicator_LoadSpritesGfx(); - TeraIndicator_LoadSpriteGfx(); + LoadIndicatorSpritesGfx(); } else if (!IsDoubleBattle()) { diff --git a/src/battle_gimmick.c b/src/battle_gimmick.c index 916d1cbea50f..d0090f6830d9 100644 --- a/src/battle_gimmick.c +++ b/src/battle_gimmick.c @@ -14,8 +14,6 @@ #include "util.h" #include "test_runner.h" -static void SpriteCb_GimmickTrigger(struct Sprite *sprite); - #include "data/gimmicks.h" // Populates gBattleStruct->gimmick.usableGimmick for each battler. @@ -246,3 +244,154 @@ static void SpriteCb_GimmickTrigger(struct Sprite *sprite) 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; +} + +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 diff --git a/src/battle_interface.c b/src/battle_interface.c index d6f47a40d69c..9999998c595f 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -195,11 +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 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); @@ -625,7 +620,6 @@ static const struct WindowTemplate sHealthboxWindowTemplate = { // 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] @@ -734,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; @@ -822,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) @@ -831,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) @@ -858,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) @@ -876,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); @@ -932,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) || GetActiveGimmick(battler) == GIMMICK_DYNAMAX) + // 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 (GetActiveGimmick(battler) == GIMMICK_TERA) - { - 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 { @@ -954,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); @@ -1255,183 +1235,6 @@ void SwapHpBarsWithHpText(void) } } -// 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 = GetActiveGimmick(battlerId) == GIMMICK_DYNAMAX; - - 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 @@ -2175,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) { diff --git a/src/battle_terastal.c b/src/battle_terastal.c index 6752be457481..f2fdd21c5f62 100644 --- a/src/battle_terastal.c +++ b/src/battle_terastal.c @@ -192,418 +192,3 @@ u16 GetTeraTypeRGB(u32 type) { return sTeraTypeRGBValues[type]; } - -// TERA INDICATOR: -static const u16 sTeraIndicatorPal[] = INCBIN_U16("graphics/battle_interface/tera_indicator.gbapal"); -static const u8 ALIGNED(4) sNormalIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/normal_indicator.4bpp"); -static const u8 ALIGNED(4) sFightingIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/fighting_indicator.4bpp"); -static const u8 ALIGNED(4) sFlyingIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/flying_indicator.4bpp"); -static const u8 ALIGNED(4) sPoisonIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/poison_indicator.4bpp"); -static const u8 ALIGNED(4) sGroundIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/ground_indicator.4bpp"); -static const u8 ALIGNED(4) sRockIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/rock_indicator.4bpp"); -static const u8 ALIGNED(4) sBugIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/bug_indicator.4bpp"); -static const u8 ALIGNED(4) sGhostIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/ghost_indicator.4bpp"); -static const u8 ALIGNED(4) sSteelIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/steel_indicator.4bpp"); -static const u8 ALIGNED(4) sFireIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/fire_indicator.4bpp"); -static const u8 ALIGNED(4) sWaterIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/water_indicator.4bpp"); -static const u8 ALIGNED(4) sGrassIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/grass_indicator.4bpp"); -static const u8 ALIGNED(4) sElectricIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/electric_indicator.4bpp"); -static const u8 ALIGNED(4) sPsychicIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/psychic_indicator.4bpp"); -static const u8 ALIGNED(4) sIceIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/ice_indicator.4bpp"); -static const u8 ALIGNED(4) sDragonIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/dragon_indicator.4bpp"); -static const u8 ALIGNED(4) sDarkIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/dark_indicator.4bpp"); -static const u8 ALIGNED(4) sFairyIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/fairy_indicator.4bpp"); -static const u8 ALIGNED(4) sStellarIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/stellar_indicator.4bpp"); - -static void SpriteCb_TeraIndicator(struct Sprite *sprite); -static const s8 sIndicatorPositions[][2] = -{ - [B_POSITION_PLAYER_LEFT] = {53, -9}, - [B_POSITION_OPPONENT_LEFT] = {44, -9}, - [B_POSITION_PLAYER_RIGHT] = {52, -9}, - [B_POSITION_OPPONENT_RIGHT] = {44, -9}, -}; - -static const struct SpritePalette sSpritePalette_TeraIndicator = -{ - sTeraIndicatorPal, TAG_TERA_INDICATOR_PAL -}; - -static const struct OamData sOamData_TeraIndicator = -{ - .shape = SPRITE_SHAPE(16x16), - .size = SPRITE_SIZE(16x16), - .priority = 1, -}; - -static const struct SpriteTemplate sSpriteTemplate_NormalIndicator = -{ - .tileTag = TAG_NORMAL_INDICATOR_TILE, - .paletteTag = TAG_TERA_INDICATOR_PAL, - .oam = &sOamData_TeraIndicator, - .anims = gDummySpriteAnimTable, - .images = NULL, - .affineAnims = gDummySpriteAffineAnimTable, - .callback = SpriteCb_TeraIndicator, -}; - -static const struct SpriteTemplate sSpriteTemplate_FightingIndicator = -{ - .tileTag = TAG_FIGHTING_INDICATOR_TILE, - .paletteTag = TAG_TERA_INDICATOR_PAL, - .oam = &sOamData_TeraIndicator, - .anims = gDummySpriteAnimTable, - .images = NULL, - .affineAnims = gDummySpriteAffineAnimTable, - .callback = SpriteCb_TeraIndicator, -}; - -static const struct SpriteTemplate sSpriteTemplate_FlyingIndicator = -{ - .tileTag = TAG_FLYING_INDICATOR_TILE, - .paletteTag = TAG_TERA_INDICATOR_PAL, - .oam = &sOamData_TeraIndicator, - .anims = gDummySpriteAnimTable, - .images = NULL, - .affineAnims = gDummySpriteAffineAnimTable, - .callback = SpriteCb_TeraIndicator, -}; - -static const struct SpriteTemplate sSpriteTemplate_PoisonIndicator = -{ - .tileTag = TAG_POISON_INDICATOR_TILE, - .paletteTag = TAG_TERA_INDICATOR_PAL, - .oam = &sOamData_TeraIndicator, - .anims = gDummySpriteAnimTable, - .images = NULL, - .affineAnims = gDummySpriteAffineAnimTable, - .callback = SpriteCb_TeraIndicator, -}; - -static const struct SpriteTemplate sSpriteTemplate_GroundIndicator = -{ - .tileTag = TAG_GROUND_INDICATOR_TILE, - .paletteTag = TAG_TERA_INDICATOR_PAL, - .oam = &sOamData_TeraIndicator, - .anims = gDummySpriteAnimTable, - .images = NULL, - .affineAnims = gDummySpriteAffineAnimTable, - .callback = SpriteCb_TeraIndicator, -}; - -static const struct SpriteTemplate sSpriteTemplate_RockIndicator = -{ - .tileTag = TAG_ROCK_INDICATOR_TILE, - .paletteTag = TAG_TERA_INDICATOR_PAL, - .oam = &sOamData_TeraIndicator, - .anims = gDummySpriteAnimTable, - .images = NULL, - .affineAnims = gDummySpriteAffineAnimTable, - .callback = SpriteCb_TeraIndicator, -}; - -static const struct SpriteTemplate sSpriteTemplate_BugIndicator = -{ - .tileTag = TAG_BUG_INDICATOR_TILE, - .paletteTag = TAG_TERA_INDICATOR_PAL, - .oam = &sOamData_TeraIndicator, - .anims = gDummySpriteAnimTable, - .images = NULL, - .affineAnims = gDummySpriteAffineAnimTable, - .callback = SpriteCb_TeraIndicator, -}; - -static const struct SpriteTemplate sSpriteTemplate_GhostIndicator = -{ - .tileTag = TAG_GHOST_INDICATOR_TILE, - .paletteTag = TAG_TERA_INDICATOR_PAL, - .oam = &sOamData_TeraIndicator, - .anims = gDummySpriteAnimTable, - .images = NULL, - .affineAnims = gDummySpriteAffineAnimTable, - .callback = SpriteCb_TeraIndicator, -}; - -static const struct SpriteTemplate sSpriteTemplate_SteelIndicator = -{ - .tileTag = TAG_STEEL_INDICATOR_TILE, - .paletteTag = TAG_TERA_INDICATOR_PAL, - .oam = &sOamData_TeraIndicator, - .anims = gDummySpriteAnimTable, - .images = NULL, - .affineAnims = gDummySpriteAffineAnimTable, - .callback = SpriteCb_TeraIndicator, -}; - -static const struct SpriteTemplate sSpriteTemplate_FireIndicator = -{ - .tileTag = TAG_FIRE_INDICATOR_TILE, - .paletteTag = TAG_TERA_INDICATOR_PAL, - .oam = &sOamData_TeraIndicator, - .anims = gDummySpriteAnimTable, - .images = NULL, - .affineAnims = gDummySpriteAffineAnimTable, - .callback = SpriteCb_TeraIndicator, -}; - -static const struct SpriteTemplate sSpriteTemplate_WaterIndicator = -{ - .tileTag = TAG_WATER_INDICATOR_TILE, - .paletteTag = TAG_TERA_INDICATOR_PAL, - .oam = &sOamData_TeraIndicator, - .anims = gDummySpriteAnimTable, - .images = NULL, - .affineAnims = gDummySpriteAffineAnimTable, - .callback = SpriteCb_TeraIndicator, -}; - -static const struct SpriteTemplate sSpriteTemplate_GrassIndicator = -{ - .tileTag = TAG_GRASS_INDICATOR_TILE, - .paletteTag = TAG_TERA_INDICATOR_PAL, - .oam = &sOamData_TeraIndicator, - .anims = gDummySpriteAnimTable, - .images = NULL, - .affineAnims = gDummySpriteAffineAnimTable, - .callback = SpriteCb_TeraIndicator, -}; - -static const struct SpriteTemplate sSpriteTemplate_ElectricIndicator = -{ - .tileTag = TAG_ELECTRIC_INDICATOR_TILE, - .paletteTag = TAG_TERA_INDICATOR_PAL, - .oam = &sOamData_TeraIndicator, - .anims = gDummySpriteAnimTable, - .images = NULL, - .affineAnims = gDummySpriteAffineAnimTable, - .callback = SpriteCb_TeraIndicator, -}; - -static const struct SpriteTemplate sSpriteTemplate_PsychicIndicator = -{ - .tileTag = TAG_PSYCHIC_INDICATOR_TILE, - .paletteTag = TAG_TERA_INDICATOR_PAL, - .oam = &sOamData_TeraIndicator, - .anims = gDummySpriteAnimTable, - .images = NULL, - .affineAnims = gDummySpriteAffineAnimTable, - .callback = SpriteCb_TeraIndicator, -}; - -static const struct SpriteTemplate sSpriteTemplate_IceIndicator = -{ - .tileTag = TAG_ICE_INDICATOR_TILE, - .paletteTag = TAG_TERA_INDICATOR_PAL, - .oam = &sOamData_TeraIndicator, - .anims = gDummySpriteAnimTable, - .images = NULL, - .affineAnims = gDummySpriteAffineAnimTable, - .callback = SpriteCb_TeraIndicator, -}; - -static const struct SpriteTemplate sSpriteTemplate_DragonIndicator = -{ - .tileTag = TAG_DRAGON_INDICATOR_TILE, - .paletteTag = TAG_TERA_INDICATOR_PAL, - .oam = &sOamData_TeraIndicator, - .anims = gDummySpriteAnimTable, - .images = NULL, - .affineAnims = gDummySpriteAffineAnimTable, - .callback = SpriteCb_TeraIndicator, -}; - -static const struct SpriteTemplate sSpriteTemplate_DarkIndicator = -{ - .tileTag = TAG_DARK_INDICATOR_TILE, - .paletteTag = TAG_TERA_INDICATOR_PAL, - .oam = &sOamData_TeraIndicator, - .anims = gDummySpriteAnimTable, - .images = NULL, - .affineAnims = gDummySpriteAffineAnimTable, - .callback = SpriteCb_TeraIndicator, -}; - -static const struct SpriteTemplate sSpriteTemplate_FairyIndicator = -{ - .tileTag = TAG_FAIRY_INDICATOR_TILE, - .paletteTag = TAG_TERA_INDICATOR_PAL, - .oam = &sOamData_TeraIndicator, - .anims = gDummySpriteAnimTable, - .images = NULL, - .affineAnims = gDummySpriteAffineAnimTable, - .callback = SpriteCb_TeraIndicator, -}; - -static const struct SpriteTemplate sSpriteTemplate_StellarIndicator = -{ - .tileTag = TAG_STELLAR_INDICATOR_TILE, - .paletteTag = TAG_TERA_INDICATOR_PAL, - .oam = &sOamData_TeraIndicator, - .anims = gDummySpriteAnimTable, - .images = NULL, - .affineAnims = gDummySpriteAffineAnimTable, - .callback = SpriteCb_TeraIndicator, -}; - -static const struct SpriteSheet sTeraIndicatorSpriteSheets[NUMBER_OF_MON_TYPES + 1] = -{ - {sNormalIndicatorGfx, sizeof(sNormalIndicatorGfx), TAG_NORMAL_INDICATOR_TILE}, - {sFightingIndicatorGfx, sizeof(sFightingIndicatorGfx), TAG_FIGHTING_INDICATOR_TILE}, - {sFlyingIndicatorGfx, sizeof(sFlyingIndicatorGfx), TAG_FLYING_INDICATOR_TILE}, - {sPoisonIndicatorGfx, sizeof(sPoisonIndicatorGfx), TAG_POISON_INDICATOR_TILE}, - {sGroundIndicatorGfx, sizeof(sGroundIndicatorGfx), TAG_GROUND_INDICATOR_TILE}, - {sRockIndicatorGfx, sizeof(sRockIndicatorGfx), TAG_ROCK_INDICATOR_TILE}, - {sBugIndicatorGfx, sizeof(sBugIndicatorGfx), TAG_BUG_INDICATOR_TILE}, - {sGhostIndicatorGfx, sizeof(sGhostIndicatorGfx), TAG_GHOST_INDICATOR_TILE}, - {sSteelIndicatorGfx, sizeof(sSteelIndicatorGfx), TAG_STEEL_INDICATOR_TILE}, - {sNormalIndicatorGfx, sizeof(sNormalIndicatorGfx), TAG_NORMAL_INDICATOR_TILE}, // TYPE_MYSTERY - {sFireIndicatorGfx, sizeof(sFireIndicatorGfx), TAG_FIRE_INDICATOR_TILE}, - {sWaterIndicatorGfx, sizeof(sWaterIndicatorGfx), TAG_WATER_INDICATOR_TILE}, - {sGrassIndicatorGfx, sizeof(sGrassIndicatorGfx), TAG_GRASS_INDICATOR_TILE}, - {sElectricIndicatorGfx, sizeof(sElectricIndicatorGfx), TAG_ELECTRIC_INDICATOR_TILE}, - {sPsychicIndicatorGfx, sizeof(sPsychicIndicatorGfx), TAG_PSYCHIC_INDICATOR_TILE}, - {sIceIndicatorGfx, sizeof(sIceIndicatorGfx), TAG_ICE_INDICATOR_TILE}, - {sDragonIndicatorGfx, sizeof(sDragonIndicatorGfx), TAG_DRAGON_INDICATOR_TILE}, - {sDarkIndicatorGfx, sizeof(sDarkIndicatorGfx), TAG_DARK_INDICATOR_TILE}, - {sFairyIndicatorGfx, sizeof(sFairyIndicatorGfx), TAG_FAIRY_INDICATOR_TILE}, - {sStellarIndicatorGfx, sizeof(sStellarIndicatorGfx), TAG_STELLAR_INDICATOR_TILE}, - {0} -}; - -static const struct SpriteTemplate * const sTeraIndicatorSpriteTemplates[NUMBER_OF_MON_TYPES] = -{ - [TYPE_NORMAL] = &sSpriteTemplate_NormalIndicator, - [TYPE_FIGHTING] = &sSpriteTemplate_FightingIndicator, - [TYPE_FLYING] = &sSpriteTemplate_FlyingIndicator, - [TYPE_POISON] = &sSpriteTemplate_PoisonIndicator, - [TYPE_GROUND] = &sSpriteTemplate_GroundIndicator, - [TYPE_ROCK] = &sSpriteTemplate_RockIndicator, - [TYPE_BUG] = &sSpriteTemplate_BugIndicator, - [TYPE_GHOST] = &sSpriteTemplate_GhostIndicator, - [TYPE_STEEL] = &sSpriteTemplate_SteelIndicator, - [TYPE_MYSTERY] = &sSpriteTemplate_NormalIndicator, // just in case - [TYPE_FIRE] = &sSpriteTemplate_FireIndicator, - [TYPE_WATER] = &sSpriteTemplate_WaterIndicator, - [TYPE_GRASS] = &sSpriteTemplate_GrassIndicator, - [TYPE_ELECTRIC] = &sSpriteTemplate_ElectricIndicator, - [TYPE_PSYCHIC] = &sSpriteTemplate_PsychicIndicator, - [TYPE_ICE] = &sSpriteTemplate_IceIndicator, - [TYPE_DRAGON] = &sSpriteTemplate_DragonIndicator, - [TYPE_DARK] = &sSpriteTemplate_DarkIndicator, - [TYPE_FAIRY] = &sSpriteTemplate_FairyIndicator, - [TYPE_STELLAR] = &sSpriteTemplate_StellarIndicator, -}; - -// for sprite data fields -#define tBattler data[0] -#define tType data[1] // Indicator type: tera -#define tPosX data[2] -#define tLevelXDelta data[3] // X position depends whether level has 3, 2 or 1 digit - -// data fields for healthboxMain -// oam.affineParam holds healthboxRight spriteId -#define hMain_TeraIndicatorId data[3] -#define hMain_HealthBarSpriteId data[5] -#define hMain_Battler data[6] -#define hMain_Data7 data[7] - -// data fields for healthboxRight -#define hOther_HealthBoxSpriteId data[5] - -// data fields for healthbar -#define hBar_HealthBoxSpriteId data[5] - -void TeraIndicator_LoadSpriteGfx(void) -{ - LoadSpriteSheets(sTeraIndicatorSpriteSheets); - LoadSpritePalette(&sSpritePalette_TeraIndicator); -} - -bool32 TeraIndicator_ShouldBeInvisible(u32 battler) -{ - return GetActiveGimmick(battler) != GIMMICK_TERA; -} - -u8 TeraIndicator_GetSpriteId(u32 healthboxSpriteId) -{ - return gBattleStruct->tera.indicatorSpriteId[gSprites[healthboxSpriteId].hMain_Battler]; -} - -void TeraIndicator_SetVisibilities(u32 healthboxId, bool32 invisible) -{ - u8 spriteId = TeraIndicator_GetSpriteId(healthboxId); - u32 battler = gSprites[healthboxId].hMain_Battler; - - if (GetSafariZoneFlag()) - return; - - if (invisible == TRUE) - gSprites[spriteId].invisible = TRUE; - else // Try visible. - gSprites[spriteId].invisible = TeraIndicator_ShouldBeInvisible(battler); -} - -void TeraIndicator_UpdateOamPriorities(u32 healthboxId, u32 oamPriority) -{ - u8 spriteId = TeraIndicator_GetSpriteId(healthboxId); - gSprites[spriteId].oam.priority = oamPriority; -} - -void TeraIndicator_UpdateLevel(u32 healthboxId, u32 level) -{ - s16 xDelta = 0; - u8 spriteId = TeraIndicator_GetSpriteId(healthboxId); - - if (level >= 100) - xDelta -= 4; - else if (level < 10) - xDelta += 5; - - gSprites[spriteId].tLevelXDelta = xDelta; -} - -void TeraIndicator_CreateSprite(u32 battler, u32 healthboxSpriteId) -{ - u32 position; - u8 spriteId; - s16 xHealthbox = 0, y = 0; - s32 x = 0; - u32 type = GetBattlerTeraType(battler); - - position = GetBattlerPosition(battler); - GetBattlerHealthboxCoords(battler, &xHealthbox, &y); - - x = sIndicatorPositions[position][0]; - y += sIndicatorPositions[position][1]; - - spriteId = gBattleStruct->tera.indicatorSpriteId[battler] = CreateSpriteAtEnd(sTeraIndicatorSpriteTemplates[type], 0, y, 0); - gSprites[spriteId].tBattler = battler; - gSprites[spriteId].tPosX = x; - gSprites[spriteId].invisible = TRUE; -} - -void TeraIndicator_DestroySprite(u32 healthboxSpriteId) -{ - u8 spriteId = TeraIndicator_GetSpriteId(healthboxSpriteId); - DestroySprite(&gSprites[spriteId]); -} - -void TeraIndicator_UpdateType(u32 battler, u32 healthboxSpriteId) -{ - TeraIndicator_DestroySprite(healthboxSpriteId); - TeraIndicator_CreateSprite(battler, healthboxSpriteId); -} - -static void SpriteCb_TeraIndicator(struct Sprite *sprite) -{ - u32 battler = sprite->tBattler; - - sprite->x = gSprites[gHealthboxSpriteIds[battler]].x + sprite->tPosX + sprite->tLevelXDelta; - sprite->x2 = gSprites[gHealthboxSpriteIds[battler]].x2; - sprite->y2 = gSprites[gHealthboxSpriteIds[battler]].y2; -} - -#undef tBattler -#undef tType -#undef tPosX -#undef tLevelXDelta diff --git a/src/battle_util.c b/src/battle_util.c index 353c7344d628..66746e862bf7 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -10473,6 +10473,7 @@ bool32 CanUltraBurst(u32 battler) 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 @@ -10482,6 +10483,7 @@ void ActivateMegaEvolution(u32 battler) void ActivateUltraBurst(u32 battler) { gLastUsedItem = gBattleMons[battler].item; + SetActiveGimmick(battler, GIMMICK_ULTRA_BURST); BattleScriptExecute(BattleScript_UltraBurst); } diff --git a/src/battle_z_move.c b/src/battle_z_move.c index 54a1e137d128..c3dc775e2155 100644 --- a/src/battle_z_move.c +++ b/src/battle_z_move.c @@ -128,7 +128,7 @@ bool32 CanUseZMove(u32 battler) return FALSE; // Check if battler has another gimmick active. - if (GetActiveGimmick(battler) != GIMMICK_NONE) + if (GetActiveGimmick(battler) != GIMMICK_NONE && GetActiveGimmick(battler) != GIMMICK_ULTRA_BURST) return FALSE; // Check if battler isn't holding a Z-Crystal. @@ -215,7 +215,7 @@ void AssignUsableZMoves(u32 battler, u16 *moves) bool32 TryChangeZTrigger(u32 battler, u32 moveIndex) { bool32 viableZMove = (gBattleStruct->zmove.possibleZMoves[battler] & gBitTable[moveIndex]) != 0; - DebugPrintf("%d: %d", moveIndex, viableZMove); + if (gBattleStruct->zmove.viable && !viableZMove) HideGimmickTriggerSprite(); // Was a viable z move, now is not -> slide out else if (!gBattleStruct->zmove.viable && viableZMove) diff --git a/src/data/gimmicks.h b/src/data/gimmicks.h index 838070cd7e98..95a233e3fe57 100644 --- a/src/data/gimmicks.h +++ b/src/data/gimmicks.h @@ -1,115 +1,4 @@ -// General trigger data - -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 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, -}; - -// Mega trigger data -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_GIMMICK_TRIGGER_TILE -}; -static const struct SpritePalette sSpritePalette_MegaTrigger = -{ - sMegaTriggerPal, TAG_GIMMICK_TRIGGER_TILE -}; - -// Z-Move trigger data -static const u8 ALIGNED(4) sZMoveTriggerGfx[] = INCBIN_U8("graphics/battle_interface/z_move_trigger.4bpp"); -static const u16 sZMoveTriggerPal[] = INCBIN_U16("graphics/battle_interface/z_move_trigger.gbapal"); - -static const struct SpriteSheet sSpriteSheet_ZMoveTrigger = { - sZMoveTriggerGfx, sizeof(sZMoveTriggerGfx), TAG_GIMMICK_TRIGGER_TILE -}; - -static const struct SpritePalette sSpritePalette_ZMoveTrigger = { - sZMoveTriggerPal, TAG_GIMMICK_TRIGGER_PAL -}; - -// Ultra Burst trigger data -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_GIMMICK_TRIGGER_TILE -}; -static const struct SpritePalette sSpritePalette_BurstTrigger = -{ - sBurstTriggerPal, TAG_GIMMICK_TRIGGER_TILE -}; - -// Dynamax trigger data - -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_GIMMICK_TRIGGER_TILE -}; -static const struct SpritePalette sSpritePalette_DynamaxTrigger = -{ - sDynamaxTriggerPal, TAG_GIMMICK_TRIGGER_PAL -}; - -// Tera trigger data -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_GIMMICK_TRIGGER_TILE -}; -static const struct SpritePalette sSpritePalette_TeraTrigger = -{ - sTeraTriggerPal, TAG_GIMMICK_TRIGGER_TILE -}; +#include "graphics/gimmicks.h" // Gimmick data @@ -121,6 +10,8 @@ const struct GimmickInfo gGimmicksInfo[GIMMICKS_COUNT] = .triggerSheet = &sSpriteSheet_MegaTrigger, .triggerPal = &sSpritePalette_MegaTrigger, .triggerTemplate = &sSpriteTemplate_GimmickTrigger, + .indicatorSheet = &sSpriteSheet_MegaIndicator, + .indicatorPal = &sSpritePalette_MegaIndicator, .CanActivate = CanMegaEvolve, .ActivateGimmick = ActivateMegaEvolution, }, @@ -145,6 +36,8 @@ const struct GimmickInfo gGimmicksInfo[GIMMICKS_COUNT] = .triggerSheet = &sSpriteSheet_DynamaxTrigger, .triggerPal = &sSpritePalette_DynamaxTrigger, .triggerTemplate = &sSpriteTemplate_GimmickTrigger, + .indicatorSheet = &sSpriteSheet_DynamaxIndicator, + .indicatorPal = &sSpritePalette_MiscIndicator, .CanActivate = CanDynamax, .ActivateGimmick = ActivateDynamax, }, @@ -153,6 +46,8 @@ const struct GimmickInfo gGimmicksInfo[GIMMICKS_COUNT] = .triggerSheet = &sSpriteSheet_TeraTrigger, .triggerPal = &sSpritePalette_TeraTrigger, .triggerTemplate = &sSpriteTemplate_GimmickTrigger, + .indicatorSheet = NULL, // handled separately + .indicatorPal = &sSpritePalette_TeraIndicator, .CanActivate = CanTerastallize, .ActivateGimmick = ActivateTera, } diff --git a/src/data/graphics/gimmicks.h b/src/data/graphics/gimmicks.h new file mode 100644 index 000000000000..b3ff08305bb7 --- /dev/null +++ b/src/data/graphics/gimmicks.h @@ -0,0 +1,234 @@ +// TRIGGERS +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, +}; + +// Mega trigger data +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_GIMMICK_TRIGGER_TILE +}; +static const struct SpritePalette sSpritePalette_MegaTrigger = +{ + sMegaTriggerPal, TAG_GIMMICK_TRIGGER_TILE +}; + +// Z-Move trigger data +static const u8 ALIGNED(4) sZMoveTriggerGfx[] = INCBIN_U8("graphics/battle_interface/z_move_trigger.4bpp"); +static const u16 sZMoveTriggerPal[] = INCBIN_U16("graphics/battle_interface/z_move_trigger.gbapal"); + +static const struct SpriteSheet sSpriteSheet_ZMoveTrigger = { + sZMoveTriggerGfx, sizeof(sZMoveTriggerGfx), TAG_GIMMICK_TRIGGER_TILE +}; +static const struct SpritePalette sSpritePalette_ZMoveTrigger = { + sZMoveTriggerPal, TAG_GIMMICK_TRIGGER_PAL +}; + +// Ultra Burst trigger data +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_GIMMICK_TRIGGER_TILE +}; +static const struct SpritePalette sSpritePalette_BurstTrigger = +{ + sBurstTriggerPal, TAG_GIMMICK_TRIGGER_TILE +}; + +// Dynamax trigger data +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_GIMMICK_TRIGGER_TILE +}; +static const struct SpritePalette sSpritePalette_DynamaxTrigger = +{ + sDynamaxTriggerPal, TAG_GIMMICK_TRIGGER_PAL +}; + +// Tera trigger data +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_GIMMICK_TRIGGER_TILE +}; +static const struct SpritePalette sSpritePalette_TeraTrigger = +{ + sTeraTriggerPal, TAG_GIMMICK_TRIGGER_TILE +}; + +// INDICATORS +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 u16 sMiscIndicatorPal[] = INCBIN_U16("graphics/battle_interface/misc_indicator.gbapal"); // has room for more colors + +static const struct SpritePalette sSpritePalette_MiscIndicator = +{ + sMiscIndicatorPal, TAG_MISC_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, +}; + +// Mega indicator data +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 struct SpriteSheet sSpriteSheet_MegaIndicator = +{ + sMegaIndicatorGfx, sizeof(sMegaIndicatorGfx), TAG_MEGA_INDICATOR_TILE +}; + +static const struct SpritePalette sSpritePalette_MegaIndicator = +{ + sMegaIndicatorPal, TAG_MEGA_INDICATOR_PAL +}; + +// Primal indicator data +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 struct SpriteSheet sSpriteSheet_AlphaIndicator = +{ + sAlphaIndicatorGfx, sizeof(sAlphaIndicatorGfx), TAG_ALPHA_INDICATOR_TILE +}; + +static const struct SpriteSheet sSpriteSheet_OmegaIndicator = +{ + sOmegaIndicatorGfx, sizeof(sOmegaIndicatorGfx), TAG_OMEGA_INDICATOR_TILE +}; + +// Dynamax indicator data +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 sSpriteSheet_DynamaxIndicator = +{ + sDynamaxIndicatorGfx, sizeof(sDynamaxIndicatorGfx), TAG_DYNAMAX_INDICATOR_TILE +}; + +// Tera indicator data +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 const struct SpritePalette sSpritePalette_TeraIndicator = +{ + sTeraIndicatorPal, TAG_TERA_INDICATOR_PAL +}; + +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} +}; From 70a3c9924268c7b7c4f28cb9a9c0ef25ea34cd42 Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Mon, 20 May 2024 12:53:11 -0700 Subject: [PATCH 16/33] removed TeraData struct --- include/battle.h | 8 +------- src/battle_terastal.c | 4 ++-- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/include/battle.h b/include/battle.h index ba7d80ecbfe1..2203eaf1478f 100644 --- a/include/battle.h +++ b/include/battle.h @@ -577,12 +577,6 @@ struct DynamaxData u16 levelUpHP; }; -struct TeraData -{ - u32 stellarBoostFlags[NUM_BATTLE_SIDES]; // stored as a bitfield of flags for all types for each side - u8 indicatorSpriteId[MAX_BATTLERS_COUNT]; -}; - struct BattleGimmickData { u8 usableGimmick[MAX_BATTLERS_COUNT]; // first usable gimmick that can be selected for each battler @@ -714,7 +708,6 @@ struct BattleStruct bool8 throwingPokeBall; struct ZMoveData zmove; struct DynamaxData dynamax; - struct TeraData tera; struct BattleGimmickData gimmick; const u8 *trainerSlideMsg; bool8 trainerSlideLowHpMsgDone; @@ -787,6 +780,7 @@ struct BattleStruct 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, diff --git a/src/battle_terastal.c b/src/battle_terastal.c index f2fdd21c5f62..7a9e85b9d6b5 100644 --- a/src/battle_terastal.c +++ b/src/battle_terastal.c @@ -101,14 +101,14 @@ u32 GetBattlerTeraType(u32 battler) 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; } From dbfd0b13fd48665b3c31e874633a7d3c6aa92e0b Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Mon, 20 May 2024 15:23:27 -0700 Subject: [PATCH 17/33] reimplemented AI logic for Z-Moves; no changes --- src/battle_ai_util.c | 4 ++-- src/battle_controller_opponent.c | 6 +++++- src/battle_controller_player_partner.c | 14 ++++++++------ src/data/trainers.h | 2 ++ 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index d07c1b967867..f279fa807cb8 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -3714,9 +3714,9 @@ 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 + return FALSE; // don't use z move on partner if (HasTrainerUsedGimmick(battlerAtk, GIMMICK_Z_MOVE)) - return FALSE; //cant use z move twice + return FALSE; // can't use z move twice if (IsViableZMove(battlerAtk, chosenMove)) { diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index 404ffc0b65ab..88ad216ac569 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -553,12 +553,16 @@ static void OpponentHandleChooseMove(u32 battler) gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); } // If opponent can and should use a gimmick (considering trainer data), do it - if (gBattleStruct->gimmick.usableGimmick[battler] != GIMMICK_NONE) + 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; } diff --git a/src/battle_controller_player_partner.c b/src/battle_controller_player_partner.c index f131449a631a..3a2b878b910d 100644 --- a/src/battle_controller_player_partner.c +++ b/src/battle_controller_player_partner.c @@ -368,15 +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 opponent can and should use a gimmick (considering trainer data), do it - if (gBattleStruct->gimmick.usableGimmick[battler] != GIMMICK_NONE) + // 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); diff --git a/src/data/trainers.h b/src/data/trainers.h index 128eed5a7f0b..964da2b53044 100644 --- a/src/data/trainers.h +++ b/src/data/trainers.h @@ -14748,6 +14748,8 @@ F_TRAINER_FEMALE | #line 5877 .species = SPECIES_POOCHYENA, .gender = TRAINER_MON_RANDOM_GENDER, +#line 5877 + .heldItem = ITEM_NORMALIUM_Z, #line 5879 .iv = TRAINER_PARTY_IVS(0, 0, 0, 0, 0, 0), #line 5878 From 6555305e83102db13c769e3b88498f24d8ff9738 Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Mon, 20 May 2024 15:41:42 -0700 Subject: [PATCH 18/33] updated Z-Move and Ultra Burst trigger gfx --- graphics/battle_interface/burst_trigger.png | Bin 314 -> 6979 bytes graphics/battle_interface/z_move_trigger.png | Bin 7461 -> 7470 bytes src/data/trainers.h | 2 -- 3 files changed, 2 deletions(-) diff --git a/graphics/battle_interface/burst_trigger.png b/graphics/battle_interface/burst_trigger.png index e3eb6384d4b4dde7ac90a245e3b0c683812787d0..bbd16ac5553f0ffa2cad56e4ad4ead08578252e5 100644 GIT binary patch literal 6979 zcmeHKc{o(<`yXY=8qtPoOxexG3}&)rY$Jn^B*`oXQy9&RHIyYKN)%=XpN&=f3aHb3e~@?l5yxLqR@qJ_rON zNHo&30Pj%n(&1bW{)gRiPJ%%Ab$zXD*cN0js5_J4NOu9CY#(<33V72UArSBGnz$3u zL&YK?--pDs*7ZdW^=}`LSXfx$gT9V_aLUWGTqO3cei)3y3b3};O&K}8QqfWAUF=*j zEA`HX(xKruQ)Asn^WW)FDL(mSJfLe_QL8fP(8|gaX2VJLDD~2-PhWiI1l)yUyas?< zJ$cCP#jWq+ACE)^=7uF^8^x;S1~yI+j%u7a2KGowjp&hy3@X&hUK@ z0xE}|7&P=gg3p>ParLDwD`^x2RqS}iGl#v=D@F|jwlhLA9OFtNd)cvLlZ9a^MZKl| zh%#FEq^Y9&>@22HCSM#8Y|Ud|cX%V8eUYpcULaD~8N8%DrMZbR4 z?a)fc@I!;b5gdXwoYpXxY1yv==Z?ESJ||2uk~+8PIJ>=0m}+w1X`akAKR)@pSD6YK zj@SH!a3fa>8l+!Q!U>XuFzFaO>XH+7f+(7~2 z60tt{i@H;SYVVf6ha^-)S+48V4ouuM7N9c~S?nL&ecA0rk)?!`Jb{?$dG+mVZnvH5 zc=*!PLo1gg^rxylilv=R@vPJm?*6kq^$^Xq79Y-X|1Nnq z4-MQ_Rk#_GwBKoqGvTs63+II`LrYvj&_OAdbhH|OVeMK*9wcT|_XZWb8MkE%ulCm3zJ+jN#JWIy< z^@w{u#X{();67TQJ5#Z!hN8Qxb|y#etf{V;bB{Ruz{@oLz!AaA!4z75e@5)&okPVO zp)n;1mzBoeAIaEq8mEd;+Q|GOL&%$abzNiXOY>P;JYUM4@&Zf>c5m3;`X;Nr?>as5 zd|JeML&dje;wSEXdWDWlzB{Bf(QEHAXGIRn@m&y}KQ5kQlQVU@IJ3cI14;T?p-)@J zaEyf|VoI#|x$L#>Sai3mHN8l$L~5w#@w#eX>i^kmN6EIIbWK13!BPKJ8xia z8hYjgZ=s2^%SMZ|D`DPJ1-YP8qSC&#U%zG zFg(6RJXxpBAzOUgm66z}Ug_t`K&Mf7)WIuimRoc3FZS*9&GZUbk=G2Dx`Q#1!dhQg zFPz#(m<*85)_1%d7VfZZm?yD4CTbf!)Hr&N!^?9`*!HaUoh98h!DDwQv772kVc(;b zDB#7rav+5#MyRl-C#WLe?UL9at|tR)oC;Ur}kYSLDMZ=R01Ea6QOszOprN z(rvEzMppBXSr_K9`i0n-fv!1MM8_sR)|cR&{^JJ77cvbO`wobF!%eDkd<`)Qq)BJr z_R=rep7*lFAe*6L1w8!j75Tx7u~aG8AxysVWK$-ejN#=ML)M%Eg>SfGB2vbOnZ*}V z^cx(?7q)~4nZmi(Wq6~7A3gUVi`b`;AD=Au_boE+y3tBvO}(F_n|u2&Q0sw>MiVGTv_}sm~e|UH54Rcfh;0J=5?k z_BG$7%hfjRkM3B9Ej{mS_u6auyj3#mZM;)Tm%Ur&iiyFZEhY@{u-tpzis!gNr{1{n z<>m9ZmR;exEW4dN=if8ucpw;Vagjhw}|xTujoq zJ1@f|2OoJlt4=)-YkFi;UPvWQC!%5S=wecBT9W;wS)5bpg=jZYQX0_vIeq1Nddcnj zQcJ|?$cEd>`MwP2eO4L}-=?SS;_V$;G zH$q1BR|D&3u5wSOrV`2Bceh_|+2F~eT(K{uO!L-JiIMdQL7VL^)u17j0*lEo*G+;N zt`^1xqx-Sa&|58AH^l|b2d2MCU;i~AC|fBU9dmC_xSXiUaZlgR zRY>ji*i-#ZgzKfq$aQji&csg3RTV{*VjLteVtw9;p~=_w zQ;T08yEH@sSGj~TbQH?()ku4#H9Q4Jx5ZTJ;2U41Dhby{( zJqI(PV`1V`p5e_i^TM(%`;GVtulJX&5U;cAtZSs?MVqU#fpMOD117_#Vfo_LrBViT z-??jjauXHLHXd8*9r_O3npEjH&9R~UQF)^)F%tse2%!@Q=0pPFudiwF8#>WHO4F!X zN3!OaWjJgzPq5g@XXZ&t=Z~oyZw_}zF5GPEfhw8jkH_qRBQ6DFlBCNns^AXg|i z2)e*AIMIJ+3?{vTlrKC#(_g&ncgrJ$YNLoBed zxQ9d6EYzjp(r}vm@Lbj6c7Mw)+lsk-U-k6aE~@)Oo_BQ43c9;*h}a@`s&}YYGpSg1R83k8Yc0|DeRSJr#j#X2^VbQ-(5#3m9vKtjx zr2@$jbdX0Kt4^k=($wKpWmPqhf`WsqQ^=}t0Hvmi#i|1+3V`{A!i-4=tCH;UYgDUL zG>{5~Q>Id|%8qb#RT>4PQURZED(Y}m3IM2~RRBjU&T);3M#UR2n66}SIq9xsCjja0 z=Cn4kDmZ?hIZ+d)j6nS}V(vm_JAww_9-zC?7+$P@rmX0$fF+x}swY|vt*VT{sHve; zF&K4>%0D1$fXM=Dag`N~LMZ>pXLVulU^<|*HlT9&=j-^O$CdC;{Xh(F-F}HPDbOvg`)ugmV!cK(CU=m*;x!n zwilTR>~jJ$1+xK5Xe}G4+)pv(f5&?{1FK4*lreA=7Osr4LSykNIJ^o9hWwc){`={Lj+`um&FR+=3Z5FTx8=-CHCRX|-tts~QG9)_r z)U|Wft@UGL<_inX3kwUKpT`P746nPQ zN-E$`IZ~&ka@p3lDgIgTTk@<_oZ5(Y?@CP+*@EeiyA=G(5X%%-zDn|c;xUcTnBUy8b8%6 zR~qP|RN)gz4k0HYkwUMSg-^gb$>A^x_=OGhc zO4KInv|2j~cM)Z(BE{Av?>gc$2ZX3^ Ks+X(l5cD5`Jbp6( delta 288 zcmV+*0pI?^Ho5|kBYyw^b5ch_0Itp)=>Px#1ZP1_K>z@;j|==^1poj55>QN3MY+$T zdzXhzX<+~W06;)M|LK2)2=NyH000SaNLh0L01m_e01m_fl`9S#00027NklsLZd#*$ zu;?5Bgg_*w?x3_eCjfmPIyNAB9CHJ?w&>U*QgM$QOE^{toDGq|tH*FJoVJb!g4+WX zO8iyb{a6HKkssa3kM87$+d@AGM8X_&=w=4MKk94u!&PnVe)Q-7KX8xp`{7t2aQV@V m{OHbpEcSyR=ov5lNa+j6+$om^;)ON<0000aB^>EX>4U6ba`-PAZ2)IW&i+q+P#=-cI!9} zg#U9Dvjp)?E{6~BoY_Gye?E|M>?F?Zp1wU3$Cj-JMWPCYf{gs}?`8hQpU*Ct5OYa2 zrHj8%Lv@WWo5iWuW^;UT37!oA*HV9>}|Mz}^G2v#cxCl3$n5*>BkKIe(bxWBYc$jqihRc8Fi@?8nEQKkX0W8Q<3+>wB$P%vy3**Fqws>0Z=h zDf=5Ic82o%vcxO%j(pGiRe2Qydk5&uYh0bzu+=(XSvJ^dn_X7+cG$rbgAEo`3m0ECEpMAkro z+kLK!?tkn?vj8IcPLwtv2KGW+S9&s6o9v>9>M! zu75Pw$0EM_Ytix)TS*91x)^We*jE;Y>$Z&KXE#&Igzwkt7mHrK&DFW@hrM`KCK?s zYLO5y7y~bf)vQ|gB&SY*eX&id=_<)D{(o5n;%d(_B+j!CWOL=VVrKoVObgb@_>m?{ z5?D4e7c;-a#Jiuh`{jp#wA2U!IWh+wf9UuZk?0;?tb#-jF@%G#0{5Q}Mlf3*v$Z*D z>NQ9g1)>SwqnQUNU$>t@;{IP@n$l!u%Mw%ZJsTN2gjJ^}YgMKa2*7l8M4#d5oPQ<% zEz~kA%4I-!KXPS(eWkQft7%mWMMZz{qj&BOW1MGH!#P4iL(xEZmWy6b>%Pgr1pCAdnR+XB&p*sPyJVq#j#BGdsz+ zgor`}(5o6e7%iL0A_k82NLA%TQgp{B=|zxr?SSJ7nC%_#5H$~F5+l?iDMBQG^jM6S zYlpX%vN?Dn(TH|m7n<2!7&6jfp2vmkZ|ZiR+lJ&B+X~6IgEG^ah>SR$Y=2SFX{mDC zQ2^B5#QTAFVyLprx_X&anDz+r7JPPl31lYydEO*lQmDbWLnwNV@}*%3_R;~pdQXT7UNS5&XulGv1t)D zkJpA@-?4E^5D-5A*MSNX41eSrW3qC7kV5^4O;s3L6b80>n)75yWGMkfBWwDeFGXQM zfouiEn91}(l2eAdw~mU0M2tXs^ml~FK60Bqw=nm7?R*5mgud~Aq7U{IpirrN6wX0; zES({`JrzOg^{eJQ|l4S5Wu6(E7 zl#_byk$SD6z>uR-J-s(c5Cz#WK#Zu6DCi_a&WnX6g$@&n!Hl^mEIk}T))W=^h)iWM)UQV#xfX0)Bb5rOp&|i zdSTN;M(c(SuSDCqmoAAiTlRNEx)hcVw@II+$k~Y zy86VWpfP4J4Q0lWc$7@@72;tU0vB!Gp9m^Y;E(4_Te)msy$x$ynQNt5<1P;X%Z5gf6eOboS9QDB#k!-36+#(h+V0%?%u9TR%GVXP6<p~?Mki;~Bv-*}1A~WRL*!jdpz0`RFtvt(AY;|y zuqr`%EA#eVKhGl2DS=V%7rkE!q87krX~AGSiGLsw$3sQ+-1N7IA9q1SkLOkkC7(z` z>!7L7RH)iEPM4`V^V?Xz9(e4}b>hfVB*e6C6{A2=kkcKZY7@U>m4NiLzH_^Ue5w=D z?gG$<_I9(Wj*jl;PL+ubLy#vepFR!wC`6|YnMWZ)L`T@>O6&dFh8m~Cs(VOx_tAH~ zM}IeVwb#C;>eCEvHUflHkDwPuSiB(159?;CwuA>xv);u)rjxdoQg^_EPqgt!NkhiG`iN^0*>8g`F`Ydn_9F%n?Jl` z%E*PsD`QYu?o`b~uPx$1tEZ4?8%*1^tF1HI7qwcI0XD2B1N1TGlLMrPx{t$x^k6}N zr*<9Zt7eoF*0lNQxWjSWv*_fI_5f}*(@iXu%SN7g4Krbr8EY!Z#Z1zEx5vHWN`H>k zX=$fyIsQKi68T`x3vpKTCOWjsa&rukixy@+Xp)@`M=$lEeY7$3cjfIaZbqw{54!r` zu0MO+BB^F7%>6JT=lfBFcH2huQAFv#vfF4hGz1@Jr?zx#cc>Yr?vO@-)NV92C9QFg z!HM1v2cBKjE{`Y2?ly|PC@1Q?=zl}gtoNm+bfd$C^C(;G^?Bm3o7hvhcMIE(w_e)B z!gac*@U`DHzi4=OkCKqM$sCsW)A?g5mQq-I0Y&l@dwez)U(4NMtq_nNi{!&T_)$<6 z5FCwOLnlSBmWpRM&M_WO>6kRFK{sJ|Exx?9+mCpZrjbY#F>V zgA{SdP@OD@UpPt?i(sL&6nNgNw7S4z7YA_yOYV;H2mx zCH^ldw21NGxF7HCJ?`ECLZi$yt7{z4^s1SRMWsx3RqB3403rqv!nDjRBc57J!*hJy z!^hXV2+#69_vh$Sa~6LCd=l{-Gt4US2Jy^h)!@8OEV81k5}y-~nRG$oN3JU_zi}=& zEbvUxOeg1wMPjMY!Ab|SqNx#25r0{s(`*yS4I@6K+y43Itwk z`(p&?-UXU<+x|Yb?dA#Ke+I7fw!hKo454hX`LQjTl%8ulx zDdh9O`x$*x78tk%de_|ETKhPC0MgXe@(pls2#gjed)?#RJ)OP%d#2Uj56S>?$`HTY zKmY&@_i0pEbXc?X4ut|FGh<;iGBPzC_k!>-+T2pShiXed7u%N0ZRYCd=4bWI$1CW- z#DnI8nS%}h4>cd0o|HrRxoLjnq2eQEU28s~jZyOvZP0ui;_rC00000NkvXXu0mh|f{L1xasU7T delta 4172 zcmV-S5VP;DI;A?0BYzDpdQ@0+Qek%>aB^>EX>4U6ba`-PAZ2)IW&i+q+Rd41lIuFI zg#U9DZwcZ|E{6jc(L3nn_Xo&!xu(;%r-)ZRWvMh!Br=gm=s5ZN-;Vn~e$sVaF6G)v zFNOUp_0+>)r(O5Yuy21Q`?jC(IK|(ej=S_c5jYgt#{17!9)H(AkNejE_hzt{-v(;y zabkWx(H~C;?aM%qDMg);?(fGy{V`As_X+&aj=u^n{aTE?(50HUVRKkKz@qT^)@T_6_xf2DzCjS zpu}dFZsHh=kGbYxpU-R-AILymdB!= zOR+sJoP<2uZG}(eH?v>u)A=+9dl!)JYh9Di^4$kuIX?OAhd*9yyZmB`DM+YnLs%i& zu@-CSVa3aVQ__z=p~fC}Y+$y4H*z$*=Hl(S-5$4m zrM&l7yW`XpA0IR+n1&*!-&_SC{yce8{aDN|0vj8H;5-ZU3IKV0)Z1AV128TwDIpv&7uDRu&M~NksTuP}$#c$MDQ_Z#1 zT3hXPwAfP1t+d*jYrTygdPE#^FTM8Gdmn>`4n`i_J-A?u8D^Ym=2>Q)ZT2}9^jT@; zRaRZit-i(%J8pDhmtA+;eGl;hl_W`)B2}7n8Gi?=op{p8r<{7)>1Vu9^Ul)FfBXD( z)cg~*U`XkO^@SR@*7_X6tDI!T48%h6AYKptr&tNE0IQ|J`4s|T_o2WFWSob44c z5KQ7jKK_N>2Xdcqb1D5txP>QjPNDm6AmkwbtZCal4Xa#{P7vr6p=c&SQ)3;UPo^DIq@claTV$o-geJ0OcSM+Ev5+Fc5rSE=1*?V?5eYkNl zF$IpXx!czf?djJB1R*M3m_coFiO7yed_J;>vuwStuwPD?1=P-&GGpJIV%^}pTN{~^ z@dT-n7!lK2OScFOyS;K((Y**TDR@<{2Tl!@G-VL*3umr2pnp)0 zIZwY+_Wa9)BYE3LYM(o=ZH93dK+5vMGGtacOP}ruVp~jL!ZujOR5w|7w$0UTtwMVn zgpiJ4=h#L$8OY`(3Yfz60*lpshUr_Z|bwKiI5^Mq|0qRuP z&L{-eA{DZCcL;Ux5jon!3ua$R4u8Vbch0?rRChRQb1O+r`1=H#fCCM4p4oukhMIwA z4X;B*Z$mYvQnR%F>!bYp8TaW_TuP}RD{f*KXV&8+!ss!Mn#l}L{$^r^$S_X6>@lmi zYk#00VlVl7!rT%ipaSF!eI*3CeN7txp9>i&$Erm?x_$wQWoG3B=yzxUHESl$1>sbQERcYN9dD#{l_~&w$ww4fx7fn#z7e#&3)7nYo ze1PC49jx!hIr3U@7=e;KZx)G=v}M@<^}(b}UXZy-6&^JOK4zlAtXK`i1X(06&b5_Y z5acF@O9Y5ws8K?I?_8tq*0{@T$M43y*p|Fub;;ZY7IaFm9Dg@KmC=YlWW+yfxy3(D z;9V12pjn4%kBy(C$sz1|DJj3b)-{p`Wtzz@jN3J?WLEp8TB4L4_@n#C>@gmXYJcBp z!R1r}D&Lecv>y0y3me&!PKX0`wG(hxb6O2WP>pU`b$s>PK&-*Ei{5@&J#I~z(QVhfn4rkrUdIq^68V+k5v;F{FsUmmxj3ug0>6h7(?g7&g=L^Ajn8u0aCka zzh+M-4rNB`>Ri0sRw{sJTvpsQGtCQ;nVoe{4d6#H)PGkRS`W1vrlZT?NwIj0$KDCy zg;Y}3#)9`ny=BvWsA*c#+;1l)o>=|Nz+)KztQ!Yn0Y-x-lF--E8h7iK8!f3mDoKd$ z$B9qJGb)N1+^L!xpxjg;!Of92wZ>Hgt~N{>bIwnXjKg`)+n~zZ)pKzu*}Cd+%^v-@1n_RBiWHT>THGeqg4g>?|wI zf>I^tU(y8fWq%aIsp1n&36Lj>pv~39r)W-Vue1V+aV&kEk7KE^#58J+Ku)MfavM)c zrkB+7jKI>6VIK43?r@SW0e8^Jg^Ql8&>fL{et&OONpe#Yi$XZH4}e9INf4W3)t-dY zlV!u5>DA^kuw>>zlxQJ-Xfi%p3=8~wkCE0z<%^Vel13b;Yxj4(_nCKt@9-fi=9zmr(#?S`GIagPO`S2+I>sXa+D8a zR2hT7pNLUrgmYzzQ1!P%^98tf3inWjMNdCOZJB>Q%6)&<@3X+gL8ZJgb7zK%iy=?w zqj2%!$OwdQUw`UJ>iI&NX1YqwQMDhs>VL8dt9(CkAD>I;zK8)zwh3F@|~gq4Pc1!-s`G3VShIK`wX7iKsxx?rLgj8&wA= zM`jhUeA?{A4_}G0f2zaoQHW2jmu6R1|vKTz#!!#ps8Tpg{WPk9y z`{ zH^gIWU0wlqcJy_O_H*#gsbO2j0e_V3g9EQN&Sv>+7(OR-Jok+x%I{`yHf-=*_Jl7Dt$zlyPp zw3|^`#w4nGZMr4*haN%S&}lyeHi&tonF6xgI-{UN-w`HJ4I~-F-MG-h&YKg2sKIH~ zOkBHVn04@D(|oh~oR0o?#nk6c?%Dcg#>ehf7o==OYX*eI^-;2!S}o?ZyS}s{9tZ%% zI<91$fL*E$e>irjv%b08m4B6156f+yOS8sIreBX<7iQfFk;DqTE#C5i1|Uv{{{VF) zsD!MDPew>+%beJgj6bz$K~seD*6eO#1Dlw(Y`xP{cd<44R-)hn0g^H{u$dcrrnmNr zZuy(jzm5JXw7^;4e7N)R;=^8h(2gVuB>oad3_nomRjkNL46pM0nSWa9ncR2NgspyW z4IiDMTCZnsSjW=09V;+uO+i~)agpAZDASB!pm00BhAy;;(87oM-Xo^;)*_DbXc3pS zh;@>*Z`IFgA9SPobgpx9tK%B>qSgx?L;pSSM%H*fqO4s?{#Shs%wUiqaSB7-0w0004nX+uL$ zNkc;*aB^>EX>4Tx0C=2zkv&MmP!xqvQ;Q-MhZZT~kfAzR5PubMlqwd%LTM|s>R|HH zKWNgBq_{W=t_25w7OM^}&bm6d3WDGdh_i!}qKlMxUs7lhAnr)__tl8Q>F%=a_C- z#2dsjo0iUbpMN;a%928SPCRbV1&JTIF1!53x#Y0GGs8wUJx?4a7E4_$cQGp)D)BUN zR8cj`7jiBuoVPfu)jDh6lfN)n&{i^Br!|5EmXJgWB4pH2K@}Eav}>f8NYi=T!$0i! zQ{->A#kN02fq&jzpw+PL?_=9;odEu4;7aTG zYfWJGlk`SMiyi@k+rY(jN0aw}%N-!{q)Ue6NPb#Eu?W1M(KqFR&@Irv=FY8sj?)Jq zOS4+O0S*pswCDE&MWb?+vJ_H#IOgEi_|eV=XyjIX5jeHZo-}G%-0bV=*|Bkr1g3 zG&V3YFf}+fH#apmII~9)SOK$s6JQ8U##?!50000mP)t-sxzD3bX<_K-Q26+0cxYgV zcxZcf-ww4Q>D&PQQyY`k;MYY8z2whDbm9PYz#(_ zABhe4UioxByKqWx93{=< Date: Mon, 20 May 2024 16:01:50 -0700 Subject: [PATCH 19/33] added testrunner check for multiple gimmick use --- test/battle/gimmick/dynamax.c | 2 +- test/test_runner_battle.c | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/test/battle/gimmick/dynamax.c b/test/battle/gimmick/dynamax.c index 02c34caccc07..4dbba8d0ff64 100644 --- a/test/battle/gimmick/dynamax.c +++ b/test/battle/gimmick/dynamax.c @@ -508,7 +508,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon are not affected by Choice items OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); } - TURN { MOVE(player, MOVE_ARM_THRUST, gimmick: GIMMICK_DYNAMAX); } + TURN { MOVE(player, MOVE_ARM_THRUST); } } SCENE { MESSAGE("Wobbuffet used Max Strike!"); HP_BAR(opponent, captureDamage: &results[i].damage); diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 39eb049e8b7f..eb0b6e11907b 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -2063,10 +2063,12 @@ void MoveGetIdAndSlot(s32 battlerId, struct MoveContext *ctx, u32 *moveId, u32 * if (ctx->explicitGimmick && ctx->gimmick != GIMMICK_NONE) { - // Screen for bad item usage as the usual checks do not function before the battle begins. u32 item = GetMonData(mon, MON_DATA_HELD_ITEM); u32 holdEffect = ItemId_GetHoldEffect(item); u32 species = GetMonData(mon, MON_DATA_SPECIES); + u32 side = GetBattlerSide(battlerId); + + // 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 @@ -2076,7 +2078,12 @@ void MoveGetIdAndSlot(s32 battlerId, struct MoveContext *ctx, u32 *moveId, u32 * 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"); - DATA.chosenGimmick[GetBattlerSide(battlerId)][DATA.currentMonIndexes[battlerId]] = ctx->gimmick; + // 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; } } From ca580104fb0d6fd507cf6f7fc3b35a508d9cef26 Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Mon, 20 May 2024 16:52:48 -0700 Subject: [PATCH 20/33] fixed duplicate z-move call in test --- test/battle/gimmick/zmove.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/battle/gimmick/zmove.c b/test/battle/gimmick/zmove.c index d0bd7896c4f5..4046b7037f7b 100644 --- a/test/battle/gimmick/zmove.c +++ b/test/battle/gimmick/zmove.c @@ -333,7 +333,7 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Z-Sleep Talk transforms a used non-status move into OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(player, MOVE_SLEEP_TALK, gimmick: GIMMICK_Z_MOVE); } - 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); From fd3235f4edbdc9668c94dd6810f6ca1905a68894 Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Tue, 28 May 2024 17:28:24 -0700 Subject: [PATCH 21/33] reorganized data/graphics/gimmicks.h --- src/battle_gimmick.c | 8 ++ src/data/graphics/gimmicks.h | 197 ++++++++++------------------------- 2 files changed, 65 insertions(+), 140 deletions(-) diff --git a/src/battle_gimmick.c b/src/battle_gimmick.c index d0090f6830d9..1a81d324735f 100644 --- a/src/battle_gimmick.c +++ b/src/battle_gimmick.c @@ -372,6 +372,14 @@ void UpdateIndicatorLevelData(u32 healthboxId, u32 level) 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; diff --git a/src/data/graphics/gimmicks.h b/src/data/graphics/gimmicks.h index b3ff08305bb7..9c1926425077 100644 --- a/src/data/graphics/gimmicks.h +++ b/src/data/graphics/gimmicks.h @@ -1,6 +1,29 @@ -// TRIGGERS -static const struct OamData sOamData_GimmickTrigger = -{ +// 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, @@ -46,143 +69,11 @@ static const struct SpriteTemplate sSpriteTemplate_GimmickTrigger = .callback = SpriteCb_GimmickTrigger, }; -// Mega trigger data -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_GIMMICK_TRIGGER_TILE -}; -static const struct SpritePalette sSpritePalette_MegaTrigger = -{ - sMegaTriggerPal, TAG_GIMMICK_TRIGGER_TILE -}; - -// Z-Move trigger data -static const u8 ALIGNED(4) sZMoveTriggerGfx[] = INCBIN_U8("graphics/battle_interface/z_move_trigger.4bpp"); -static const u16 sZMoveTriggerPal[] = INCBIN_U16("graphics/battle_interface/z_move_trigger.gbapal"); - -static const struct SpriteSheet sSpriteSheet_ZMoveTrigger = { - sZMoveTriggerGfx, sizeof(sZMoveTriggerGfx), TAG_GIMMICK_TRIGGER_TILE -}; -static const struct SpritePalette sSpritePalette_ZMoveTrigger = { - sZMoveTriggerPal, TAG_GIMMICK_TRIGGER_PAL -}; - -// Ultra Burst trigger data -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_GIMMICK_TRIGGER_TILE -}; -static const struct SpritePalette sSpritePalette_BurstTrigger = -{ - sBurstTriggerPal, TAG_GIMMICK_TRIGGER_TILE -}; - -// Dynamax trigger data -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_GIMMICK_TRIGGER_TILE -}; -static const struct SpritePalette sSpritePalette_DynamaxTrigger = -{ - sDynamaxTriggerPal, TAG_GIMMICK_TRIGGER_PAL -}; - -// Tera trigger data -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_GIMMICK_TRIGGER_TILE -}; -static const struct SpritePalette sSpritePalette_TeraTrigger = -{ - sTeraTriggerPal, TAG_GIMMICK_TRIGGER_TILE -}; - -// INDICATORS -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 u16 sMiscIndicatorPal[] = INCBIN_U16("graphics/battle_interface/misc_indicator.gbapal"); // has room for more colors - -static const struct SpritePalette sSpritePalette_MiscIndicator = -{ - sMiscIndicatorPal, TAG_MISC_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, -}; - -// Mega indicator data +// indicator data 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 struct SpriteSheet sSpriteSheet_MegaIndicator = -{ - sMegaIndicatorGfx, sizeof(sMegaIndicatorGfx), TAG_MEGA_INDICATOR_TILE -}; - -static const struct SpritePalette sSpritePalette_MegaIndicator = -{ - sMegaIndicatorPal, TAG_MEGA_INDICATOR_PAL -}; - -// Primal indicator data 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 struct SpriteSheet sSpriteSheet_AlphaIndicator = -{ - sAlphaIndicatorGfx, sizeof(sAlphaIndicatorGfx), TAG_ALPHA_INDICATOR_TILE -}; - -static const struct SpriteSheet sSpriteSheet_OmegaIndicator = -{ - sOmegaIndicatorGfx, sizeof(sOmegaIndicatorGfx), TAG_OMEGA_INDICATOR_TILE -}; - -// Dynamax indicator data 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 sSpriteSheet_DynamaxIndicator = -{ - sDynamaxIndicatorGfx, sizeof(sDynamaxIndicatorGfx), TAG_DYNAMAX_INDICATOR_TILE -}; - -// Tera indicator data -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"); @@ -203,11 +94,14 @@ static const u8 ALIGNED(4) sDarkIndicatorGfx[] = INCBIN_U8("graphics/battle_inte 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 struct SpritePalette sSpritePalette_TeraIndicator = -{ - sTeraIndicatorPal, TAG_TERA_INDICATOR_PAL -}; +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}, @@ -232,3 +126,26 @@ static const struct SpriteSheet sTeraIndicatorSpriteSheets[NUMBER_OF_MON_TYPES + {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, +}; From eef728195b2583970c8282cc025c0fcf256ea784 Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Tue, 28 May 2024 19:27:03 -0700 Subject: [PATCH 22/33] added signature Z-Move ability tests; implemented Guardian of Alola --- asm/macros/battle_script.inc | 4 + data/battle_scripts_1.s | 10 ++ include/battle_scripts.h | 1 + include/constants/battle_move_effects.h | 1 + src/battle_script_commands.c | 9 ++ src/data/battle_move_effects.h | 6 ++ src/data/moves_info.h | 4 +- test/battle/gimmick/zmove.c | 138 ++++++++++++++++++++++++ 8 files changed, 171 insertions(+), 2 deletions(-) diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index f661ad9dc2cd..dc70c2347aa6 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1650,6 +1650,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 diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index b6a2e941ba39..e7eb3feded57 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -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 diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 7059e26d6e01..f1aa37d3b48d 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -839,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 diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index 04ddb6d88167..1b6d2f5692f6 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -353,6 +353,7 @@ enum { EFFECT_SPICY_EXTRACT, EFFECT_TERA_BLAST, EFFECT_TERA_STARSTORM, + EFFECT_GUARDIAN_OF_ALOLA, NUM_BATTLE_MOVE_EFFECTS, }; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 747eb0531d23..103ad11f90de 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -17027,3 +17027,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; +} diff --git a/src/data/battle_move_effects.h b/src/data/battle_move_effects.h index 1f474206ccf6..9426a181e9d8 100644 --- a/src/data/battle_move_effects.h +++ b/src/data/battle_move_effects.h @@ -2249,4 +2249,10 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = .battleScript = BattleScript_EffectPhotonGeyser, .battleTvScore = 0, // TODO: Assign points }, + + [EFFECT_GUARDIAN_OF_ALOLA] = + { + .battleScript = BattleScript_DamageToQuarterTargetHP, + .battleTvScore = 0, // TODO: Assign points + } }; diff --git a/src/data/moves_info.h b/src/data/moves_info.h index 240a7e168d14..ab6fb295080b 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -20227,9 +20227,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, diff --git a/test/battle/gimmick/zmove.c b/test/battle/gimmick/zmove.c index 4046b7037f7b..3a682135202b 100644 --- a/test/battle/gimmick/zmove.c +++ b/test/battle/gimmick/zmove.c @@ -417,3 +417,141 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Light That Burns the Sky uses the battler's highest 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!"); + } +} From e795879e17ad1bd3d8b17fe9e504f737e25fb8b8 Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Tue, 28 May 2024 20:05:15 -0700 Subject: [PATCH 23/33] fixed bad test update --- test/battle/gimmick/dynamax.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/battle/gimmick/dynamax.c b/test/battle/gimmick/dynamax.c index fbf1df76663c..4aa344ba2920 100644 --- a/test/battle/gimmick/dynamax.c +++ b/test/battle/gimmick/dynamax.c @@ -1480,7 +1480,8 @@ 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); From 179d4ca3af0de60f482cc7740573ea8052065f6f Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Thu, 30 May 2024 12:44:04 -0700 Subject: [PATCH 24/33] fixed Thousand Arrows not affecting Tera Flying; clean-up --- include/battle_gimmick.h | 1 - src/battle_util.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/include/battle_gimmick.h b/include/battle_gimmick.h index 86ff0bb1be5a..d8b3e51f3e97 100644 --- a/include/battle_gimmick.h +++ b/include/battle_gimmick.h @@ -45,7 +45,6 @@ void UpdateIndicatorVisibilityAndType(u32 healthboxId, bool32 invisible); void UpdateIndicatorOamPriority(u32 healthboxId, u32 oamPriority); void UpdateIndicatorLevelData(u32 healthboxId, u32 level); void CreateIndicatorSprite(u32 battler); -void DestroyIndicatorSprite(u32 healthboxId); extern const struct GimmickInfo gGimmicksInfo[]; diff --git a/src/battle_util.c b/src/battle_util.c index 5d858f854570..9708593ef3d5 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -10250,7 +10250,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); } From e6f21c646eaef9061aa8040f0c074a92bb3e55c9 Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Thu, 6 Jun 2024 12:58:36 -0700 Subject: [PATCH 25/33] fixed -ate tests --- test/battle/ability/ate_abilities.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/battle/ability/ate_abilities.c b/test/battle/ability/ate_abilities.c index 12ee75722a83..555fefca74bb 100644 --- a/test/battle/ability/ate_abilities.c +++ b/test/battle/ability/ate_abilities.c @@ -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,7 +62,7 @@ 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, gimmick: GIMMICK_MEGA); } } SCENE { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponent); ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); @@ -76,7 +76,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); From 544d05409f36242eebe963b0ad53023a82bbe0a0 Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Thu, 6 Jun 2024 14:11:48 -0700 Subject: [PATCH 26/33] fixed tera tests --- test/battle/gimmick/terastal.c | 2 +- test/battle/move_effect/tera_blast.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/battle/gimmick/terastal.c b/test/battle/gimmick/terastal.c index c4c0c1fc8056..b3925fcbae5c 100644 --- a/test/battle/gimmick/terastal.c +++ b/test/battle/gimmick/terastal.c @@ -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); } diff --git a/test/battle/move_effect/tera_blast.c b/test/battle/move_effect/tera_blast.c index c899dc6f2280..4fcb52be3a85 100644 --- a/test/battle/move_effect/tera_blast.c +++ b/test/battle/move_effect/tera_blast.c @@ -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…"); From 3f3d56bd1a77562961450ce819ad2c2dad70fc87 Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Thu, 6 Jun 2024 14:26:05 -0700 Subject: [PATCH 27/33] fixed tera tests really --- include/test/battle.h | 2 +- test/battle/move_effect/tera_starstorm.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/test/battle.h b/include/test/battle.h index a3477b1fca7a..b42d8a8af76a 100644 --- a/include/test/battle.h +++ b/include/test/battle.h @@ -326,7 +326,7 @@ * 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. gimmick: GIMMICK_MEGA causes the battler to Mega * Evolve if able, hit: FALSE causes the move to miss, criticalHit: TRUE diff --git a/test/battle/move_effect/tera_starstorm.c b/test/battle/move_effect/tera_starstorm.c index cad4f9a015d0..44e42f474d6b 100644 --- a/test/battle/move_effect/tera_starstorm.c +++ b/test/battle/move_effect/tera_starstorm.c @@ -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); From abae73497e755a9ae7d5902968e5fb2638aff6f0 Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Fri, 7 Jun 2024 10:43:58 -0700 Subject: [PATCH 28/33] fixed last batch of tests --- src/battle_main.c | 8 ++++---- test/battle/ability/ate_abilities.c | 2 +- test/battle/gimmick/zmove.c | 18 +++++++++++++++++- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/battle_main.c b/src/battle_main.c index 8ce9ea7f570c..785a3a778663 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -5670,7 +5670,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,15 +5785,15 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk) attackerAbility = GetBattlerAbility(battlerAtk); if (gMovesInfo[move].type == TYPE_NORMAL && TrySetAteType(move, battlerAtk, attackerAbility) - && GetActiveGimmick(battlerAtk) != GIMMICK_DYNAMAX - && GetActiveGimmick(battlerAtk) != GIMMICK_Z_MOVE) + && GetActiveGimmick(battlerAtk) != GIMMICK_DYNAMAX) { 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 (GetActiveGimmick(battlerAtk) != GIMMICK_DYNAMAX) diff --git a/test/battle/ability/ate_abilities.c b/test/battle/ability/ate_abilities.c index 555fefca74bb..a866ea1f616a 100644 --- a/test/battle/ability/ate_abilities.c +++ b/test/battle/ability/ate_abilities.c @@ -62,7 +62,7 @@ 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, gimmick: GIMMICK_MEGA); } + TURN { MOVE(opponent, MOVE_TACKLE); } } SCENE { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponent); ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); diff --git a/test/battle/gimmick/zmove.c b/test/battle/gimmick/zmove.c index 3a682135202b..dbaa4488d560 100644 --- a/test/battle/gimmick/zmove.c +++ b/test/battle/gimmick/zmove.c @@ -21,7 +21,7 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Z-Moves do not retain priority") SINGLE_BATTLE_TEST("(Z-MOVE) Z-Moves are not affected by -ate abilities") { GIVEN { - PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_REFRIGERATE); Item(ITEM_NORMALIUM_Z); } + PLAYER(SPECIES_AURORUS) { Ability(ABILITY_REFRIGERATE); Item(ITEM_NORMALIUM_Z); } OPPONENT(SPECIES_SWELLOW); } WHEN { TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_Z_MOVE); } @@ -32,12 +32,28 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Z-Moves are not affected by -ate abilities") } } +SINGLE_BATTLE_TEST("(Z-MOVE) Z-Moves are affected by Ion Deluge") +{ + GIVEN { + 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_PROTECT].effect == EFFECT_PROTECT); PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMALIUM_Z); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { From 4bf0fdd2a4f924c9c35d803c696120cd2499e3f0 Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Fri, 7 Jun 2024 13:11:58 -0700 Subject: [PATCH 29/33] fixed -ate mega test again --- test/battle/ability/ate_abilities.c | 1 - 1 file changed, 1 deletion(-) diff --git a/test/battle/ability/ate_abilities.c b/test/battle/ability/ate_abilities.c index a866ea1f616a..203452cc0152 100644 --- a/test/battle/ability/ate_abilities.c +++ b/test/battle/ability/ate_abilities.c @@ -64,7 +64,6 @@ SINGLE_BATTLE_TEST("Refrigerate turns a normal type move into Ice") } WHEN { 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!"); } From e23f2b03b5be7d266a69a4abf1c3688a2cc03a19 Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Sun, 9 Jun 2024 18:31:09 -0700 Subject: [PATCH 30/33] code review --- include/battle_controllers.h | 2 +- include/battle_gimmick.h | 10 +++--- include/battle_interface.h | 46 +++++++++++++------------- include/config.h | 2 +- include/data.h | 3 +- src/battle_ai_util.c | 10 +++--- src/battle_controller_opponent.c | 6 ++-- src/battle_controller_player.c | 3 +- src/battle_controller_player_partner.c | 6 ++-- src/battle_dynamax.c | 9 ++--- src/battle_gimmick.c | 31 +++++------------ src/battle_main.c | 4 +-- src/battle_script_commands.c | 6 ++-- src/battle_terastal.c | 15 +++++---- src/battle_util.c | 15 +++++---- src/battle_z_move.c | 5 +-- src/data/graphics/gimmicks.h | 3 +- test/battle/gimmick/dynamax.c | 3 +- test/battle/gimmick/zmove.c | 34 +++++++++++++++++-- test/test_runner_battle.c | 2 +- tools/trainerproc/main.c | 2 +- 21 files changed, 116 insertions(+), 101 deletions(-) diff --git a/include/battle_controllers.h b/include/battle_controllers.h index e50fa5158e3b..a670113bc791 100644 --- a/include/battle_controllers.h +++ b/include/battle_controllers.h @@ -98,7 +98,7 @@ enum { // Special return values in gBattleBufferB from Battle Controller functions. #define RET_VALUE_LEVELED_UP 11 -#define RET_GIMMICK (1 << 7) +#define RET_GIMMICK (1 << 7) struct UnusedControllerStruct { diff --git a/include/battle_gimmick.h b/include/battle_gimmick.h index d8b3e51f3e97..de1352a611e1 100644 --- a/include/battle_gimmick.h +++ b/include/battle_gimmick.h @@ -14,11 +14,11 @@ enum Gimmick 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; + 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); }; diff --git a/include/battle_interface.h b/include/battle_interface.h index 0002d744d1b3..6635298dc102 100644 --- a/include/battle_interface.h +++ b/include/battle_interface.h @@ -52,34 +52,34 @@ enum #define TAG_MEGA_INDICATOR_TILE 0xD778 #define TAG_ALPHA_INDICATOR_TILE 0xD779 #define TAG_OMEGA_INDICATOR_TILE 0xD77A -#define TAG_DYNAMAX_INDICATOR_TILE 0xD77E - -#define TAG_NORMAL_INDICATOR_TILE 0xD77F -#define TAG_FIGHTING_INDICATOR_TILE 0xD780 -#define TAG_FLYING_INDICATOR_TILE 0xD781 -#define TAG_POISON_INDICATOR_TILE 0xD782 -#define TAG_GROUND_INDICATOR_TILE 0xD783 -#define TAG_ROCK_INDICATOR_TILE 0xD784 -#define TAG_BUG_INDICATOR_TILE 0xD785 -#define TAG_GHOST_INDICATOR_TILE 0xD786 -#define TAG_STEEL_INDICATOR_TILE 0xD787 +#define TAG_DYNAMAX_INDICATOR_TILE 0xD77B + +#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_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_TERA_INDICATOR_PAL 0xD77E +#define TAG_TERA_INDICATOR_PAL 0xD77A enum { diff --git a/include/config.h b/include/config.h index 46aace77b7e9..0f1b64bd13fe 100644 --- a/include/config.h +++ b/include/config.h @@ -6,7 +6,7 @@ // still has them in the ROM. This is because the developers forgot // to define NDEBUG before release, however this has been changed as // Ruby's actual debug build does not use the AGBPrint features. -// #define NDEBUG +#define NDEBUG // To enable printf debugging, comment out "#define NDEBUG". This allows // the various AGBPrint functions to be used. (See include/gba/isagbprint.h). diff --git a/include/data.h b/include/data.h index 5cb0429da366..60824cf9866f 100644 --- a/include/data.h +++ b/include/data.h @@ -70,10 +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; - u8 useGimmick:4; + u8 padding:2; }; #define TRAINER_PARTY(partyArray) partyArray, .partySize = ARRAY_COUNT(partyArray) diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index f09d2c988376..cc0a76b4e720 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -3745,20 +3745,20 @@ bool32 ShouldUseZMove(u32 battlerAtk, u32 battlerDef, u32 chosenMove) if (IsViableZMove(battlerAtk, chosenMove)) { u8 effectiveness; - u32 zmove = GetUsableZMove(battlerAtk, chosenMove); + u32 zMove = GetUsableZMove(battlerAtk, chosenMove); if (gBattleMons[battlerDef].ability == ABILITY_DISGUISE - && !gMovesInfo[zmove].ignoresTargetAbility + && !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 - && !gMovesInfo[zmove].ignoresTargetAbility + && !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(zmove)) + if (IS_MOVE_STATUS(chosenMove) && !IS_MOVE_STATUS(zMove)) return FALSE; - else if (!IS_MOVE_STATUS(chosenMove) && IS_MOVE_STATUS(zmove)) + else if (!IS_MOVE_STATUS(chosenMove) && IS_MOVE_STATUS(zMove)) return FALSE; if (!IS_MOVE_STATUS(chosenMove) && AI_CalcDamageSaveBattlers(chosenMove, battlerAtk, battlerDef, &effectiveness, FALSE, DMG_ROLL_DEFAULT) >= gBattleMons[battlerDef].hp) diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index 88ad216ac569..a2dd8f8f1a92 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -554,15 +554,13 @@ static void OpponentHandleChooseMove(u32 battler) } // 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]))) + && !(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; } diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index bee2422437a8..6f5e644b72f0 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -780,14 +780,13 @@ static void HandleInputChooseMove(u32 battler) else if ((JOY_NEW(B_BUTTON) || gPlayerDpadHoldFrames > 59) && !sDescriptionSubmenu) { PlaySE(SE_SELECT); + gBattleStruct->gimmick.playerSelect = FALSE; if (gBattleStruct->zmove.viewing) { - gBattleStruct->gimmick.playerSelect = FALSE; ReloadMoveNames(battler); } else { - gBattleStruct->gimmick.playerSelect = FALSE; BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, 0xFFFF); HideGimmickTriggerSprite(); PlayerBufferExecCompleted(battler); diff --git a/src/battle_controller_player_partner.c b/src/battle_controller_player_partner.c index 3a2b878b910d..5f13872f496e 100644 --- a/src/battle_controller_player_partner.c +++ b/src/battle_controller_player_partner.c @@ -370,15 +370,13 @@ static void PlayerPartnerHandleChooseMove(u32 battler) } // 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]))) + && !(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); diff --git a/src/battle_dynamax.c b/src/battle_dynamax.c index a0a657db7310..ccfe3efa898a 100644 --- a/src/battle_dynamax.c +++ b/src/battle_dynamax.c @@ -77,12 +77,13 @@ bool32 CanDynamax(u32 battler) u16 holdEffect = GetBattlerHoldEffect(battler, FALSE); // Check if Dynamax battle flag is set. This needs to be defined in include/config/battle.h - if ((B_FLAG_DYNAMAX_BATTLE == 0 || (B_FLAG_DYNAMAX_BATTLE != 0 && !FlagGet(B_FLAG_DYNAMAX_BATTLE))) && !TESTING) + if (!TESTING && (B_FLAG_DYNAMAX_BATTLE == 0 || (B_FLAG_DYNAMAX_BATTLE != 0 && !FlagGet(B_FLAG_DYNAMAX_BATTLE)))) return FALSE; // Check if Player has a Dynamax Band. - if ((GetBattlerPosition(battler) == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && GetBattlerPosition(battler) == B_POSITION_PLAYER_RIGHT)) - && !CheckBagHasItem(ITEM_DYNAMAX_BAND, 1) && !TESTING) + if (!TESTING && (GetBattlerPosition(battler) == B_POSITION_PLAYER_LEFT + || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && GetBattlerPosition(battler) == B_POSITION_PLAYER_RIGHT)) + && !CheckBagHasItem(ITEM_DYNAMAX_BAND, 1)) return FALSE; // Check if species isn't allowed to Dynamax. @@ -104,7 +105,7 @@ bool32 CanDynamax(u32 battler) return FALSE; // Check if battler is holding a Z-Crystal or Mega Stone. - if ((holdEffect == HOLD_EFFECT_Z_CRYSTAL || holdEffect == HOLD_EFFECT_MEGA_STONE) && !TESTING) + 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. diff --git a/src/battle_gimmick.c b/src/battle_gimmick.c index 1a81d324735f..053e045ba854 100644 --- a/src/battle_gimmick.c +++ b/src/battle_gimmick.c @@ -72,7 +72,7 @@ bool32 ShouldTrainerBattlerUseGimmick(u32 battler, enum Gimmick gimmick) } // 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)) + && !((gBattleTypeFlags & BATTLE_TYPE_MULTI) && battler == B_POSITION_PLAYER_RIGHT)) { return TRUE; } @@ -100,9 +100,7 @@ bool32 HasTrainerUsedGimmick(u32 battler, enum Gimmick gimmick) } // 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. @@ -137,28 +135,26 @@ void CreateGimmickTriggerSprite(u32 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) + || 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); + 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[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; @@ -263,17 +259,12 @@ void LoadIndicatorSpritesGfx(void) 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); @@ -323,17 +314,11 @@ 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) diff --git a/src/battle_main.c b/src/battle_main.c index 785a3a778663..3de32d4a48f5 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -5784,8 +5784,8 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk) attackerAbility = GetBattlerAbility(battlerAtk); if (gMovesInfo[move].type == TYPE_NORMAL - && TrySetAteType(move, battlerAtk, attackerAbility) - && GetActiveGimmick(battlerAtk) != GIMMICK_DYNAMAX) + && TrySetAteType(move, battlerAtk, attackerAbility) + && GetActiveGimmick(battlerAtk) != GIMMICK_DYNAMAX) { gBattleStruct->ateBoost[battlerAtk] = 1; } diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index b7cc8981081e..01a2c2582f83 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1364,7 +1364,8 @@ static void Cmd_attackcanceler(void) } // Z-moves and Max Moves bypass protection, but deal reduced damage (factored in AccumulateOtherModifiers) - if ((IsZMove(gCurrentMove) || IsMaxMove(gCurrentMove)) + if ((IsZMove(gCurrentMove) + || IsMaxMove(gCurrentMove)) && IS_BATTLER_PROTECTED(gBattlerTarget)) { BattleScriptPush(cmd->nextInstr); @@ -9825,7 +9826,8 @@ static void Cmd_various(void) || gMovesInfo[move].instructBanned || gBattleMoveEffects[gMovesInfo[move].effect].twoTurnEffect || (GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX) - || IsZMove(move) || IsMaxMove(move)) + || IsZMove(move) + || IsMaxMove(move)) { gBattlescriptCurrInstr = cmd->failInstr; } diff --git a/src/battle_terastal.c b/src/battle_terastal.c index decad5580344..3f158100cea5 100644 --- a/src/battle_terastal.c +++ b/src/battle_terastal.c @@ -64,13 +64,14 @@ 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)))))) - && !TESTING) + 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))) { - return FALSE; + if (!CheckBagHasItem(ITEM_TERA_ORB, 1)) + return FALSE; + if (B_FLAG_TERA_ORB_CHARGED != 0 && !FlagGet(B_FLAG_TERA_ORB_CHARGED)) + return FALSE; } // Check if Trainer has already Terastallized. @@ -86,7 +87,7 @@ bool32 CanTerastallize(u32 battler) return FALSE; // Check if battler is holding a Z-Crystal or Mega Stone. - if ((holdEffect == HOLD_EFFECT_Z_CRYSTAL || holdEffect == HOLD_EFFECT_MEGA_STONE) && !TESTING) + if (!TESTING && (holdEffect == HOLD_EFFECT_Z_CRYSTAL || holdEffect == HOLD_EFFECT_MEGA_STONE)) // tests make this check already return FALSE; // Every check passed! diff --git a/src/battle_util.c b/src/battle_util.c index 008dc30e502c..9548646f6a98 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -204,9 +204,8 @@ void HandleAction_UseMove(void) gBattleStruct->categoryOverride = gMovesInfo[gCurrentMove].category; gCurrentMove = gChosenMove = GetUsableZMove(gBattlerAttacker, gCurrentMove); } - - // check max move used - if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_DYNAMAX) + // check Max Move used + else if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_DYNAMAX) { gBattleStruct->categoryOverride = gMovesInfo[gCurrentMove].category; gCurrentMove = gChosenMove = GetMaxMove(gBattlerAttacker, gCurrentMove); @@ -10457,8 +10456,9 @@ bool32 CanMegaEvolve(u32 battler) 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) && !TESTING) + 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 has already Mega Evolved. @@ -10494,8 +10494,9 @@ bool32 CanUltraBurst(u32 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) && !TESTING) + 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 has already Ultra Bursted. diff --git a/src/battle_z_move.c b/src/battle_z_move.c index c3dc775e2155..c000c3580851 100644 --- a/src/battle_z_move.c +++ b/src/battle_z_move.c @@ -115,8 +115,9 @@ bool32 CanUseZMove(u32 battler) u32 holdEffect = GetBattlerHoldEffect(battler, FALSE); // 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) && !TESTING) + 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 diff --git a/src/data/graphics/gimmicks.h b/src/data/graphics/gimmicks.h index 9c1926425077..08c1ffe4c55d 100644 --- a/src/data/graphics/gimmicks.h +++ b/src/data/graphics/gimmicks.h @@ -23,7 +23,8 @@ static const struct SpritePalette sSpritePalette_BurstTrigger = {sBurstTriggerPa 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 = { +static const struct OamData sOamData_GimmickTrigger = +{ .y = 0, .affineMode = 0, .objMode = 0, diff --git a/test/battle/gimmick/dynamax.c b/test/battle/gimmick/dynamax.c index f7231d2fd380..56fd2e25eaa8 100644 --- a/test/battle/gimmick/dynamax.c +++ b/test/battle/gimmick/dynamax.c @@ -1484,8 +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, gimmick: GIMMICK_DYNAMAX); - 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); diff --git a/test/battle/gimmick/zmove.c b/test/battle/gimmick/zmove.c index dbaa4488d560..bde241bfd72a 100644 --- a/test/battle/gimmick/zmove.c +++ b/test/battle/gimmick/zmove.c @@ -6,6 +6,7 @@ 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 { @@ -21,6 +22,8 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Z-Moves do not retain priority") 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 { @@ -35,6 +38,7 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Z-Moves are not affected by -ate abilities") 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); @@ -53,6 +57,7 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Z-Moves deal 1/4 damage through protect", s16 damag 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); @@ -91,6 +96,7 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Z_EFFECT_RESET_STATS clears a battler's negative st 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); @@ -112,6 +118,7 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Z_EFFECT_BOOST_CRITS raises a battler's critical hi { 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); @@ -129,6 +136,7 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Z_EFFECT_BOOST_CRITS raises a battler's critical hi 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); @@ -149,6 +157,7 @@ DOUBLE_BATTLE_TEST("(Z-MOVE) Z_EFFECT_FOLLOW_ME redirects attacks to the user") 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); } @@ -172,6 +181,10 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Z_EFFECT_CURSE activates Z_EFFECT_RECOVER_HP or Z_E 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); @@ -205,6 +218,7 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Z_EFFECT_CURSE activates Z_EFFECT_RECOVER_HP or Z_E 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); @@ -226,6 +240,7 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Z-Mirror Move raises the user's attack by two stage 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); @@ -244,6 +259,7 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Z-Mirror Move raises the user's attack by two stage 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); @@ -265,6 +281,7 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Z-Me First raises the user's speed by two stages an 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); @@ -296,6 +313,7 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Z-Nature Power transforms into different Z-Moves ba 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 { @@ -310,8 +328,14 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Z-Nature Power transforms into different Z-Moves ba 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 { - PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMALIUM_Z); } + 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); } @@ -330,6 +354,7 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Z-Weather Ball transforms into different Z-Moves ba 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 { @@ -345,6 +370,7 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Z-Weather Ball transforms into different Z-Moves ba 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 { @@ -359,6 +385,7 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Z-Sleep Talk transforms a used non-status move into 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 { @@ -374,7 +401,7 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Z-Sleep Talk turns Weather Ball into Breakneck Blit DOUBLE_BATTLE_TEST("(Z-MOVE) Instruct fails if the target last used a Z-Move") { GIVEN { - ASSUME(gMovesInfo[MOVE_DESTINY_BOND].zMove.effect == Z_EFFECT_FOLLOW_ME); + ASSUME(gMovesInfo[MOVE_TACKLE].type == TYPE_NORMAL); PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMALIUM_Z); } PLAYER(SPECIES_WYNAUT); OPPONENT(SPECIES_WOBBUFFET); @@ -393,7 +420,7 @@ DOUBLE_BATTLE_TEST("(Z-MOVE) Instruct fails if the target last used a Z-Move") 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_DESTINY_BOND].zMove.effect == Z_EFFECT_FOLLOW_ME); + ASSUME(gMovesInfo[MOVE_TACKLE].type == TYPE_NORMAL); PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_DANCER); Item(ITEM_NORMALIUM_Z); } PLAYER(SPECIES_WYNAUT); OPPONENT(SPECIES_WOBBUFFET); @@ -417,6 +444,7 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Light That Burns the Sky uses the battler's highest 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 { diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 9f43845e4901..b652c7d565f6 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -2126,7 +2126,7 @@ void MoveGetIdAndSlot(s32 battlerId, struct MoveContext *ctx, u32 *moveId, u32 * && ctx->gimmick == GIMMICK_Z_MOVE), "Cannot use multiple gimmicks on the same battler"); DATA.chosenGimmick[side][DATA.currentMonIndexes[battlerId]] = ctx->gimmick; - *moveSlot |= RET_GIMMICK; + *moveSlot |= RET_GIMMICK; } } diff --git a/tools/trainerproc/main.c b/tools/trainerproc/main.c index 41ffb8bcef10..1f9c1e23908f 100644 --- a/tools/trainerproc/main.c +++ b/tools/trainerproc/main.c @@ -1827,7 +1827,7 @@ static void fprint_trainers(const char *output_path, FILE *f, struct Parsed *par 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 = "); From ea80bf65cb6272417d10a52d5452a03d58ba06c6 Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Sun, 9 Jun 2024 19:38:17 -0700 Subject: [PATCH 31/33] code review pt.2 --- src/battle_controller_opponent.c | 2 ++ src/battle_controller_player_partner.c | 2 ++ src/battle_gimmick.c | 2 ++ tools/trainerproc/main.c | 1 - 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index a2dd8f8f1a92..a5dc6f3af137 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -560,7 +560,9 @@ static void OpponentHandleChooseMove(u32 battler) BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_GIMMICK) | (gBattlerTarget << 8)); } else + { BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (gBattlerTarget << 8)); + } } break; } diff --git a/src/battle_controller_player_partner.c b/src/battle_controller_player_partner.c index 5f13872f496e..a60afb328589 100644 --- a/src/battle_controller_player_partner.c +++ b/src/battle_controller_player_partner.c @@ -376,7 +376,9 @@ static void PlayerPartnerHandleChooseMove(u32 battler) BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_GIMMICK) | (gBattlerTarget << 8)); } else + { BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (gBattlerTarget << 8)); + } } PlayerPartnerBufferExecCompleted(battler); diff --git a/src/battle_gimmick.c b/src/battle_gimmick.c index 053e045ba854..995c68f2ae2c 100644 --- a/src/battle_gimmick.c +++ b/src/battle_gimmick.c @@ -100,7 +100,9 @@ bool32 HasTrainerUsedGimmick(u32 battler, enum Gimmick gimmick) } // 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. diff --git a/tools/trainerproc/main.c b/tools/trainerproc/main.c index 1f9c1e23908f..52df550bd72d 100644 --- a/tools/trainerproc/main.c +++ b/tools/trainerproc/main.c @@ -1826,7 +1826,6 @@ static void fprint_trainers(const char *output_path, FILE *f, struct Parsed *par { fprintf(f, " .useGimmick = GIMMICK_DYNAMAX,\n"); } - else if (pokemon->tera_type_line) { fprintf(f, "#line %d\n", pokemon->tera_type_line); From 1eb200063cc85797d51c3f67a8b03419bd9b9d41 Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Wed, 12 Jun 2024 21:32:21 -0700 Subject: [PATCH 32/33] tweaked CanTera again --- src/battle_terastal.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/battle_terastal.c b/src/battle_terastal.c index 3f158100cea5..ff01ee2597d1 100644 --- a/src/battle_terastal.c +++ b/src/battle_terastal.c @@ -64,12 +64,13 @@ bool32 CanTerastallize(u32 battler) u32 holdEffect = GetBattlerHoldEffect(battler, FALSE); // Check if Player has Tera Orb and has charge. - 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 (!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 (!CheckBagHasItem(ITEM_TERA_ORB, 1)) - return FALSE; if (B_FLAG_TERA_ORB_CHARGED != 0 && !FlagGet(B_FLAG_TERA_ORB_CHARGED)) return FALSE; } From d434657b61874529f0b37fb3501a882c5d3fea18 Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Fri, 14 Jun 2024 19:19:09 -0700 Subject: [PATCH 33/33] dynamax flag only required for player --- src/battle_dynamax.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/battle_dynamax.c b/src/battle_dynamax.c index ccfe3efa898a..96a1aa512842 100644 --- a/src/battle_dynamax.c +++ b/src/battle_dynamax.c @@ -76,15 +76,15 @@ bool32 CanDynamax(u32 battler) u16 species = gBattleMons[battler].species; u16 holdEffect = GetBattlerHoldEffect(battler, FALSE); - // Check if Dynamax battle flag is set. This needs to be defined in include/config/battle.h - if (!TESTING && (B_FLAG_DYNAMAX_BATTLE == 0 || (B_FLAG_DYNAMAX_BATTLE != 0 && !FlagGet(B_FLAG_DYNAMAX_BATTLE)))) - return FALSE; - // Check if Player has a Dynamax Band. if (!TESTING && (GetBattlerPosition(battler) == B_POSITION_PLAYER_LEFT - || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && GetBattlerPosition(battler) == B_POSITION_PLAYER_RIGHT)) - && !CheckBagHasItem(ITEM_DYNAMAX_BAND, 1)) - return FALSE; + || (!(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