diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index dc1dc1a47c..5977c3321b 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -10618,8 +10618,8 @@ BattleScript_DynamaxBegins:: playanimation BS_SCRIPTING, B_ANIM_SLIDE_OFFSCREEN waitanimation returntoball BS_SCRIPTING - updatedynamax switchinanim BS_SCRIPTING, TRUE + updatedynamax playanimation BS_SCRIPTING, B_ANIM_DYNAMAX_GROWTH waitanimation end diff --git a/graphics/battle_interface/dynamax_indicator.png b/graphics/battle_interface/dynamax_indicator.png new file mode 100644 index 0000000000..767a71c1ad Binary files /dev/null and b/graphics/battle_interface/dynamax_indicator.png differ diff --git a/include/battle.h b/include/battle.h index f2b9fb04a5..ac60b0c899 100644 --- a/include/battle.h +++ b/include/battle.h @@ -527,6 +527,7 @@ struct DynamaxData { bool8 playerSelect; u8 triggerSpriteId; + u8 indicatorSpriteId[MAX_BATTLERS_COUNT]; bool8 toDynamax[MAX_BATTLERS_COUNT]; bool8 alreadyDynamaxed[NUM_BATTLE_SIDES]; bool8 dynamaxed[MAX_BATTLERS_COUNT]; diff --git a/include/battle_dynamax.h b/include/battle_dynamax.h index 2fcb0ff402..3854d4c032 100644 --- a/include/battle_dynamax.h +++ b/include/battle_dynamax.h @@ -69,10 +69,20 @@ const u8 *GetMaxMoveName(u16 move); void ChooseDamageNonTypesString(u8 type); u32 GetMaxMoveStatusEffect(u16 move); u16 SetMaxMoveEffect(u16 move); + void ChangeDynamaxTriggerSprite(u8 spriteId, u8 animId); void CreateDynamaxTriggerSprite(u8, bool8); void HideDynamaxTriggerSprite(void); bool32 IsDynamaxTriggerSpriteActive(void); void DestroyDynamaxTriggerSprite(void); +void DynamaxIndicator_LoadSpriteGfx(void); +bool32 DynamaxIndicator_ShouldBeInvisible(u32 battlerId); +u8 DynamaxIndicator_GetSpriteId(u32 healthboxSpriteId); +void DynamaxIndicator_SetVisibilities(u32 healthboxId, bool32 invisible); +void DynamaxIndicator_UpdateOamPriorities(u32 healthboxId, u32 oamPriority); +void DynamaxIndicator_UpdateLevel(u32 healthboxId, u32 level); +void DynamaxIndicator_CreateSprite(u32 battlerId, u32 healthboxSpriteId); +void DynamaxIndicator_DestroySprite(u32 healthboxSpriteId); + #endif diff --git a/include/battle_interface.h b/include/battle_interface.h index f222c56e9d..6ea2501b77 100644 --- a/include/battle_interface.h +++ b/include/battle_interface.h @@ -90,6 +90,7 @@ void DestoryHealthboxSprite(u8 healthboxSpriteId); void DummyBattleInterfaceFunc(u8 healthboxSpriteId, bool8 isDoubleBattleBankOnly); void UpdateOamPriorityInAllHealthboxes(u8 priority, bool32 hideHpBoxes); 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); diff --git a/src/battle_dynamax.c b/src/battle_dynamax.c index 589e8fd983..c9093dc12c 100644 --- a/src/battle_dynamax.c +++ b/src/battle_dynamax.c @@ -87,6 +87,10 @@ static const struct GMaxMove sGMaxMoveTable[] = extern u8 gMaxMovePowers[MOVES_COUNT]; +// forward declarations +static void SpriteCb_DynamaxTrigger(struct Sprite *); +static void SpriteCb_DynamaxIndicator(struct Sprite *); + // Returns whether a battler is Dynamaxed. bool8 IsDynamaxed(u16 battlerId) { @@ -618,7 +622,7 @@ u16 SetMaxMoveEffect(u16 move) return effect; } -// Sprite Data +// 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"); @@ -666,7 +670,6 @@ static const union AnimCmd *const sSpriteAnimTable_DynamaxTrigger[] = sSpriteAnim_DynamaxTriggerOn, }; -static void SpriteCb_DynamaxTrigger(struct Sprite *sprite); static const struct SpriteTemplate sSpriteTemplate_DynamaxTrigger = { .tileTag = TAG_DYNAMAX_TRIGGER_TILE, @@ -799,3 +802,160 @@ void DestroyDynamaxTriggerSprite(void) #undef tBattler #undef tHide + + +// DYNAMAX INDICATOR: +static const u8 ALIGNED(4) sDynamaxIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/dynamax_indicator.4bpp"); +static const u16 sDynamaxIndicatorPal[] = INCBIN_U16("graphics/battle_interface/dynamax_indicator.gbapal"); + +static const struct SpriteSheet sSpriteSheet_DynamaxIndicator = +{ + sDynamaxIndicatorGfx, sizeof(sDynamaxIndicatorGfx), TAG_DYNAMAX_INDICATOR_TILE +}; +static const struct SpritePalette sSpritePalette_DynamaxIndicator = +{ + sDynamaxIndicatorPal, TAG_DYNAMAX_INDICATOR_PAL +}; + +static const struct SpriteSheet sDynamaxIndicator_SpriteSheet[] = +{ + sDynamaxIndicatorGfx, sizeof(sDynamaxIndicatorGfx), TAG_DYNAMAX_INDICATOR_TILE +}; + +static const struct SpritePalette sDynamaxIndicator_SpritePalette[] = +{ + sDynamaxIndicatorPal, TAG_DYNAMAX_INDICATOR_PAL +}; + +static const struct OamData sOamData_DynamaxIndicator = +{ + .shape = SPRITE_SHAPE(16x16), + .size = SPRITE_SIZE(16x16), + .priority = 1, +}; + +static const struct SpriteTemplate sSpriteTemplate_DynamaxIndicator = +{ + .tileTag = TAG_DYNAMAX_INDICATOR_TILE, + .paletteTag = TAG_DYNAMAX_INDICATOR_PAL, + .oam = &sOamData_DynamaxIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_DynamaxIndicator, +}; + +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: dynamax +#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_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] + +void DynamaxIndicator_LoadSpriteGfx(void) +{ + LoadSpriteSheet(sDynamaxIndicator_SpriteSheet); + LoadSpritePalette(sDynamaxIndicator_SpritePalette); +} + +bool32 DynamaxIndicator_ShouldBeInvisible(u32 battlerId) +{ + return !IsDynamaxed(battlerId); +} + +u8 DynamaxIndicator_GetSpriteId(u32 healthboxSpriteId) +{ + return gBattleStruct->dynamax.indicatorSpriteId[gSprites[healthboxSpriteId].hMain_Battler]; +} + +void DynamaxIndicator_SetVisibilities(u32 healthboxId, bool32 invisible) +{ + u32 i; + u8 spriteId = DynamaxIndicator_GetSpriteId(healthboxId); + u32 battlerId = gSprites[healthboxId].hMain_Battler; + + if (invisible == TRUE) + gSprites[spriteId].invisible = TRUE; + else // Try visible. + gSprites[spriteId].invisible = DynamaxIndicator_ShouldBeInvisible(battlerId); +} + +void DynamaxIndicator_UpdateOamPriorities(u32 healthboxId, u32 oamPriority) +{ + u32 i; + u8 spriteId = DynamaxIndicator_GetSpriteId(healthboxId); + gSprites[spriteId].oam.priority = oamPriority; +} + +void DynamaxIndicator_UpdateLevel(u32 healthboxId, u32 level) +{ + u32 i; + s16 xDelta = 0; + u8 spriteId = DynamaxIndicator_GetSpriteId(healthboxId); + + if (level >= 100) + xDelta -= 4; + else if (level < 10) + xDelta += 5; + + gSprites[spriteId].tLevelXDelta = xDelta; +} + +void DynamaxIndicator_CreateSprite(u32 battlerId, u32 healthboxSpriteId) +{ + u32 position, level; + 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 = gBattleStruct->dynamax.indicatorSpriteId[battlerId] = CreateSpriteAtEnd(&sSpriteTemplate_DynamaxIndicator, 0, y, 0); + gSprites[spriteId].tBattler = battlerId; + gSprites[spriteId].tPosX = x; + gSprites[spriteId].invisible = TRUE; +} + +void DynamaxIndicator_DestroySprite(u32 healthboxSpriteId) +{ + u8 spriteId = DynamaxIndicator_GetSpriteId(healthboxSpriteId); + DestroySprite(&gSprites[spriteId]); +} + +static void SpriteCb_DynamaxIndicator(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 +#undef tLevelXDelta diff --git a/src/battle_gfx_sfx_util.c b/src/battle_gfx_sfx_util.c index 7203c12e42..bc4fea906c 100644 --- a/src/battle_gfx_sfx_util.c +++ b/src/battle_gfx_sfx_util.c @@ -699,6 +699,7 @@ bool8 BattleLoadAllHealthBoxesGfx(u8 state) LoadSpritePalette(&sSpritePalettes_HealthBoxHealthBar[0]); LoadSpritePalette(&sSpritePalettes_HealthBoxHealthBar[1]); MegaIndicator_LoadSpritesGfx(); + DynamaxIndicator_LoadSpriteGfx(); } else if (!IsDoubleBattle()) { diff --git a/src/battle_interface.c b/src/battle_interface.c index 8bbd26764b..4e1e71a014 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -785,6 +785,9 @@ u8 CreateBattlerHealthboxSprites(u8 battlerId) // Create mega indicator sprites. MegaIndicator_CreateSprites(battlerId, healthboxLeftSpriteId); + // Create dynamax indicator sprites. + DynamaxIndicator_CreateSprite(battlerId, healthboxLeftSpriteId); + gBattleStruct->ballSpriteIds[0] = MAX_SPRITES; gBattleStruct->ballSpriteIds[1] = MAX_SPRITES; @@ -868,6 +871,7 @@ void SetHealthboxSpriteInvisible(u8 healthboxSpriteId) gSprites[gSprites[healthboxSpriteId].hMain_HealthBarSpriteId].invisible = TRUE; gSprites[gSprites[healthboxSpriteId].oam.affineParam].invisible = TRUE; MegaIndicator_SetVisibilities(healthboxSpriteId, TRUE); + DynamaxIndicator_SetVisibilities(healthboxSpriteId, TRUE); } void SetHealthboxSpriteVisible(u8 healthboxSpriteId) @@ -876,6 +880,7 @@ void SetHealthboxSpriteVisible(u8 healthboxSpriteId) gSprites[gSprites[healthboxSpriteId].hMain_HealthBarSpriteId].invisible = FALSE; gSprites[gSprites[healthboxSpriteId].oam.affineParam].invisible = FALSE; MegaIndicator_SetVisibilities(healthboxSpriteId, FALSE); + DynamaxIndicator_SetVisibilities(healthboxSpriteId, FALSE); } static void UpdateSpritePos(u8 spriteId, s16 x, s16 y) @@ -887,6 +892,7 @@ static void UpdateSpritePos(u8 spriteId, s16 x, s16 y) void DestoryHealthboxSprite(u8 healthboxSpriteId) { MegaIndicator_DestroySprites(healthboxSpriteId); + DynamaxIndicator_DestroySprite(healthboxSpriteId); DestroySprite(&gSprites[gSprites[healthboxSpriteId].oam.affineParam]); DestroySprite(&gSprites[gSprites[healthboxSpriteId].hMain_HealthBarSpriteId]); DestroySprite(&gSprites[healthboxSpriteId]); @@ -910,6 +916,7 @@ static void TryToggleHealboxVisibility(u32 priority, u32 healthboxLeftSpriteId, gSprites[healthboxRightSpriteId].invisible = invisible; gSprites[healthbarSpriteId].invisible = invisible; MegaIndicator_SetVisibilities(healthboxLeftSpriteId, invisible); + DynamaxIndicator_SetVisibilities(healthboxLeftSpriteId, invisible); } void UpdateOamPriorityInAllHealthboxes(u8 priority, bool32 hideHPBoxes) @@ -927,6 +934,7 @@ void UpdateOamPriorityInAllHealthboxes(u8 priority, bool32 hideHPBoxes) gSprites[healthbarSpriteId].oam.priority = priority; MegaIndicator_UpdateOamPriorities(healthboxLeftSpriteId, priority); + DynamaxIndicator_UpdateOamPriorities(healthboxLeftSpriteId, priority); #if B_HIDE_HEALTHBOX_IN_ANIMS if (hideHPBoxes && IsBattlerAlive(i)) @@ -983,14 +991,17 @@ 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. + // Don't print Lv char if mon is mega evolved or primal reverted or Dynamaxed. if (gBattleStruct->mega.evolvedPartyIds[GetBattlerSide(battler)] & gBitTable[gBattlerPartyIndexes[battler]] - || gBattleStruct->mega.primalRevertedPartyIds[GetBattlerSide(battler)] & gBitTable[gBattlerPartyIndexes[battler]]) + || gBattleStruct->mega.primalRevertedPartyIds[GetBattlerSide(battler)] & gBitTable[gBattlerPartyIndexes[battler]] + || IsDynamaxed(battler)) { 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); + DynamaxIndicator_UpdateLevel(healthboxSpriteId, lvl); + DynamaxIndicator_SetVisibilities(healthboxSpriteId, FALSE); } else {