From 76073fab52d7e156252584d69e66bf50ec8ea9db Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Fri, 10 Mar 2023 10:20:27 -0800 Subject: [PATCH] added basic Dynamax-in-battle --- asm/macros/battle_script.inc | 4 +++ data/battle_anim_scripts.s | 9 ++++++ data/battle_scripts_1.s | 19 +++++++++++- include/battle.h | 4 +++ include/battle_controllers.h | 1 + include/battle_dynamax.h | 5 +++ include/battle_scripts.h | 4 +++ include/constants/battle_anim.h | 3 +- include/constants/battle_script_commands.h | 1 + sound/cry_tables.inc | 1 + src/battle_controller_player.c | 21 ++++++++++++- src/battle_dynamax.c | 36 ++++++++++++++++++++-- src/battle_main.c | 20 +++++++++++- src/battle_script_commands.c | 17 ++++++++++ src/battle_util.c | 16 ++++++++-- 15 files changed, 153 insertions(+), 8 deletions(-) diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 2d8eaec0ab..89d8dee20e 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -2246,6 +2246,10 @@ various 0, VARIOUS_TRY_RECYCLE_BERRY .4byte \ptr .endm + + .macro applydynamaxhpmultiplier + various 0, VARIOUS_APPLY_DYNAMAX_HP_MULTIPLIER + .endm @ Tries to increase or decrease a battler's stat's stat stage by a specified amount. If impossible, jumps to \script. .macro modifybattlerstatstage battler:req, stat:req, mode:req, amount:req, script:req, animation:req, customString diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index 9055aed3b9..34f7471772 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -999,6 +999,7 @@ gBattleAnims_General:: .4byte General_ShellTrapSetUp @ B_ANIM_SHELL_TRAP_SETUP .4byte General_ZMoveActivate @ B_ANIM_ZMOVE_ACTIVATE .4byte General_AffectionHangedOn @ B_ANIM_AFFECTION_HANGED_ON + .4byte General_DynamaxGrowth @ B_ANIM_DYNAMAX_GROWTH .4byte General_SetWeather @ B_ANIM_MAX_SET_WEATHER .align 2 @@ -30943,3 +30944,11 @@ Move_G_MAX_RAPID_FLOW: launchtask AnimTask_DynamaxGrowth 0x5 0x1 0x1 waitforvisualfinish end + +@@@ DYNAMAX AND MAX RAIDS +General_DynamaxGrowth:: @ PORTED FROM CFRU + createvisualtask SoundTask_PlayCryWithEcho, 2, ANIM_ATTACKER, 2 + delay 8 + launchtask AnimTask_DynamaxGrowth 0x5 0x1 0x0 + waitforvisualfinish + end diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 0709626c67..3aae553ef7 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -10588,7 +10588,24 @@ BattleScript_RecycleBerriesAlliesEnd: restoretarget goto BattleScript_MoveEnd -@@ End Max Moves +@@@ END MAX MOVES @@@ + +BattleScript_DynamaxBegins:: + printstring STRINGID_EMPTYSTRING3 + playanimation BS_SCRIPTING, B_ANIM_SLIDE_OFFSCREEN + waitanimation + returntoball BS_SCRIPTING + @applydynamaxhpmultiplier + switchinanim BS_SCRIPTING, TRUE + playanimation BS_SCRIPTING, B_ANIM_DYNAMAX_GROWTH + waitanimation + end + +BattleScript_DynamaxEnds:: + printstring STRINGID_EMPTYSTRING3 + playanimation BS_SCRIPTING, B_ANIM_FORM_CHANGE + waitanimation + end2 BattleScript_PokemonCantUseTheMove:: attackstring diff --git a/include/battle.h b/include/battle.h index 130a23e5b5..1b8cc8e9b7 100644 --- a/include/battle.h +++ b/include/battle.h @@ -524,6 +524,10 @@ struct ZMoveData struct DynamaxData { + bool8 playerSelect; + bool8 toDynamax[MAX_BATTLERS_COUNT]; + bool8 alreadyDynamaxed[NUM_BATTLE_SIDES]; + bool8 dynamaxed[MAX_BATTLERS_COUNT]; u8 dynamaxTurns[MAX_BATTLERS_COUNT]; u8 usingMaxMove[MAX_BATTLERS_COUNT]; u8 activeSplit; diff --git a/include/battle_controllers.h b/include/battle_controllers.h index e1ab98b0c7..2025f26e3d 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_DYNAMAX 0x40 struct UnusedControllerStruct { diff --git a/include/battle_dynamax.h b/include/battle_dynamax.h index 1996dc4c5c..a26b5ac579 100644 --- a/include/battle_dynamax.h +++ b/include/battle_dynamax.h @@ -1,6 +1,8 @@ #ifndef GUARD_BATTLE_DYNAMAX_H #define GUARD_BATTLE_DYNAMAX_H +#define DYNAMAX_TURNS 3 + enum MaxMoveEffect { MAX_EFFECT_NONE, @@ -55,6 +57,9 @@ enum MaxMoveEffect }; bool8 IsDynamaxed(u16 battlerId); +bool8 CanDynamax(u16 battlerId); +void PrepareBattlerForDynamax(u16 battlerId); +void UndoDynamax(u16 battlerId); bool8 ShouldUseMaxMove(u16 battlerId, u16 baseMove); u16 GetMaxMove(u16 battlerId, u16 baseMove); u8 GetMaxMovePower(u16 move); diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 4606b0649a..ac79d1304c 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -486,4 +486,8 @@ extern const u8 BattleScript_EffectHealOneSixthAllies[]; extern const u8 BattleScript_EffectCureStatusAllies[]; extern const u8 BattleScript_EffectRecycleBerriesAllies[]; +// dynamax and max raids +extern const u8 BattleScript_DynamaxBegins[]; +extern const u8 BattleScript_DynamaxEnds[]; + #endif // GUARD_BATTLE_SCRIPTS_H \ No newline at end of file diff --git a/include/constants/battle_anim.h b/include/constants/battle_anim.h index dba22fac26..fc4623704d 100644 --- a/include/constants/battle_anim.h +++ b/include/constants/battle_anim.h @@ -544,7 +544,8 @@ #define B_ANIM_SHELL_TRAP_SETUP 34 #define B_ANIM_ZMOVE_ACTIVATE 35 // Using Z Moves #define B_ANIM_AFFECTION_HANGED_ON 36 -#define B_ANIM_MAX_SET_WEATHER 37 +#define B_ANIM_DYNAMAX_GROWTH 37 +#define B_ANIM_MAX_SET_WEATHER 38 // 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 c17c440d02..4dfe54f1df 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -267,6 +267,7 @@ #define VARIOUS_TRY_SET_STATUS2 175 #define VARIOUS_TRY_HEAL_SIXTH_HP 176 #define VARIOUS_TRY_RECYCLE_BERRY 177 +#define VARIOUS_APPLY_DYNAMAX_HP_MULTIPLIER 178 // Cmd_manipulatedamage #define DMG_CHANGE_SIGN 0 diff --git a/sound/cry_tables.inc b/sound/cry_tables.inc index 7714cd00ec..25645bcd06 100644 --- a/sound/cry_tables.inc +++ b/sound/cry_tables.inc @@ -4188,3 +4188,4 @@ gCryTable_Reverse:: cry_reverse Cry_Unown cry_reverse Cry_Unown .endif +@ TODO: Gigantamax Cries diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index 75581bd761..6619eaff77 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -379,6 +379,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->dynamax.playerSelect) + BtlController_EmitTwoReturnValues(BUFFER_B, 10, gMoveSelectionCursor[gActiveBattler] | RET_DYNAMAX | (gMultiUsePlayerCursor << 8)); else BtlController_EmitTwoReturnValues(BUFFER_B, 10, gMoveSelectionCursor[gActiveBattler] | (gMultiUsePlayerCursor << 8)); EndBounceEffect(gMultiUsePlayerCursor, BOUNCE_HEALTHBOX); @@ -537,6 +539,8 @@ static void HandleInputShowEntireFieldTargets(void) HideAllTargets(); if (gBattleStruct->mega.playerSelect) BtlController_EmitTwoReturnValues(BUFFER_B, 10, gMoveSelectionCursor[gActiveBattler] | RET_MEGA_EVOLUTION | (gMultiUsePlayerCursor << 8)); + else if (gBattleStruct->dynamax.playerSelect) + BtlController_EmitTwoReturnValues(BUFFER_B, 10, gMoveSelectionCursor[gActiveBattler] | RET_DYNAMAX | (gMultiUsePlayerCursor << 8)); else BtlController_EmitTwoReturnValues(BUFFER_B, 10, gMoveSelectionCursor[gActiveBattler] | (gMultiUsePlayerCursor << 8)); HideMegaTriggerSprite(); @@ -565,6 +569,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->dynamax.playerSelect) + BtlController_EmitTwoReturnValues(BUFFER_B, 10, gMoveSelectionCursor[gActiveBattler] | RET_DYNAMAX | (gMultiUsePlayerCursor << 8)); else BtlController_EmitTwoReturnValues(BUFFER_B, 10, gMoveSelectionCursor[gActiveBattler] | (gMultiUsePlayerCursor << 8)); HideTriggerSprites(); @@ -680,6 +686,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->dynamax.playerSelect) + BtlController_EmitTwoReturnValues(BUFFER_B, 10, gMoveSelectionCursor[gActiveBattler] | RET_DYNAMAX | (gMultiUsePlayerCursor << 8)); else BtlController_EmitTwoReturnValues(BUFFER_B, 10, gMoveSelectionCursor[gActiveBattler] | (gMultiUsePlayerCursor << 8)); HideTriggerSprites(); @@ -716,6 +724,7 @@ static void HandleInputChooseMove(void) else { gBattleStruct->mega.playerSelect = FALSE; + gBattleStruct->dynamax.playerSelect = FALSE; gBattleStruct->zmove.viable = FALSE; BtlController_EmitTwoReturnValues(BUFFER_B, 10, 0xFFFF); HideTriggerSprites(); @@ -810,12 +819,20 @@ static void HandleInputChooseMove(void) else ReloadMoveNames(); } + else if (CanDynamax(gActiveBattler)) + { + gBattleStruct->dynamax.playerSelect ^= 1; + MoveSelectionDisplayMoveNames(); + MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 0); + PlaySE(SE_SELECT); + } } } static void ReloadMoveNames(void) { gBattleStruct->mega.playerSelect = FALSE; + gBattleStruct->dynamax.playerSelect = FALSE; gBattleStruct->zmove.viewing = FALSE; MoveSelectionDestroyCursorAt(0); MoveSelectionDisplayMoveNames(); @@ -1685,7 +1702,8 @@ static void MoveSelectionDisplayMoveNames(void) for (i = 0; i < MAX_MON_MOVES; i++) { MoveSelectionDestroyCursorAt(i); - if (IsDynamaxed(gActiveBattler)) + if ((gBattleStruct->dynamax.playerSelect && CanDynamax(gActiveBattler)) + || IsDynamaxed(gActiveBattler)) StringCopy(gDisplayedStringBattle, gMoveNames[GetMaxMove(gActiveBattler, moveInfo->moves[i])]); else StringCopy(gDisplayedStringBattle, gMoveNames[moveInfo->moves[i]]); @@ -2867,6 +2885,7 @@ static void PlayerHandleChooseMove(void) InitMoveSelectionsVarsAndStrings(); gBattleStruct->mega.playerSelect = FALSE; + gBattleStruct->dynamax.playerSelect = FALSE; if (!IsMegaTriggerSpriteActive()) gBattleStruct->mega.triggerSpriteId = 0xFF; if (CanMegaEvolve(gActiveBattler)) diff --git a/src/battle_dynamax.c b/src/battle_dynamax.c index bd3d24584d..6980c532e6 100644 --- a/src/battle_dynamax.c +++ b/src/battle_dynamax.c @@ -84,20 +84,52 @@ static const struct GMaxMove sGMaxMoveTable[] = extern u8 gMaxMovePowers[MOVES_COUNT]; +// Returns whether a battler is Dynamaxed. bool8 IsDynamaxed(u16 battlerId) { - if (/*IsRaidBoss(battlerId) ||*/ gBattleStruct->dynamax.dynamaxTurns[battlerId] > 0) + u8 side = GetBattlerSide(battlerId); + if (gBattleStruct->dynamax.dynamaxed[battlerId] + /*|| IsRaidBoss(battlerId)*/) return TRUE; return FALSE; } +// Returns whether a battler can Dynamax. +bool8 CanDynamax(u16 battlerId) +{ + // TODO: Requires Dynamax Band if not in a Max Raid (as well as special flag). + if (!gBattleStruct->dynamax.alreadyDynamaxed[GetBattlerSide(battlerId)] + && !gBattleStruct->dynamax.dynamaxed[battlerId] + && !gBattleStruct->dynamax.dynamaxed[BATTLE_PARTNER(battlerId)] + && !gBattleStruct->dynamax.toDynamax[BATTLE_PARTNER(battlerId)]) + return TRUE; + return FALSE; +} + +// Sets flags used for Dynamaxing. +void PrepareBattlerForDynamax(u16 battlerId) +{ + u8 side = GetBattlerSide(battlerId); + gBattleStruct->dynamax.alreadyDynamaxed[side] = TRUE; + gBattleStruct->dynamax.dynamaxed[battlerId] = TRUE; + gBattleStruct->dynamax.dynamaxTurns[battlerId] = DYNAMAX_TURNS; +} + +void UndoDynamax(u16 battlerId) +{ + u8 side = GetBattlerSide(battlerId); + gBattleStruct->dynamax.dynamaxed[battlerId] = FALSE; + gBattleStruct->dynamax.dynamaxTurns[battlerId] = 0; // safety check for switch-in / faint + // TODO: Undo Gigantamax form change. +} + // Returns whether a move should be converted into a Max Move. bool8 ShouldUseMaxMove(u16 battlerId, u16 baseMove) { // TODO: Raids //if (IsRaidBoss(battlerId)) // return !IsRaidBossUsingRegularMove(battlerId, baseMove); - return IsDynamaxed(battlerId); + return IsDynamaxed(battlerId) || gBattleStruct->dynamax.toDynamax[battlerId]; } static u16 GetTypeBasedMaxMove(u16 battlerId, u16 type) diff --git a/src/battle_main.c b/src/battle_main.c index 15414d7221..7db1c979cc 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3169,6 +3169,9 @@ void SwitchInClearSetData(void) // Reset G-Max Chi Strike boosts. gBattleStruct->bonusCritStages[gActiveBattler] = 0; + // Reset Dynamax flags. + UndoDynamax(gActiveBattler); + gBattleStruct->overwrittenAbilities[gActiveBattler] = ABILITY_NONE; Ai_UpdateSwitchInData(gActiveBattler); @@ -3272,6 +3275,7 @@ void FaintClearSetData(void) UndoFormChange(gBattlerPartyIndexes[gActiveBattler], GET_BATTLER_SIDE(gActiveBattler), FALSE); if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) UndoMegaEvolution(gBattlerPartyIndexes[gActiveBattler]); + // TODO: UndoDynamax gBattleStruct->overwrittenAbilities[gActiveBattler] = ABILITY_NONE; @@ -4267,11 +4271,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; + *(gBattleStruct->chosenMovePositions + gActiveBattler) = gBattleResources->bufferB[gActiveBattler][2] & ~RET_MEGA_EVOLUTION & ~RET_DYNAMAX; 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_DYNAMAX) + gBattleStruct->dynamax.toDynamax[gActiveBattler] = TRUE; if (ShouldUseMaxMove(gActiveBattler, gChosenMoveByBattler[gActiveBattler])) // max move check { gBattleStruct->dynamax.moveSlot[gActiveBattler] = gBattleStruct->chosenMovePositions[gActiveBattler]; @@ -4888,6 +4894,7 @@ void SpecialStatusesClear(void) memset(&gSpecialStatuses, 0, sizeof(gSpecialStatuses)); } +// TODO: Rename function to denote use for Dynamax, too. static void CheckMegaEvolutionBeforeTurn(void) { if (!(gHitMarker & HITMARKER_RUN)) @@ -4896,6 +4903,16 @@ static void CheckMegaEvolutionBeforeTurn(void) { gActiveBattler = gBattlerAttacker = gBattleStruct->mega.battlerId; gBattleStruct->mega.battlerId++; + // Dynamax Check + if (gBattleStruct->dynamax.toDynamax[gActiveBattler]) + { + gBattleStruct->dynamax.toDynamax[gActiveBattler] = FALSE; + gBattleScripting.battler = gActiveBattler; + PrepareBattlerForDynamax(gActiveBattler); + BattleScriptExecute(BattleScript_DynamaxBegins); + return; + } + // Mega Evo Check if (gBattleStruct->mega.toEvolve & gBitTable[gActiveBattler] && !(gProtectStructs[gActiveBattler].noValidMoves)) { @@ -5265,6 +5282,7 @@ static void HandleEndTurn_FinishBattle(void) UndoMegaEvolution(i); UndoFormChange(i, B_SIDE_PLAYER, FALSE); DoBurmyFormChange(i); + // TODO: UndoDynamax(i) } #if B_RECALCULATE_STATS >= GEN_5 // Recalculate the stats of every party member before the end diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 6144be8c1b..ecf96f8150 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -11348,6 +11348,23 @@ static void Cmd_various(void) } return; } + case VARIOUS_APPLY_DYNAMAX_HP_MULTIPLIER: + { + VARIOUS_ARGS(); + + if (gBattleMons[gBattleScripting.battler].species != SPECIES_SHEDINJA) + { + u16 multiplier = UQ_4_12(2.0); // placeholder; TODO: Dynamax level + + gBattleMons[gBattleScripting.battler].hp = UQ_4_12_TO_INT((gBattleMons[gBattleScripting.battler].hp * multiplier) + UQ_4_12_ROUND); + gBattleMons[gBattleScripting.battler].maxHP = UQ_4_12_TO_INT((gBattleMons[gBattleScripting.battler].maxHP * multiplier) + UQ_4_12_ROUND); + + BtlController_EmitSetMonData(BUFFER_A, REQUEST_MAX_HP_BATTLE, 0, sizeof(gBattleMons[gBattleScripting.battler].maxHP), &gBattleMons[gBattleScripting.battler].maxHP); + BtlController_EmitSetMonData(BUFFER_A, REQUEST_HP_BATTLE, 0, sizeof(gBattleMons[gBattleScripting.battler].hp), &gBattleMons[gBattleScripting.battler].hp); + MarkBattlerForControllerExec(gBattleScripting.battler); + } + break; + } } // End of switch (cmd->id) gBattlescriptCurrInstr = cmd->nextInstr; diff --git a/src/battle_util.c b/src/battle_util.c index 1ab09ed764..626c1be05e 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -1746,7 +1746,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_DYNAMAX; u32 move = gBattleMons[gActiveBattler].moves[moveId]; u32 holdEffect = GetBattlerHoldEffect(gActiveBattler, TRUE); u16 *choicedMove = &gBattleStruct->choicedMove[gActiveBattler]; @@ -2674,7 +2674,8 @@ enum ENDTURN_SLOW_START, ENDTURN_PLASMA_FISTS, ENDTURN_CUD_CHEW, - ENDTURN_TORMENT, + ENDTURN_TORMENT, // supposedly this goes after Taunt, before Encore, but Encore is first right now? + ENDTURN_DYNAMAX, ENDTURN_BATTLER_COUNT }; @@ -3232,6 +3233,17 @@ u8 DoBattlerEndTurnEffects(void) } gBattleStruct->turnEffectsTracker++; break; + case ENDTURN_DYNAMAX: + if (IsDynamaxed(gActiveBattler) + && --gBattleStruct->dynamax.dynamaxTurns[gActiveBattler] == 0) + { + gBattleScripting.battler = gActiveBattler; + UndoDynamax(gActiveBattler); + BattleScriptExecute(BattleScript_DynamaxEnds); + effect++; + } + gBattleStruct->turnEffectsTracker++; + break; case ENDTURN_BATTLER_COUNT: // done gBattleStruct->turnEffectsTracker = 0; gBattleStruct->turnEffectsBattlerId++;