From c3e31af5f3f9577c6726cc006a9cffea8110f383 Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Wed, 9 Aug 2023 22:12:26 -0500 Subject: [PATCH 01/12] Ultra burst trigger --- asm/macros/battle_script.inc | 5 + data/battle_anim_scripts.s | 33 ++++ data/battle_scripts_1.s | 15 ++ include/battle.h | 10 + include/battle_controllers.h | 2 + include/battle_interface.h | 7 + include/battle_scripts.h | 1 + include/battle_util.h | 2 + include/constants/battle_anim.h | 1 + include/constants/battle_script_commands.h | 1 + include/constants/battle_string_ids.h | 4 +- include/constants/form_change_types.h | 5 + include/constants/pokemon.h | 1 + src/battle_anim.c | 1 + src/battle_controller_opponent.c | 2 + src/battle_controller_player.c | 23 ++- src/battle_controller_player_partner.c | 2 + src/battle_interface.c | 183 ++++++++++++++++++ src/battle_main.c | 24 ++- src/battle_message.c | 5 + src/battle_script_commands.c | 27 ++- src/battle_util.c | 54 +++++- src/data/pokemon/form_change_table_pointers.h | 2 + src/data/pokemon/form_change_tables.h | 8 + src/data/pokemon/species_info.h | 2 +- test/test_runner_battle.c | 3 + 26 files changed, 403 insertions(+), 20 deletions(-) diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 275cc40a8..cd90d9b5a 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1643,6 +1643,11 @@ .byte \case .endm + .macro handleultraburst battler:req, case:req + various \battler, VARIOUS_HANDLE_ULTRA_BURST + .byte \case + .endm + .macro handleformchange battler:req, case:req various \battler, VARIOUS_HANDLE_FORM_CHANGE .byte \case diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index cef036f8b..825e36c38 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -947,6 +947,7 @@ gBattleAnims_General:: .4byte General_ZMoveActivate @ B_ANIM_ZMOVE_ACTIVATE .4byte General_AffectionHangedOn @ B_ANIM_AFFECTION_HANGED_ON .4byte General_Snow @ B_ANIM_SNOW_CONTINUES + .4byte General_UltraBurst @ B_ANIM_ULTRA_BURST .align 2 gBattleAnims_Special:: @@ -27002,6 +27003,38 @@ General_PrimalReversion_Omega: blendoff end +General_UltraBurst:: + loadspritegfx ANIM_TAG_SPARK_2 @spark + loadspritegfx ANIM_TAG_LEAF @green + loadspritegfx ANIM_TAG_ELECTRIC_ORBS @charge particles + loadspritegfx ANIM_TAG_CIRCLE_OF_LIGHT @psycho boost + monbg ANIM_ATTACKER + setalpha 12, 8 + createvisualtask AnimTask_BlendBattleAnimPal, 0xa, (F_PAL_BG | F_PAL_ADJACENT), 0x2, 0x0, 0xF, 0x0000 + waitforvisualfinish + createvisualtask AnimTask_ElectricChargingParticles, 2, ANIM_ATTACKER, 60, 2, 12 @ charge particles to attacker + delay 0x1e + loopsewithpan SE_M_CHARGE, SOUND_PAN_ATTACKER, 0xe, 0xa + createsprite gSuperpowerOrbSpriteTemplate, ANIM_TARGET, 3, 0x0 + call LightThatBurnsTheSkyGreenSparks + call LightThatBurnsTheSkyGreenSparks + call LightThatBurnsTheSkyGreenSparks + call LightThatBurnsTheSkyGreenSparks + call LightThatBurnsTheSkyGreenSparks + call LightThatBurnsTheSkyGreenSparks + call LightThatBurnsTheSkyGreenSparks + call LightThatBurnsTheSkyGreenSparks + call LightThatBurnsTheSkyGreenSparks + delay 0xe + createvisualtask AnimTask_TransformMon, 2, 1, 0 + createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 16, 0, RGB_WHITEALPHA + createvisualtask AnimTask_HorizontalShake, 5, ANIM_TARGET, 5, 14 + waitforvisualfinish + createvisualtask SoundTask_PlayNormalCry, 0 + clearmonbg ANIM_ATK_PARTNER + blendoff + end + General_AffectionHangedOn:: loadspritegfx ANIM_TAG_RED_HEART loopsewithpan SE_M_CHARM, SOUND_PAN_ATTACKER, 12, 3 diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 056aaa673..d46e72e41 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -7818,6 +7818,21 @@ BattleScript_PrimalReversionRet:: switchinabilities BS_ATTACKER return +BattleScript_UltraBurst:: + printstring STRINGID_EMPTYSTRING3 + trytrainerslidezmovemsg BS_ATTACKER + printstring STRINGID_ULTRABURSTREACTING + waitmessage B_WAIT_TIME_LONG + setbyte gIsCriticalHit, 0 + handleultraburst BS_ATTACKER, 0 + playanimation BS_ATTACKER, B_ANIM_ULTRA_BURST + waitanimation + handleultraburst BS_ATTACKER, 1 + printstring STRINGID_ULTRABURSTCOMPLETED + waitmessage B_WAIT_TIME_LONG + switchinabilities BS_ATTACKER + end3 + BattleScript_AttackerFormChange:: pause 5 copybyte gBattlerAbility, gBattlerAttacker diff --git a/include/battle.h b/include/battle.h index 00893d105..163245184 100644 --- a/include/battle.h +++ b/include/battle.h @@ -486,6 +486,15 @@ struct MegaEvolutionData 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; @@ -614,6 +623,7 @@ struct BattleStruct u8 abilityPopUpSpriteIds[MAX_BATTLERS_COUNT][2]; // two per battler bool8 throwingPokeBall; struct MegaEvolutionData mega; + struct UltraBurstData burst; struct ZMoveData zmove; const u8 *trainerSlideMsg; bool8 trainerSlideLowHpMsgDone; diff --git a/include/battle_controllers.h b/include/battle_controllers.h index e1ab98b0c..16c67ca01 100644 --- a/include/battle_controllers.h +++ b/include/battle_controllers.h @@ -99,6 +99,7 @@ enum { // Special return values in gBattleBufferB from Battle Controller functions. #define RET_VALUE_LEVELED_UP 11 #define RET_MEGA_EVOLUTION 0x80 +#define RET_ULTRA_BURST 0x70 struct UnusedControllerStruct { @@ -129,6 +130,7 @@ struct ChooseMoveStruct u8 monType2; u8 monType3; struct MegaEvolutionData mega; + struct UltraBurstData burst; struct ZMoveData zmove; }; diff --git a/include/battle_interface.h b/include/battle_interface.h index e2937748a..2618dc4a9 100644 --- a/include/battle_interface.h +++ b/include/battle_interface.h @@ -53,11 +53,13 @@ enum #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_MEGA_TRIGGER_PAL 0xD777 #define TAG_MEGA_INDICATOR_PAL 0xD778 #define TAG_ALPHA_OMEGA_INDICATOR_PAL 0xD779 // Alpha and Omega 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 enum { @@ -91,6 +93,11 @@ 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); u8 CreatePartyStatusSummarySprites(u8 battler, struct HpAndStatus *partyInfo, bool8 skipPlayer, bool8 isBattleStart); void Task_HidePartyStatusSummary(u8 taskId); diff --git a/include/battle_scripts.h b/include/battle_scripts.h index bcc8632a4..98ea8ba15 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -472,6 +472,7 @@ extern const u8 BattleScript_SpikesActivates[]; extern const u8 BattleScript_BerserkGeneRet[]; extern const u8 BattleScript_TargetFormChangeWithStringNoPopup[]; extern const u8 BattleScript_DefDown[]; +extern const u8 BattleScript_UltraBurst[]; // zmoves extern const u8 BattleScript_ZMoveActivateDamaging[]; diff --git a/include/battle_util.h b/include/battle_util.h index 9de5d6ded..a82010e01 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -177,8 +177,10 @@ uq4_12_t GetTypeModifier(u8 atkType, u8 defType); s32 GetStealthHazardDamage(u8 hazardType, u8 battlerId); s32 GetStealthHazardDamageByTypesAndHP(u8 hazardType, u8 type1, u8 type2, u32 maxHp); bool32 CanMegaEvolve(u8 battlerId); +bool32 CanUltraBurst(u8 battlerId); bool32 IsBattlerMegaEvolved(u8 battlerId); bool32 IsBattlerPrimalReverted(u8 battlerId); +bool32 IsBattlerUltraBursted(u8 battlerId); u16 GetBattleFormChangeTargetSpecies(u8 battlerId, u16 method); bool32 TryBattleFormChange(u8 battlerId, u16 method); bool32 DoBattlersShareType(u32 battler1, u32 battler2); diff --git a/include/constants/battle_anim.h b/include/constants/battle_anim.h index 29c39b689..e70eba0fd 100644 --- a/include/constants/battle_anim.h +++ b/include/constants/battle_anim.h @@ -548,6 +548,7 @@ #define B_ANIM_ZMOVE_ACTIVATE 34 // Using Z Moves #define B_ANIM_AFFECTION_HANGED_ON 35 #define B_ANIM_SNOW_CONTINUES 36 +#define B_ANIM_ULTRA_BURST 37 // special animations table (gBattleAnims_Special) #define B_ANIM_LVL_UP 0 diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index 387800da4..20e6d7e45 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -257,6 +257,7 @@ #define VARIOUS_TRY_REVIVAL_BLESSING 165 #define VARIOUS_TRY_TRAINER_SLIDE_MSG_Z_MOVE 166 #define VARIOUS_TRY_TRAINER_SLIDE_MSG_MEGA_EVOLUTION 167 +#define VARIOUS_HANDLE_ULTRA_BURST 168 // Cmd_manipulatedamage #define DMG_CHANGE_SIGN 0 diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 97284d6fe..35d991bb8 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -665,8 +665,10 @@ #define STRINGID_SNOWSTOPPED 663 #define STRINGID_SNOWWARNINGSNOW 664 #define STRINGID_PKMNITEMMELTED 665 +#define STRINGID_ULTRABURSTREACTING 666 +#define STRINGID_ULTRABURSTCOMPLETED 667 -#define BATTLESTRINGS_COUNT 666 +#define BATTLESTRINGS_COUNT 668 // This is the string id that gBattleStringsTable starts with. // String ids before this (e.g. STRINGID_INTROMSG) are not in the table, diff --git a/include/constants/form_change_types.h b/include/constants/form_change_types.h index 81d1b045b..db96b0657 100644 --- a/include/constants/form_change_types.h +++ b/include/constants/form_change_types.h @@ -96,4 +96,9 @@ // param1: ability to check. #define FORM_CHANGE_BATTLE_TURN_END 15 +// Form change that activates when the mon has the defined item. +// If it's on the player's side, it also requires for the player to trigger it by pressing START before selecting a move. +// param1: item to hold. +#define FORM_CHANGE_BATTLE_ULTRA_BURST 16 + #endif // GUARD_CONSTANTS_FORM_CHANGE_TYPES_H diff --git a/include/constants/pokemon.h b/include/constants/pokemon.h index af7178041..3b54f9851 100644 --- a/include/constants/pokemon.h +++ b/include/constants/pokemon.h @@ -327,6 +327,7 @@ #define SPECIES_FLAG_HISUIAN_FORM (1 << 7) #define SPECIES_FLAG_ALL_PERFECT_IVS (1 << 8) #define SPECIES_FLAG_CANNOT_BE_TRADED (1 << 9) +#define SPECIES_FLAG_ULTRA_BURST (1 << 10) #define LEGENDARY_PERFECT_IV_COUNT 3 diff --git a/src/battle_anim.c b/src/battle_anim.c index 5691e21e2..808676c2e 100644 --- a/src/battle_anim.c +++ b/src/battle_anim.c @@ -275,6 +275,7 @@ void LaunchBattleAnimation(u32 animType, u32 animId) case B_ANIM_WISH_HEAL: case B_ANIM_MEGA_EVOLUTION: case B_ANIM_PRIMAL_REVERSION: + case B_ANIM_ULTRA_BURST: case B_ANIM_GULP_MISSILE: sAnimHideHpBoxes = TRUE; break; diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index a786dafa6..ab05a7f33 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -1600,6 +1600,8 @@ static void OpponentHandleChooseMove(void) QueueZMove(gActiveBattler, chosenMove); if (CanMegaEvolve(gActiveBattler)) // If opponent can mega evolve, do it. BtlController_EmitTwoReturnValues(BUFFER_B, 10, (chosenMoveId) | (RET_MEGA_EVOLUTION) | (gBattlerTarget << 8)); + else if (CanUltraBurst(gActiveBattler)) + BtlController_EmitTwoReturnValues(BUFFER_B, 10, (chosenMoveId) | (RET_ULTRA_BURST) | (gBattlerTarget << 8)); else BtlController_EmitTwoReturnValues(BUFFER_B, 10, (chosenMoveId) | (gBattlerTarget << 8)); } diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index eb47db444..b12da78df 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -376,6 +376,8 @@ static void HandleInputChooseTarget(void) gSprites[gBattlerSpriteIds[gMultiUsePlayerCursor]].callback = SpriteCB_HideAsMoveTarget; if (gBattleStruct->mega.playerSelect) BtlController_EmitTwoReturnValues(BUFFER_B, 10, gMoveSelectionCursor[gActiveBattler] | RET_MEGA_EVOLUTION | (gMultiUsePlayerCursor << 8)); + else if (gBattleStruct->burst.playerSelect) + BtlController_EmitTwoReturnValues(BUFFER_B, 10, gMoveSelectionCursor[gActiveBattler] | RET_ULTRA_BURST | (gMultiUsePlayerCursor << 8)); else BtlController_EmitTwoReturnValues(BUFFER_B, 10, gMoveSelectionCursor[gActiveBattler] | (gMultiUsePlayerCursor << 8)); EndBounceEffect(gMultiUsePlayerCursor, BOUNCE_HEALTHBOX); @@ -534,9 +536,11 @@ static void HandleInputShowEntireFieldTargets(void) HideAllTargets(); if (gBattleStruct->mega.playerSelect) BtlController_EmitTwoReturnValues(BUFFER_B, 10, gMoveSelectionCursor[gActiveBattler] | RET_MEGA_EVOLUTION | (gMultiUsePlayerCursor << 8)); + else if (gBattleStruct->burst.playerSelect) + BtlController_EmitTwoReturnValues(BUFFER_B, 10, gMoveSelectionCursor[gActiveBattler] | RET_ULTRA_BURST | (gMultiUsePlayerCursor << 8)); else BtlController_EmitTwoReturnValues(BUFFER_B, 10, gMoveSelectionCursor[gActiveBattler] | (gMultiUsePlayerCursor << 8)); - HideMegaTriggerSprite(); + HideTriggerSprites(); PlayerBufferExecCompleted(); } else if (JOY_NEW(B_BUTTON) || gPlayerDpadHoldFrames > 59) @@ -562,6 +566,8 @@ static void HandleInputShowTargets(void) HideShownTargets(); if (gBattleStruct->mega.playerSelect) BtlController_EmitTwoReturnValues(BUFFER_B, 10, gMoveSelectionCursor[gActiveBattler] | RET_MEGA_EVOLUTION | (gMultiUsePlayerCursor << 8)); + else if (gBattleStruct->burst.playerSelect) + BtlController_EmitTwoReturnValues(BUFFER_B, 10, gMoveSelectionCursor[gActiveBattler] | RET_ULTRA_BURST | (gMultiUsePlayerCursor << 8)); else BtlController_EmitTwoReturnValues(BUFFER_B, 10, gMoveSelectionCursor[gActiveBattler] | (gMultiUsePlayerCursor << 8)); HideTriggerSprites(); @@ -677,6 +683,8 @@ static void HandleInputChooseMove(void) default: if (gBattleStruct->mega.playerSelect) BtlController_EmitTwoReturnValues(BUFFER_B, 10, gMoveSelectionCursor[gActiveBattler] | RET_MEGA_EVOLUTION | (gMultiUsePlayerCursor << 8)); + else if (gBattleStruct->burst.playerSelect) + BtlController_EmitTwoReturnValues(BUFFER_B, 10, gMoveSelectionCursor[gActiveBattler] | RET_ULTRA_BURST | (gMultiUsePlayerCursor << 8)); else BtlController_EmitTwoReturnValues(BUFFER_B, 10, gMoveSelectionCursor[gActiveBattler] | (gMultiUsePlayerCursor << 8)); HideTriggerSprites(); @@ -713,6 +721,7 @@ static void HandleInputChooseMove(void) else { gBattleStruct->mega.playerSelect = FALSE; + gBattleStruct->burst.playerSelect = FALSE; gBattleStruct->zmove.viable = FALSE; BtlController_EmitTwoReturnValues(BUFFER_B, 10, 0xFFFF); HideTriggerSprites(); @@ -797,6 +806,12 @@ static void HandleInputChooseMove(void) ChangeMegaTriggerSprite(gBattleStruct->mega.triggerSpriteId, gBattleStruct->mega.playerSelect); PlaySE(SE_SELECT); } + else if (CanUltraBurst(gActiveBattler)) + { + gBattleStruct->burst.playerSelect ^= 1; + ChangeBurstTriggerSprite(gBattleStruct->burst.triggerSpriteId, gBattleStruct->burst.playerSelect); + PlaySE(SE_SELECT); + } else if (gBattleStruct->zmove.viable) { // show z move name / info @@ -813,6 +828,7 @@ static void HandleInputChooseMove(void) static void ReloadMoveNames(void) { gBattleStruct->mega.playerSelect = FALSE; + gBattleStruct->burst.playerSelect = FALSE; gBattleStruct->zmove.viewing = FALSE; MoveSelectionDestroyCursorAt(0); MoveSelectionDisplayMoveNames(); @@ -2862,10 +2878,15 @@ static void PlayerHandleChooseMove(void) InitMoveSelectionsVarsAndStrings(); gBattleStruct->mega.playerSelect = FALSE; + gBattleStruct->burst.playerSelect = FALSE; if (!IsMegaTriggerSpriteActive()) gBattleStruct->mega.triggerSpriteId = 0xFF; if (CanMegaEvolve(gActiveBattler)) CreateMegaTriggerSprite(gActiveBattler, 0); + if (!IsBurstTriggerSpriteActive()) + gBattleStruct->burst.triggerSpriteId = 0xFF; + if (CanUltraBurst(gActiveBattler)) + CreateBurstTriggerSprite(gActiveBattler, 0); if (!IsZMoveTriggerSpriteActive()) gBattleStruct->zmove.triggerSpriteId = 0xFF; diff --git a/src/battle_controller_player_partner.c b/src/battle_controller_player_partner.c index 3a4beeca9..0d1bbe03a 100644 --- a/src/battle_controller_player_partner.c +++ b/src/battle_controller_player_partner.c @@ -1539,6 +1539,8 @@ static void PlayerPartnerHandleChooseMove(void) // If partner can mega evolve, do it. if (CanMegaEvolve(gActiveBattler)) BtlController_EmitTwoReturnValues(BUFFER_B, 10, (chosenMoveId) | (RET_MEGA_EVOLUTION) | (gBattlerTarget << 8)); + else if (CanUltraBurst(gActiveBattler)) + BtlController_EmitTwoReturnValues(BUFFER_B, 10, (chosenMoveId) | (RET_ULTRA_BURST) | (gBattlerTarget << 8)); else BtlController_EmitTwoReturnValues(BUFFER_B, 10, (chosenMoveId) | (gBattlerTarget << 8)); diff --git a/src/battle_interface.c b/src/battle_interface.c index 7ac936b2f..281d18e04 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -194,6 +194,7 @@ 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_SetVisibilities(u32 healthboxId, bool32 invisible); static void MegaIndicator_UpdateLevel(u32 healthboxId, u32 level); static void MegaIndicator_CreateSprite(u32 battlerId, u32 healthboxSpriteId); @@ -676,6 +677,64 @@ static const struct SpriteTemplate sSpriteTemplate_MegaTrigger = .callback = SpriteCb_MegaTrigger }; +static const u8 ALIGNED(4) sBurstTriggerGfx[] = INCBIN_U8("graphics/battle_interface/mega_trigger.4bpp"); +static const u16 sBurstTriggerPal[] = INCBIN_U16("graphics/battle_interface/mega_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. @@ -1419,6 +1478,7 @@ void HideMegaTriggerSprite(void) void HideTriggerSprites(void) { HideMegaTriggerSprite(); + HideBurstTriggerSprite(); HideZMoveTriggerSprite(); } @@ -1434,6 +1494,129 @@ void DestroyMegaTriggerSprite(void) #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 != 0xFF) + { + 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 { diff --git a/src/battle_main.c b/src/battle_main.c index ace56189c..86d72bd73 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3186,6 +3186,7 @@ static void BattleStartClearSetData(void) gBattleStruct->arenaLostOpponentMons = 0; gBattleStruct->mega.triggerSpriteId = 0xFF; + gBattleStruct->burst.triggerSpriteId = 0xFF; gBattleStruct->stickyWebUser = 0xFF; gBattleStruct->appearedInBattle = 0; @@ -4350,6 +4351,7 @@ static void HandleTurnActionSelectionState(void) } gBattleStruct->mega.toEvolve &= ~(gBitTable[BATTLE_PARTNER(GetBattlerPosition(gActiveBattler))]); + gBattleStruct->burst.toBurst &= ~(gBitTable[BATTLE_PARTNER(GetBattlerPosition(gActiveBattler))]); gBattleStruct->zmove.toBeUsed[BATTLE_PARTNER(GetBattlerPosition(gActiveBattler))] = MOVE_NONE; BtlController_EmitEndBounceEffect(BUFFER_A); MarkBattlerForControllerExec(gActiveBattler); @@ -4436,11 +4438,13 @@ static void HandleTurnActionSelectionState(void) RecordedBattle_SetBattlerAction(gActiveBattler, gBattleResources->bufferB[gActiveBattler][2]); RecordedBattle_SetBattlerAction(gActiveBattler, gBattleResources->bufferB[gActiveBattler][3]); } - *(gBattleStruct->chosenMovePositions + gActiveBattler) = gBattleResources->bufferB[gActiveBattler][2] & ~RET_MEGA_EVOLUTION; - gChosenMoveByBattler[gActiveBattler] = gBattleMons[gActiveBattler].moves[*(gBattleStruct->chosenMovePositions + gActiveBattler)]; - *(gBattleStruct->moveTarget + gActiveBattler) = gBattleResources->bufferB[gActiveBattler][3]; + gBattleStruct->chosenMovePositions[gActiveBattler] = gBattleResources->bufferB[gActiveBattler][2] & ~(RET_MEGA_EVOLUTION | RET_ULTRA_BURST); + gChosenMoveByBattler[gActiveBattler] = gBattleMons[gActiveBattler].moves[gBattleStruct->chosenMovePositions[gActiveBattler]]; + gBattleStruct->moveTarget[gActiveBattler] = gBattleResources->bufferB[gActiveBattler][3]; if (gBattleResources->bufferB[gActiveBattler][2] & RET_MEGA_EVOLUTION) gBattleStruct->mega.toEvolve |= gBitTable[gActiveBattler]; + else if (gBattleResources->bufferB[gActiveBattler][2] & RET_ULTRA_BURST) + gBattleStruct->burst.toBurst |= gBitTable[gActiveBattler]; gBattleCommunication[gActiveBattler]++; } break; @@ -5054,7 +5058,7 @@ static void PopulateArrayWithBattlers(u8 *battlers) static bool32 TryDoMegaEvosBeforeMoves(void) { - if (!(gHitMarker & HITMARKER_RUN) && gBattleStruct->mega.toEvolve) + if (!(gHitMarker & HITMARKER_RUN) && (gBattleStruct->mega.toEvolve || gBattleStruct->burst.toBurst)) { u32 i; struct Pokemon *party; @@ -5079,6 +5083,18 @@ static bool32 TryDoMegaEvosBeforeMoves(void) BattleScriptExecute(BattleScript_MegaEvolution); return TRUE; } + + if (gBattleStruct->burst.toBurst & gBitTable[megaOrder[i]] + && !(gProtectStructs[megaOrder[i]].noValidMoves)) + { + gActiveBattler = gBattlerAttacker = megaOrder[i]; + gBattleStruct->burst.toBurst &= ~(gBitTable[gActiveBattler]); + gLastUsedItem = gBattleMons[gActiveBattler].item; + party = GetBattlerParty(gActiveBattler); + mon = &party[gBattlerPartyIndexes[gActiveBattler]]; + BattleScriptExecute(BattleScript_UltraBurst); + return TRUE; + } } } diff --git a/src/battle_message.c b/src/battle_message.c index 172f5a11f..811395cb8 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -800,6 +800,9 @@ static const u8 sText_ItemRestoredSpeciesPP[] = _("{B_BUFF1} had its\nPP restore static const u8 sText_AtkTrappedDef[] = _("{B_ATK_NAME_WITH_PREFIX} trapped\nthe {B_DEF_NAME_WITH_PREFIX}!"); static const u8 sText_MirrorHerbCopied[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} used its {B_LAST_ITEM}\nto mirror its opponent's stat changes!"); static const u8 sText_PkmnItemMelted[] = _("{B_ATK_NAME_WITH_PREFIX} corroded\n{B_DEF_NAME_WITH_PREFIX}'s {B_LAST_ITEM}!"); +static const u8 sText_UltraBurstReacting[] = _("Bright light is about to\nburst out of {B_ATK_NAME_WITH_PREFIX}!"); +static const u8 sText_UltraBurstCompleted[] = _("{B_ATK_NAME_WITH_PREFIX} regained its\ntrue power through Ultra Burst!"); + const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = { @@ -1456,6 +1459,8 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = [STRINGID_PKMNFROSTBITEHEALED - BATTLESTRINGS_TABLE_START] = sText_PkmnFrostbiteHealed, [STRINGID_PKMNFROSTBITEHEALED2 - BATTLESTRINGS_TABLE_START] = sText_PkmnFrostbiteHealed2, [STRINGID_PKMNFROSTBITEHEALEDBY - BATTLESTRINGS_TABLE_START] = sText_PkmnFrostbiteHealedBy, + [STRINGID_ULTRABURSTREACTING - BATTLESTRINGS_TABLE_START] = sText_UltraBurstReacting, + [STRINGID_ULTRABURSTCOMPLETED - BATTLESTRINGS_TABLE_START] = sText_UltraBurstCompleted, }; const u16 gTrainerUsedItemStringIds[] = diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index aa5276805..42d427a93 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -4998,7 +4998,8 @@ static void Cmd_playanimation(void) || animId == B_ANIM_ILLUSION_OFF || animId == B_ANIM_FORM_CHANGE || animId == B_ANIM_SUBSTITUTE_FADE - || animId == B_ANIM_PRIMAL_REVERSION) + || animId == B_ANIM_PRIMAL_REVERSION + || animId == B_ANIM_ULTRA_BURST) { BtlController_EmitBattleAnimation(BUFFER_A, animId, *argPtr); MarkBattlerForControllerExec(gActiveBattler); @@ -5049,7 +5050,8 @@ static void Cmd_playanimation_var(void) || *animIdPtr == B_ANIM_ILLUSION_OFF || *animIdPtr == B_ANIM_FORM_CHANGE || *animIdPtr == B_ANIM_SUBSTITUTE_FADE - || *animIdPtr == B_ANIM_PRIMAL_REVERSION) + || *animIdPtr == B_ANIM_PRIMAL_REVERSION + || *animIdPtr == B_ANIM_ULTRA_BURST) { BtlController_EmitBattleAnimation(BUFFER_A, *animIdPtr, *argPtr); MarkBattlerForControllerExec(gActiveBattler); @@ -8593,7 +8595,7 @@ static bool32 CourtChangeSwapSideStatuses(void) SWAP(sideTimerPlayer->stickyWebBattlerSide, sideTimerOpp->stickyWebBattlerSide, temp); } -static void HandleScriptMegaPrimal(u32 caseId, u32 battlerId, bool32 isMega) +static void HandleScriptMegaPrimalBurst(u32 caseId, u32 battlerId, u32 type) { struct Pokemon *party = GetBattlerParty(battlerId); struct Pokemon *mon = &party[gBattlerPartyIndexes[battlerId]]; @@ -8603,13 +8605,15 @@ static void HandleScriptMegaPrimal(u32 caseId, u32 battlerId, bool32 isMega) // Change species. if (caseId == 0) { - if (isMega) + if (type == 0) { if (!TryBattleFormChange(battlerId, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM)) TryBattleFormChange(battlerId, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_MOVE); } - else + else if (type == 1) TryBattleFormChange(battlerId, FORM_CHANGE_BATTLE_PRIMAL_REVERSION); + else + TryBattleFormChange(battlerId, FORM_CHANGE_BATTLE_ULTRA_BURST); PREPARE_SPECIES_BUFFER(gBattleTextBuff1, gBattleMons[battlerId].species); @@ -8622,7 +8626,7 @@ static void HandleScriptMegaPrimal(u32 caseId, u32 battlerId, bool32 isMega) UpdateHealthboxAttribute(gHealthboxSpriteIds[battlerId], mon, HEALTHBOX_ALL); if (side == B_SIDE_OPPONENT) SetBattlerShadowSpriteCallback(battlerId, gBattleMons[battlerId].species); - if (isMega) + if (type == 0) gBattleStruct->mega.alreadyEvolved[position] = TRUE; } } @@ -9680,14 +9684,21 @@ static void Cmd_various(void) case VARIOUS_HANDLE_MEGA_EVO: { VARIOUS_ARGS(u8 case_); - HandleScriptMegaPrimal(cmd->case_, gActiveBattler, TRUE); + HandleScriptMegaPrimalBurst(cmd->case_, gActiveBattler, 0); gBattlescriptCurrInstr = cmd->nextInstr; return; } case VARIOUS_HANDLE_PRIMAL_REVERSION: { VARIOUS_ARGS(u8 case_); - HandleScriptMegaPrimal(cmd->case_, gActiveBattler, FALSE); + HandleScriptMegaPrimalBurst(cmd->case_, gActiveBattler, 1); + gBattlescriptCurrInstr = cmd->nextInstr; + return; + } + case VARIOUS_HANDLE_ULTRA_BURST: + { + VARIOUS_ARGS(u8 case_); + HandleScriptMegaPrimalBurst(cmd->case_, gActiveBattler, 2); gBattlescriptCurrInstr = cmd->nextInstr; return; } diff --git a/src/battle_util.c b/src/battle_util.c index 2f200026b..0ef2752f5 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -1612,7 +1612,7 @@ static bool32 IsBelchPreventingMove(u32 battler, u32 move) u8 TrySetCantSelectMoveBattleScript(void) { u32 limitations = 0; - u8 moveId = gBattleResources->bufferB[gActiveBattler][2] & ~RET_MEGA_EVOLUTION; + u8 moveId = gBattleResources->bufferB[gActiveBattler][2] & ~(RET_MEGA_EVOLUTION | RET_ULTRA_BURST); u32 move = gBattleMons[gActiveBattler].moves[moveId]; u32 holdEffect = GetBattlerHoldEffect(gActiveBattler, TRUE); u16 *choicedMove = &gBattleStruct->choicedMove[gActiveBattler]; @@ -9959,6 +9959,7 @@ bool32 DoesSpeciesUseHoldItemToChangeForm(u16 species, u16 heldItemId) { case FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM: case FORM_CHANGE_BATTLE_PRIMAL_REVERSION: + case FORM_CHANGE_BATTLE_ULTRA_BURST: case FORM_CHANGE_ITEM_HOLD: if (formChanges[i].param1 == heldItemId) return TRUE; @@ -10029,6 +10030,40 @@ bool32 CanMegaEvolve(u8 battlerId) return FALSE; } +bool32 CanUltraBurst(u8 battlerId) +{ + u32 itemId, holdEffect, species; + struct Pokemon *mon; + u8 battlerPosition = GetBattlerPosition(battlerId); + u8 partnerPosition = GetBattlerPosition(BATTLE_PARTNER(battlerId)); + struct UltraBurstData *burst = &(((struct ChooseMoveStruct *)(&gBattleResources->bufferA[gActiveBattler][4]))->burst); + + // Gets mon data. + if (GetBattlerSide(battlerId) == B_SIDE_OPPONENT) + mon = &gEnemyParty[gBattlerPartyIndexes[battlerId]]; + else + mon = &gPlayerParty[gBattlerPartyIndexes[battlerId]]; + + species = GetMonData(mon, MON_DATA_SPECIES); + itemId = GetMonData(mon, MON_DATA_HELD_ITEM); + + // Check if there is an entry in the evolution table for Ultra Burst. + if (GetBattleFormChangeTargetSpecies(battlerId, FORM_CHANGE_BATTLE_ULTRA_BURST) != SPECIES_NONE) + { + if (itemId == ITEM_ENIGMA_BERRY_E_READER) + holdEffect = gEnigmaBerries[battlerId].holdEffect; + else + holdEffect = ItemId_GetHoldEffect(itemId); + + // Can Ultra Burst via Z Crystal. + if (holdEffect == HOLD_EFFECT_Z_CRYSTAL) + return TRUE; + } + + // No checks passed, the mon CAN'T ultra burst. + return FALSE; +} + bool32 IsBattlerMegaEvolved(u8 battlerId) { // While Transform does copy stats and visuals, it shouldn't be counted as true Mega Evolution. @@ -10045,6 +10080,14 @@ bool32 IsBattlerPrimalReverted(u8 battlerId) return (gSpeciesInfo[gBattleMons[battlerId].species].flags & SPECIES_FLAG_PRIMAL_REVERSION); } +bool32 IsBattlerUltraBursted(u8 battlerId) +{ + // While Transform does copy stats and visuals, it shouldn't be counted as true Ultra Burst. + if (gBattleMons[battlerId].status2 & STATUS2_TRANSFORMED) + return FALSE; + return (gSpeciesInfo[gBattleMons[battlerId].species].flags & SPECIES_FLAG_ULTRA_BURST); +} + // Returns SPECIES_NONE if no form change is possible u16 GetBattleFormChangeTargetSpecies(u8 battlerId, u16 method) { @@ -10068,6 +10111,7 @@ u16 GetBattleFormChangeTargetSpecies(u8 battlerId, u16 method) { case FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM: case FORM_CHANGE_BATTLE_PRIMAL_REVERSION: + case FORM_CHANGE_BATTLE_ULTRA_BURST: if (heldItem == formChanges[i].param1) targetSpecies = formChanges[i].targetSpecies; break; @@ -10138,8 +10182,8 @@ bool32 CanBattlerFormChange(u8 battlerId, u16 method) if (gBattleMons[battlerId].status2 & STATUS2_TRANSFORMED && B_TRANSFORM_FORM_CHANGES >= GEN_5) return FALSE; - // Mega Evolved Pokémon should always revert to normal upon fainting or ending the battle. - if (IsBattlerMegaEvolved(battlerId) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_END_BATTLE)) + // Mega Evolved and Ultra Bursted Pokémon should always revert to normal upon fainting or ending the battle. + if ((IsBattlerMegaEvolved(battlerId) || IsBattlerUltraBursted(battlerId)) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_END_BATTLE)) return TRUE; else if (IsBattlerPrimalReverted(battlerId) && (method == FORM_CHANGE_END_BATTLE)) return TRUE; @@ -10175,8 +10219,8 @@ bool32 TryBattleFormChange(u8 battlerId, u16 method) { bool8 restoreSpecies = FALSE; - // Mega Evolved Pokémon should always revert to normal upon fainting or ending the battle, so no need to add it to the form change tables. - if (IsBattlerMegaEvolved(battlerId) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_END_BATTLE)) + // Mega Evolved and Ultra Bursted Pokémon should always revert to normal upon fainting or ending the battle, so no need to add it to the form change tables. + if ((IsBattlerMegaEvolved(battlerId) || IsBattlerUltraBursted(battlerId)) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_END_BATTLE)) restoreSpecies = TRUE; // Unlike Megas, Primal Reversion isn't canceled on fainting. diff --git a/src/data/pokemon/form_change_table_pointers.h b/src/data/pokemon/form_change_table_pointers.h index 8453ed480..9f75ee3e2 100644 --- a/src/data/pokemon/form_change_table_pointers.h +++ b/src/data/pokemon/form_change_table_pointers.h @@ -210,6 +210,8 @@ const struct FormChange *const gFormChangeTablePointers[NUM_SPECIES] = [SPECIES_MINIOR_CORE_VIOLET] = sMiniorVioletFormChangeTable, [SPECIES_MINIOR_METEOR_YELLOW] = sMiniorYellowFormChangeTable, [SPECIES_MINIOR_CORE_YELLOW] = sMiniorYellowFormChangeTable, + [SPECIES_NECROZMA_DUSK_MANE] = sNecrozmaDuskManeFormChangeTable, + [SPECIES_NECROZMA_DAWN_WINGS] = sNecrozmaDawnWingsFormChangeTable, #endif #if P_GEN_8_POKEMON == TRUE [SPECIES_CRAMORANT] = sCramorantFormChangeTable, diff --git a/src/data/pokemon/form_change_tables.h b/src/data/pokemon/form_change_tables.h index 1d9b0e8dc..2db4d8a1d 100644 --- a/src/data/pokemon/form_change_tables.h +++ b/src/data/pokemon/form_change_tables.h @@ -548,6 +548,14 @@ static const struct FormChange sMiniorYellowFormChangeTable[] = { {FORM_CHANGE_END_BATTLE, SPECIES_MINIOR_CORE_YELLOW}, {FORM_CHANGE_TERMINATOR}, }; +static const struct FormChange sNecrozmaDuskManeFormChangeTable[] = { + {FORM_CHANGE_BATTLE_ULTRA_BURST, SPECIES_NECROZMA_ULTRA, ITEM_ULTRANECROZIUM_Z}, + {FORM_CHANGE_TERMINATOR}, +}; +static const struct FormChange sNecrozmaDawnWingsFormChangeTable[] = { + {FORM_CHANGE_BATTLE_ULTRA_BURST, SPECIES_NECROZMA_ULTRA, ITEM_ULTRANECROZIUM_Z}, + {FORM_CHANGE_TERMINATOR}, +}; #endif #if P_GEN_8_POKEMON == TRUE diff --git a/src/data/pokemon/species_info.h b/src/data/pokemon/species_info.h index 37028623b..b6eb71483 100644 --- a/src/data/pokemon/species_info.h +++ b/src/data/pokemon/species_info.h @@ -24484,7 +24484,7 @@ const struct SpeciesInfo gSpeciesInfo[] = .abilities = {ABILITY_NEUROFORCE, ABILITY_NONE}, .bodyColor = BODY_COLOR_YELLOW, .noFlip = TRUE, - .flags = SPECIES_FLAG_LEGENDARY, + .flags = SPECIES_FLAG_LEGENDARY | SPECIES_FLAG_ULTRA_BURST, }, [SPECIES_MAGEARNA_ORIGINAL_COLOR] = MAGEARNA_SPECIES_INFO(BODY_COLOR_RED), diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 4de144c38..524a501fe 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -1483,6 +1483,9 @@ void Move(u32 sourceLine, struct BattlePokemon *battler, struct MoveContext ctx) if (ctx.explicitMegaEvolve && ctx.megaEvolve) moveSlot |= RET_MEGA_EVOLUTION; + if (ctx.explicitUltraBurst && ctx.ultraBurst) + moveSlot |= RET_ULTRA_BURST; + if (ctx.explicitTarget) { target = ctx.target - gBattleMons; From c3a8edae32d4f9bf18db5a7260a990424fd4012e Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Fri, 11 Aug 2023 13:36:01 -0500 Subject: [PATCH 02/12] Sprites and fixes --- graphics/battle_interface/burst_trigger.png | Bin 0 -> 314 bytes src/battle_interface.c | 4 ++-- src/battle_main.c | 6 ++--- src/battle_script_commands.c | 2 ++ src/battle_util.c | 23 +++++++++++++++++++- 5 files changed, 29 insertions(+), 6 deletions(-) create mode 100644 graphics/battle_interface/burst_trigger.png diff --git a/graphics/battle_interface/burst_trigger.png b/graphics/battle_interface/burst_trigger.png new file mode 100644 index 0000000000000000000000000000000000000000..e3eb6384d4b4dde7ac90a245e3b0c683812787d0 GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^3P9|@!VDxk7i7!@QjEnx?oJHr&dIz4a#+$GeH|GX zHuiJ>Nn{1`g#vs+Tz8&dTs^(rH!~3^;Nal!|7CqE$47A>hqJ&VvY3H^?+6GpPSxg< z1`2NUba4!^IDL20L0)D>9+pNfrSJ(284Slo3q%EW{ttV!+yBO1zDMFxm5hg%tS<76 zNEMjy(dB}OV_wwax(zR67+M@umc3iB#ZsN2R^G}?p-QHhdFQqlH!Z_ye zPCU5!c!B<$ZGG%bx0yw~{)FEB8^Mw?QU319{&!E>Z#&s@xEzuxd6C9;!2VVC_rsyN zZ|lCiV6ZRf|NQ%5gkr(Zi@kqdoU7CM)-3-*|F6eO?h|)3r?Wk3H3K@H!PC{xWt~$( F696<{c6a~) literal 0 HcmV?d00001 diff --git a/src/battle_interface.c b/src/battle_interface.c index 281d18e04..3256dda8f 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -677,8 +677,8 @@ static const struct SpriteTemplate sSpriteTemplate_MegaTrigger = .callback = SpriteCb_MegaTrigger }; -static const u8 ALIGNED(4) sBurstTriggerGfx[] = INCBIN_U8("graphics/battle_interface/mega_trigger.4bpp"); -static const u16 sBurstTriggerPal[] = INCBIN_U16("graphics/battle_interface/mega_trigger.gbapal"); +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 = { diff --git a/src/battle_main.c b/src/battle_main.c index 86d72bd73..b39634eef 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -4438,9 +4438,9 @@ static void HandleTurnActionSelectionState(void) RecordedBattle_SetBattlerAction(gActiveBattler, gBattleResources->bufferB[gActiveBattler][2]); RecordedBattle_SetBattlerAction(gActiveBattler, gBattleResources->bufferB[gActiveBattler][3]); } - gBattleStruct->chosenMovePositions[gActiveBattler] = gBattleResources->bufferB[gActiveBattler][2] & ~(RET_MEGA_EVOLUTION | RET_ULTRA_BURST); - gChosenMoveByBattler[gActiveBattler] = gBattleMons[gActiveBattler].moves[gBattleStruct->chosenMovePositions[gActiveBattler]]; - gBattleStruct->moveTarget[gActiveBattler] = gBattleResources->bufferB[gActiveBattler][3]; + *(gBattleStruct->chosenMovePositions + gActiveBattler) = gBattleResources->bufferB[gActiveBattler][2] & ~(RET_MEGA_EVOLUTION | RET_ULTRA_BURST); + gChosenMoveByBattler[gActiveBattler] = gBattleMons[gActiveBattler].moves[*(gBattleStruct->chosenMovePositions + gActiveBattler)]; + *(gBattleStruct->moveTarget + gActiveBattler) = gBattleResources->bufferB[gActiveBattler][3]; if (gBattleResources->bufferB[gActiveBattler][2] & RET_MEGA_EVOLUTION) gBattleStruct->mega.toEvolve |= gBitTable[gActiveBattler]; else if (gBattleResources->bufferB[gActiveBattler][2] & RET_ULTRA_BURST) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 42d427a93..04ef3bcf3 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -8628,6 +8628,8 @@ static void HandleScriptMegaPrimalBurst(u32 caseId, u32 battlerId, u32 type) SetBattlerShadowSpriteCallback(battlerId, gBattleMons[battlerId].species); if (type == 0) gBattleStruct->mega.alreadyEvolved[position] = TRUE; + if (type == 2) + gBattleStruct->burst.alreadyBursted[position] = TRUE; } } diff --git a/src/battle_util.c b/src/battle_util.c index 0ef2752f5..5e50dc29c 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -10036,7 +10036,28 @@ bool32 CanUltraBurst(u8 battlerId) struct Pokemon *mon; u8 battlerPosition = GetBattlerPosition(battlerId); u8 partnerPosition = GetBattlerPosition(BATTLE_PARTNER(battlerId)); - struct UltraBurstData *burst = &(((struct ChooseMoveStruct *)(&gBattleResources->bufferA[gActiveBattler][4]))->burst); + + // Check if Player has a Z Ring + if ((GetBattlerPosition(battlerId) == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && GetBattlerPosition(battlerId) == 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]) + return FALSE; + + // Cannot use z move and ultra burst on same turn + if (gBattleStruct->zmove.toBeUsed[battlerId]) + return FALSE; + + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE + && IsPartnerMonFromSameTrainer(battlerId) + && (gBattleStruct->burst.alreadyBursted[partnerPosition] || (gBattleStruct->burst.toBurst & gBitTable[BATTLE_PARTNER(battlerId)]))) + return FALSE; + + // Check if mon is currently held by Sky Drop + if (gStatuses3[battlerId] & STATUS3_SKY_DROPPED) + return FALSE; // Gets mon data. if (GetBattlerSide(battlerId) == B_SIDE_OPPONENT) From 063d83b58e1b1b88751acedaf9f6eb920489819b Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Fri, 11 Aug 2023 14:01:45 -0500 Subject: [PATCH 03/12] Fix build --- test/test_battle.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/test_battle.h b/test/test_battle.h index 74a3bc2e8..3468a6841 100644 --- a/test/test_battle.h +++ b/test/test_battle.h @@ -795,6 +795,8 @@ struct MoveContext u16 explicitSecondaryEffect:1; u16 megaEvolve:1; u16 explicitMegaEvolve:1; + u16 ultraBurst:1; + u16 explicitUltraBurst:1; // TODO: u8 zMove:1; u16 allowed:1; u16 explicitAllowed:1; From 2b27b0bf001f59477d0008b856d374e94759ddf9 Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Sat, 12 Aug 2023 12:30:36 -0500 Subject: [PATCH 04/12] Constants --- include/constants/battle.h | 5 +++++ src/battle_script_commands.c | 14 +++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/include/constants/battle.h b/include/constants/battle.h index 7f2cb164e..d99eae919 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -495,4 +495,9 @@ #define PARENTAL_BOND_2ND_HIT 1 #define PARENTAL_BOND_OFF 0 +// Constants for if HandleScriptMegaPrimalBurst should handle Mega Evolution, Primal Reversion, or Ultra Burst. +#define HANDLE_TYPE_MEGA_EVOLUTION 0 +#define HANDLE_TYPE_PRIMAL_REVERSION 1 +#define HANDLE_TYPE_ULTRA_BURST 2 + #endif // GUARD_CONSTANTS_BATTLE_H diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 04ef3bcf3..f42bdae7d 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -8605,12 +8605,12 @@ static void HandleScriptMegaPrimalBurst(u32 caseId, u32 battlerId, u32 type) // Change species. if (caseId == 0) { - if (type == 0) + if (type == HANDLE_TYPE_MEGA_EVOLUTION) { if (!TryBattleFormChange(battlerId, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM)) TryBattleFormChange(battlerId, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_MOVE); } - else if (type == 1) + else if (type == HANDLE_TYPE_PRIMAL_REVERSION) TryBattleFormChange(battlerId, FORM_CHANGE_BATTLE_PRIMAL_REVERSION); else TryBattleFormChange(battlerId, FORM_CHANGE_BATTLE_ULTRA_BURST); @@ -8626,9 +8626,9 @@ static void HandleScriptMegaPrimalBurst(u32 caseId, u32 battlerId, u32 type) UpdateHealthboxAttribute(gHealthboxSpriteIds[battlerId], mon, HEALTHBOX_ALL); if (side == B_SIDE_OPPONENT) SetBattlerShadowSpriteCallback(battlerId, gBattleMons[battlerId].species); - if (type == 0) + if (type == HANDLE_TYPE_MEGA_EVOLUTION) gBattleStruct->mega.alreadyEvolved[position] = TRUE; - if (type == 2) + if (type == HANDLE_TYPE_ULTRA_BURST) gBattleStruct->burst.alreadyBursted[position] = TRUE; } } @@ -9686,21 +9686,21 @@ static void Cmd_various(void) case VARIOUS_HANDLE_MEGA_EVO: { VARIOUS_ARGS(u8 case_); - HandleScriptMegaPrimalBurst(cmd->case_, gActiveBattler, 0); + HandleScriptMegaPrimalBurst(cmd->case_, gActiveBattler, HANDLE_TYPE_MEGA_EVOLUTION); gBattlescriptCurrInstr = cmd->nextInstr; return; } case VARIOUS_HANDLE_PRIMAL_REVERSION: { VARIOUS_ARGS(u8 case_); - HandleScriptMegaPrimalBurst(cmd->case_, gActiveBattler, 1); + HandleScriptMegaPrimalBurst(cmd->case_, gActiveBattler, HANDLE_TYPE_PRIMAL_REVERSION); gBattlescriptCurrInstr = cmd->nextInstr; return; } case VARIOUS_HANDLE_ULTRA_BURST: { VARIOUS_ARGS(u8 case_); - HandleScriptMegaPrimalBurst(cmd->case_, gActiveBattler, 2); + HandleScriptMegaPrimalBurst(cmd->case_, gActiveBattler, HANDLE_TYPE_ULTRA_BURST); gBattlescriptCurrInstr = cmd->nextInstr; return; } From 4e0e70ae81ad915bbb4a29f12b84cf3036a48ce3 Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Thu, 17 Aug 2023 13:29:04 -0500 Subject: [PATCH 05/12] Fix memory corruption when handling the ultra burst trigger sprite --- src/battle_interface.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/battle_interface.c b/src/battle_interface.c index 64ea370d7..feab3bc67 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -1596,11 +1596,11 @@ bool32 IsBurstTriggerSpriteActive(void) void HideBurstTriggerSprite(void) { - if (gBattleStruct->burst.triggerSpriteId != 0xFF) - { - ChangeBurstTriggerSprite(gBattleStruct->burst.triggerSpriteId, 0); - gSprites[gBattleStruct->burst.triggerSpriteId].tHide = TRUE; - } + if (gBattleStruct->burst.triggerSpriteId >= MAX_SPRITES) + return; + ChangeBurstTriggerSprite(gBattleStruct->burst.triggerSpriteId, 0); + gSprites[gBattleStruct->burst.triggerSpriteId].tHide = TRUE; + } void DestroyBurstTriggerSprite(void) From 4ca4b2dd7698a18098f550ad3da712e47f57d198 Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Thu, 17 Aug 2023 13:29:27 -0500 Subject: [PATCH 06/12] Update battle_interface.c --- src/battle_interface.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/battle_interface.c b/src/battle_interface.c index feab3bc67..dbb576db8 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -1600,7 +1600,6 @@ void HideBurstTriggerSprite(void) return; ChangeBurstTriggerSprite(gBattleStruct->burst.triggerSpriteId, 0); gSprites[gBattleStruct->burst.triggerSpriteId].tHide = TRUE; - } void DestroyBurstTriggerSprite(void) From 2e1b137183f6cccb0ddb9ce3be9d6e14f2044186 Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Sat, 26 Aug 2023 15:19:47 -0500 Subject: [PATCH 07/12] Ultra Burst tests --- test/battle/form_change/ultra_burst.c | 121 ++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 test/battle/form_change/ultra_burst.c diff --git a/test/battle/form_change/ultra_burst.c b/test/battle/form_change/ultra_burst.c new file mode 100644 index 000000000..1fc5dc83c --- /dev/null +++ b/test/battle/form_change/ultra_burst.c @@ -0,0 +1,121 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Dusk Mane Necrozma can Ultra Burst holding Ultranecrozium Z") +{ + GIVEN { + PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Item(ITEM_ULTRANECROZIUM_Z); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE, ultraBurst: TRUE); } + } SCENE { + MESSAGE("Bright light is about to burst out of Necrozma!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ULTRA_BURST, player); + MESSAGE("Necrozma regained its true power through Ultra Burst!"); + } THEN { + EXPECT_EQ(player->species, SPECIES_NECROZMA_ULTRA); + } +} + +DOUBLE_BATTLE_TEST("Ultra Burst's order is determined by Speed - opponent faster") +{ + GIVEN { + PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Item(ITEM_ULTRANECROZIUM_Z); Speed(1); } + PLAYER(SPECIES_WOBBUFFET) { Speed(3); } + 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); } + } SCENE { + MESSAGE("Bright light is about to burst out of Foe Necrozma!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ULTRA_BURST, opponentLeft); + MESSAGE("Foe Necrozma regained its true power through Ultra Burst!"); + MESSAGE("Bright light is about to burst out of Necrozma!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ULTRA_BURST, playerLeft); + MESSAGE("Necrozma regained its true power through Ultra Burst!"); + } +} + +DOUBLE_BATTLE_TEST("Ultra Burst's order is determined by Speed - player faster") +{ + GIVEN { + PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Item(ITEM_ULTRANECROZIUM_Z); Speed(5); } + PLAYER(SPECIES_WOBBUFFET) { Speed(3); } + 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); } + } SCENE { + MESSAGE("Bright light is about to burst out of Necrozma!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ULTRA_BURST, playerLeft); + MESSAGE("Necrozma regained its true power through Ultra Burst!"); + MESSAGE("Bright light is about to burst out of Foe Necrozma!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ULTRA_BURST, opponentLeft); + MESSAGE("Foe Necrozma regained its true power through Ultra Burst!"); + } +} + +SINGLE_BATTLE_TEST("Ultra Burst affects turn order") +{ + GIVEN { + ASSUME(B_MEGA_EVO_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); } + } SCENE { + MESSAGE("Necrozma used Celebrate!"); + MESSAGE("Foe Wobbuffet used Celebrate!"); + } THEN { + ASSUME(player->speed == 205); + } +} + +DOUBLE_BATTLE_TEST("Ultra Burst happens after switching, but before Focus Punch-like Moves") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_FOCUS_PUNCH].effect == EFFECT_FOCUS_PUNCH); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Item(ITEM_ULTRANECROZIUM_Z); } + OPPONENT(SPECIES_WYNAUT); + 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 {} + } SCENE { + MESSAGE("2 withdrew Wobbuffet!"); + MESSAGE("2 sent out Wobbuffet!"); + + MESSAGE("Bright light is about to burst out of Necrozma!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ULTRA_BURST, playerRight); + MESSAGE("Necrozma regained its true power through Ultra Burst!"); + + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FOCUS_PUNCH_SETUP, playerRight); + MESSAGE("Necrozma is tightening its focus!"); + + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FOCUS_PUNCH_SETUP, playerLeft); + MESSAGE("Wobbuffet is tightening its focus!"); + } +} + +SINGLE_BATTLE_TEST("Ultra Burst and Mega Evolution can happen on the same turn") +{ + GIVEN { + 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); } + } SCENE { + MESSAGE("Bright light is about to burst out of Necrozma!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ULTRA_BURST, player); + MESSAGE("Necrozma regained its true power through Ultra Burst!"); + + MESSAGE("Foe Gardevoir's Gardevoirite is reacting to 's Mega Ring!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponent); + MESSAGE("Foe Gardevoir has Mega Evolved into Mega Gardevoir!"); + } THEN { + EXPECT_EQ(player->species, SPECIES_NECROZMA_ULTRA); + EXPECT_EQ(opponent->species, SPECIES_GARDEVOIR_MEGA); + } +} From ac67900c0afc22163789dcfc7c37c8865c9b3b07 Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Sat, 26 Aug 2023 15:32:10 -0500 Subject: [PATCH 08/12] Fix test speed --- test/battle/form_change/ultra_burst.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/battle/form_change/ultra_burst.c b/test/battle/form_change/ultra_burst.c index 1fc5dc83c..dac4309ae 100644 --- a/test/battle/form_change/ultra_burst.c +++ b/test/battle/form_change/ultra_burst.c @@ -59,15 +59,15 @@ SINGLE_BATTLE_TEST("Ultra Burst affects turn order") { GIVEN { ASSUME(B_MEGA_EVO_TURN_ORDER); - PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Item(ITEM_ULTRANECROZIUM_Z); Speed(105); } - OPPONENT(SPECIES_WOBBUFFET) { Speed(106); } + PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Item(ITEM_ULTRANECROZIUM_Z); Speed(77); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(78); } } WHEN { TURN { MOVE(player, MOVE_CELEBRATE, ultraBurst: TRUE); } } SCENE { MESSAGE("Necrozma used Celebrate!"); MESSAGE("Foe Wobbuffet used Celebrate!"); } THEN { - ASSUME(player->speed == 205); + ASSUME(player->speed == 129); } } From 96e39cde0b89426e7bbc72612a1fbbd3d5b13928 Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Sat, 26 Aug 2023 16:14:24 -0500 Subject: [PATCH 09/12] Update ultra_burst.c --- test/battle/form_change/ultra_burst.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/battle/form_change/ultra_burst.c b/test/battle/form_change/ultra_burst.c index dac4309ae..1c9131c54 100644 --- a/test/battle/form_change/ultra_burst.c +++ b/test/battle/form_change/ultra_burst.c @@ -59,15 +59,15 @@ SINGLE_BATTLE_TEST("Ultra Burst affects turn order") { GIVEN { ASSUME(B_MEGA_EVO_TURN_ORDER); - PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Item(ITEM_ULTRANECROZIUM_Z); Speed(77); } - OPPONENT(SPECIES_WOBBUFFET) { Speed(78); } + PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Item(ITEM_ULTRANECROZIUM_Z); Speed(105); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(106); } } WHEN { TURN { MOVE(player, MOVE_CELEBRATE, ultraBurst: TRUE); } } SCENE { MESSAGE("Necrozma used Celebrate!"); MESSAGE("Foe Wobbuffet used Celebrate!"); } THEN { - ASSUME(player->speed == 129); + ASSUME(player->speed == 234); } } From 7ffd31f5de2ee685d63b61f0ad5bf77ec47f9d6f Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Sat, 26 Aug 2023 16:42:35 -0500 Subject: [PATCH 10/12] Update ultra_burst.c --- test/battle/form_change/ultra_burst.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/battle/form_change/ultra_burst.c b/test/battle/form_change/ultra_burst.c index 1c9131c54..f22d8b7b0 100644 --- a/test/battle/form_change/ultra_burst.c +++ b/test/battle/form_change/ultra_burst.c @@ -4,6 +4,7 @@ SINGLE_BATTLE_TEST("Dusk Mane Necrozma can Ultra Burst holding Ultranecrozium Z") { GIVEN { + ASSUME(P_GEN_7_POKEMON == TRUE); PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Item(ITEM_ULTRANECROZIUM_Z); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -20,6 +21,7 @@ SINGLE_BATTLE_TEST("Dusk Mane Necrozma can Ultra Burst holding Ultranecrozium Z" DOUBLE_BATTLE_TEST("Ultra Burst's order is determined by Speed - opponent faster") { GIVEN { + ASSUME(P_GEN_7_POKEMON == TRUE); PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Item(ITEM_ULTRANECROZIUM_Z); Speed(1); } PLAYER(SPECIES_WOBBUFFET) { Speed(3); } OPPONENT(SPECIES_NECROZMA_DAWN_WINGS) { Item(ITEM_ULTRANECROZIUM_Z); Speed(3); } @@ -39,6 +41,7 @@ DOUBLE_BATTLE_TEST("Ultra Burst's order is determined by Speed - opponent faster DOUBLE_BATTLE_TEST("Ultra Burst's order is determined by Speed - player faster") { GIVEN { + ASSUME(P_GEN_7_POKEMON == TRUE); PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Item(ITEM_ULTRANECROZIUM_Z); Speed(5); } PLAYER(SPECIES_WOBBUFFET) { Speed(3); } OPPONENT(SPECIES_NECROZMA_DAWN_WINGS) { Item(ITEM_ULTRANECROZIUM_Z); Speed(2); } @@ -58,6 +61,7 @@ DOUBLE_BATTLE_TEST("Ultra Burst's order is determined by Speed - player faster") SINGLE_BATTLE_TEST("Ultra Burst affects turn order") { GIVEN { + ASSUME(P_GEN_7_POKEMON == TRUE); ASSUME(B_MEGA_EVO_TURN_ORDER); PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Item(ITEM_ULTRANECROZIUM_Z); Speed(105); } OPPONENT(SPECIES_WOBBUFFET) { Speed(106); } @@ -67,13 +71,14 @@ SINGLE_BATTLE_TEST("Ultra Burst affects turn order") MESSAGE("Necrozma used Celebrate!"); MESSAGE("Foe Wobbuffet used Celebrate!"); } THEN { - ASSUME(player->speed == 234); + ASSUME(player->speed == 263); } } DOUBLE_BATTLE_TEST("Ultra Burst happens after switching, but before Focus Punch-like Moves") { GIVEN { + ASSUME(P_GEN_7_POKEMON == TRUE); ASSUME(gBattleMoves[MOVE_FOCUS_PUNCH].effect == EFFECT_FOCUS_PUNCH); PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Item(ITEM_ULTRANECROZIUM_Z); } @@ -102,6 +107,7 @@ DOUBLE_BATTLE_TEST("Ultra Burst happens after switching, but before Focus Punch- SINGLE_BATTLE_TEST("Ultra Burst and Mega Evolution can happen on the same turn") { GIVEN { + ASSUME(P_GEN_7_POKEMON == TRUE); PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Item(ITEM_ULTRANECROZIUM_Z); Speed(3); } OPPONENT(SPECIES_GARDEVOIR) { Item(ITEM_GARDEVOIRITE); Speed(2); } } WHEN { From 028536e2d34b711a589973ac8d43b2b726e74bc6 Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Tue, 29 Aug 2023 17:12:09 -0500 Subject: [PATCH 11/12] Spaces --- src/battle_controller_player_partner.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/battle_controller_player_partner.c b/src/battle_controller_player_partner.c index 8a7f18d0e..0b2cb55d6 100644 --- a/src/battle_controller_player_partner.c +++ b/src/battle_controller_player_partner.c @@ -373,13 +373,12 @@ static void PlayerPartnerHandleChooseMove(u32 battler) // If partner can mega evolve, do it. if (CanMegaEvolve(battler)) BtlController_EmitTwoReturnValues(BUFFER_B, 10, (chosenMoveId) | (RET_MEGA_EVOLUTION) | (gBattlerTarget << 8)); - else if (CanUltraBurst(battler)) - BtlController_EmitTwoReturnValues(BUFFER_B, 10, (chosenMoveId) | (RET_ULTRA_BURST) | (gBattlerTarget << 8)); + else if (CanUltraBurst(battler)) + BtlController_EmitTwoReturnValues(BUFFER_B, 10, (chosenMoveId) | (RET_ULTRA_BURST) | (gBattlerTarget << 8)); else BtlController_EmitTwoReturnValues(BUFFER_B, 10, (chosenMoveId) | (gBattlerTarget << 8)); - } - + PlayerPartnerBufferExecCompleted(battler); } From c427d96f3ffb52dc9367d8c1f826a8646ab733cc Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Tue, 29 Aug 2023 19:13:21 -0500 Subject: [PATCH 12/12] Better Ultra Burst animation --- data/battle_anim_scripts.s | 7 ++++++- src/battle_anim_new.c | 12 ++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index 825e36c38..62772a5c1 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -27004,6 +27004,7 @@ General_PrimalReversion_Omega: end General_UltraBurst:: + loadspritegfx ANIM_TAG_ULTRA_BURST_SYMBOL loadspritegfx ANIM_TAG_SPARK_2 @spark loadspritegfx ANIM_TAG_LEAF @green loadspritegfx ANIM_TAG_ELECTRIC_ORBS @charge particles @@ -27025,12 +27026,16 @@ General_UltraBurst:: call LightThatBurnsTheSkyGreenSparks call LightThatBurnsTheSkyGreenSparks call LightThatBurnsTheSkyGreenSparks - delay 0xe + delay 20 + createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 0, 16, RGB_WHITEALPHA createvisualtask AnimTask_TransformMon, 2, 1, 0 + createsprite gUltraBurstSymbolSpriteTemplate, ANIM_ATTACKER, 0x0, 0x0, 0x0, 0x0, 0x0 + waitforvisualfinish createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 16, 0, RGB_WHITEALPHA createvisualtask AnimTask_HorizontalShake, 5, ANIM_TARGET, 5, 14 waitforvisualfinish createvisualtask SoundTask_PlayNormalCry, 0 + waitforvisualfinish clearmonbg ANIM_ATK_PARTNER blendoff end diff --git a/src/battle_anim_new.c b/src/battle_anim_new.c index 608e6ae94..966887b25 100644 --- a/src/battle_anim_new.c +++ b/src/battle_anim_new.c @@ -4761,6 +4761,18 @@ const struct SpriteTemplate gSpriteTemplate_BitterMaliceRing = { .callback = AnimParticleInVortex }; +//ultra burst +const struct SpriteTemplate gUltraBurstSymbolSpriteTemplate = +{ + .tileTag = ANIM_TAG_ULTRA_BURST_SYMBOL, + .paletteTag = ANIM_TAG_ULTRA_BURST_SYMBOL, + .oam = &gOamData_AffineDouble_ObjBlend_32x32, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gAffineAnims_LusterPurgeCircle, + .callback = AnimSpriteOnMonPos +}; + // Z MOVES //activate const struct SpriteTemplate gZMoveSymbolSpriteTemplate =