From 2bcfaf8b574d3e693af57d7010b0888fbd602ba8 Mon Sep 17 00:00:00 2001 From: AgustinGDLV Date: Fri, 24 Mar 2023 22:06:09 -0700 Subject: [PATCH] CanDynamax rewrite, level-up + misc. bug fixes --- include/battle.h | 1 + include/battle_dynamax.h | 2 +- include/config/battle.h | 1 + src/battle_controller_player.c | 10 ++++ src/battle_controller_player_partner.c | 10 ++++ src/battle_dynamax.c | 74 ++++++++++++++++---------- src/battle_util.c | 2 +- test/dynamax.c | 4 -- 8 files changed, 71 insertions(+), 33 deletions(-) diff --git a/include/battle.h b/include/battle.h index 5366ce75e9..8e33b7e36c 100644 --- a/include/battle.h +++ b/include/battle.h @@ -532,6 +532,7 @@ struct DynamaxData u8 splits[MAX_BATTLERS_COUNT]; u16 baseMove[MAX_BATTLERS_COUNT]; // base move of Max Move u16 lastUsedBaseMove; + u16 beforeLevelHP; }; struct StolenItem diff --git a/include/battle_dynamax.h b/include/battle_dynamax.h index d5cb0c2a5b..64655a9c98 100644 --- a/include/battle_dynamax.h +++ b/include/battle_dynamax.h @@ -58,7 +58,7 @@ enum MaxMoveEffect bool32 IsDynamaxed(u16 battlerId); bool32 CanDynamax(u16 battlerId); -void ApplyDynamaxHPMultiplier(u16 battlerId, struct Pokemon* mon); +void ApplyDynamaxHPMultiplier(struct Pokemon* mon); void PrepareBattlerForDynamax(u16 battlerId); u16 GetNonDynamaxHP(u16 battlerId); u16 GetNonDynamaxMaxHP(u16 battlerId); diff --git a/include/config/battle.h b/include/config/battle.h index eeeaa3bd4e..b971a36db7 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -148,6 +148,7 @@ #define B_SMART_WILD_AI_FLAG 0 // If not 0, you can set this flag in a script to enable smart wild pokemon #define B_FLAG_NO_BAG_USE 0 // If this flag is set, the ability to use the bag in battle is disabled. #define B_FLAG_NO_CATCHING 0 // If this flag is set, the ability to catch wild Pokémon is disabled. +#define B_FLAG_DYNAMAX_BATTLE 0 // If this flag is set, the ability to Dynamax in battle is enabled for all trainers. // Var Settings // To use the following features in scripting, replace the 0s with the var ID you're assigning it to. diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index 3d8b8306e4..788146c7e1 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -1418,7 +1418,17 @@ static void Task_GiveExpToMon(u8 taskId) u8 savedActiveBattler; SetMonData(mon, MON_DATA_EXP, &nextLvlExp); + gBattleStruct->dynamax.beforeLevelHP = GetMonData(mon, MON_DATA_HP); CalculateMonStats(mon); + + // Prevent Dynamaxed HP from being reset upon level-up. + if (IsDynamaxed(battlerId)) + { + ApplyDynamaxHPMultiplier(mon); + gBattleMons[battlerId].hp = gBattleStruct->dynamax.beforeLevelHP; + SetMonData(mon, MON_DATA_HP, &gBattleMons[battlerId].hp); + } + gainedExp -= nextLvlExp - currExp; savedActiveBattler = gActiveBattler; gActiveBattler = battlerId; diff --git a/src/battle_controller_player_partner.c b/src/battle_controller_player_partner.c index 88bc4f974d..1a8f812a06 100644 --- a/src/battle_controller_player_partner.c +++ b/src/battle_controller_player_partner.c @@ -332,7 +332,17 @@ static void Task_GiveExpToMon(u8 taskId) u8 savedActiveBank; SetMonData(mon, MON_DATA_EXP, &nextLvlExp); + gBattleStruct->dynamax.beforeLevelHP = GetMonData(mon, MON_DATA_HP); CalculateMonStats(mon); + + // Prevent Dynamaxed HP from being reset upon level-up. + if (IsDynamaxed(battlerId)) + { + ApplyDynamaxHPMultiplier(mon); + gBattleMons[battlerId].hp = gBattleStruct->dynamax.beforeLevelHP; + SetMonData(mon, MON_DATA_HP, &gBattleMons[battlerId].hp); + } + gainedExp -= nextLvlExp - currExp; savedActiveBank = gActiveBattler; gActiveBattler = battlerId; diff --git a/src/battle_dynamax.c b/src/battle_dynamax.c index eeb36fc2e0..e9757a3bfd 100644 --- a/src/battle_dynamax.c +++ b/src/battle_dynamax.c @@ -6,6 +6,7 @@ #include "battle_scripts.h" #include "battle_script_commands.h" #include "data.h" +#include "event_data.h" #include "graphics.h" #include "item.h" #include "pokemon.h" @@ -16,6 +17,7 @@ #include "constants/abilities.h" #include "constants/battle_move_effects.h" #include "constants/battle_string_ids.h" +#include "constants/flags.h" #include "constants/hold_effects.h" #include "constants/items.h" #include "constants/moves.h" @@ -104,33 +106,54 @@ bool32 IsDynamaxed(u16 battlerId) // Returns whether a battler can Dynamax. bool32 CanDynamax(u16 battlerId) { - // TODO: Requires Dynamax Band if not in a Max Raid (as well as special flag). u16 species = gBattleMons[battlerId].species; u16 holdEffect = ItemId_GetHoldEffect(gBattleMons[battlerId].item); - if (!gBattleStruct->dynamax.alreadyDynamaxed[GetBattlerSide(battlerId)] - && !gBattleStruct->dynamax.dynamaxed[battlerId] - && !gBattleStruct->dynamax.dynamaxed[BATTLE_PARTNER(battlerId)] - && !gBattleStruct->dynamax.toDynamax[BATTLE_PARTNER(battlerId)] - && species != SPECIES_ZACIAN && species != SPECIES_ZACIAN_CROWNED_SWORD - && species != SPECIES_ZAMAZENTA && species != SPECIES_ZAMAZENTA_CROWNED_SHIELD - && species != SPECIES_ETERNATUS && species != SPECIES_ETERNATUS_ETERNAMAX - && holdEffect != HOLD_EFFECT_MEGA_STONE && holdEffect != HOLD_EFFECT_Z_CRYSTAL) - return TRUE; - return FALSE; + + // Check if Dynamax battle flag is set. This needs to be defined in include/config/battle.h + #if B_FLAG_DYNAMAX_BATTLE != 0 + if (!FlagGet(B_FLAG_DYNAMAX_BATTLE)) + #endif + return FALSE; + + // Check if Player has a Dynamax Band. + if ((GetBattlerPosition(battlerId) == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && GetBattlerPosition(battlerId) == B_POSITION_PLAYER_RIGHT)) + && !CheckBagHasItem(ITEM_DYNAMAX_BAND, 1)) + return FALSE; + + // Check if species isn't allowed to Dynamax. + if (species == SPECIES_ZACIAN && species == SPECIES_ZACIAN_CROWNED_SWORD + && species == SPECIES_ZAMAZENTA && species == SPECIES_ZAMAZENTA_CROWNED_SHIELD + && species == SPECIES_ETERNATUS && species == SPECIES_ETERNATUS_ETERNAMAX) + return FALSE; + + // Cannot Dynamax if you can Mega Evolve or use a Z-Move + if (holdEffect == HOLD_EFFECT_MEGA_STONE && holdEffect == HOLD_EFFECT_Z_CRYSTAL) + return FALSE; + + // Cannot Dynamax if your side has already or will Dynamax. + if (gBattleStruct->dynamax.alreadyDynamaxed[GetBattlerSide(battlerId)] + || gBattleStruct->dynamax.dynamaxed[BATTLE_PARTNER(battlerId)] + || gBattleStruct->dynamax.toDynamax[BATTLE_PARTNER(battlerId)]) + return FALSE; + + // TODO: Cannot Dynamax in a Max Raid if you don't have Dynamax Energy. + // if (gBattleTypeFlags & BATTLE_TYPE_RAID && gBattleStruct->raid.dynamaxEnergy != battlerId) + // return FALSE; + + // No checks failed, all set! + return TRUE; } // Applies the HP Multiplier for Dynamaxed Pokemon and Raid Bosses. -void ApplyDynamaxHPMultiplier(u16 battlerId, struct Pokemon* mon) +void ApplyDynamaxHPMultiplier(struct Pokemon* mon) { - if (gBattleMons[battlerId].species == SPECIES_SHEDINJA) - { + if (GetMonData(mon, MON_DATA_SPECIES) == SPECIES_SHEDINJA) return; - } else { u16 mult = UQ_4_12(1.5); // placeholder - u16 hp = UQ_4_12_TO_INT((gBattleMons[battlerId].hp * mult) + UQ_4_12_ROUND); - u16 maxHP = UQ_4_12_TO_INT((gBattleMons[battlerId].maxHP * mult) + UQ_4_12_ROUND); + u16 hp = UQ_4_12_TO_INT((GetMonData(mon, MON_DATA_HP) * mult) + UQ_4_12_ROUND); + u16 maxHP = UQ_4_12_TO_INT((GetMonData(mon, MON_DATA_MAX_HP) * mult) + UQ_4_12_ROUND); SetMonData(mon, MON_DATA_HP, &hp); SetMonData(mon, MON_DATA_MAX_HP, &maxHP); } @@ -140,9 +163,7 @@ void ApplyDynamaxHPMultiplier(u16 battlerId, struct Pokemon* mon) u16 GetNonDynamaxHP(u16 battlerId) { if (!IsDynamaxed(battlerId) || gBattleMons[battlerId].species == SPECIES_SHEDINJA) - { return gBattleMons[battlerId].hp; - } else { u16 mult = UQ_4_12(1.0/1.5); // placeholder @@ -155,9 +176,7 @@ u16 GetNonDynamaxHP(u16 battlerId) u16 GetNonDynamaxMaxHP(u16 battlerId) { if (!IsDynamaxed(battlerId) || gBattleMons[battlerId].species == SPECIES_SHEDINJA) - { return gBattleMons[battlerId].maxHP; - } else { u16 mult = UQ_4_12(1.0/1.5); // placeholder @@ -184,7 +203,8 @@ void PrepareBattlerForDynamax(u16 battlerId) gBattleStruct->choicedMove[battlerId] = MOVE_NONE; // Try Gigantamax form change. - TryBattleFormChange(battlerId, FORM_CHANGE_BATTLE_GIGANTAMAX); + if (!(gBattleMons[battlerId].status2 & STATUS2_TRANSFORMED)) // Ditto cannot Gigantamax. + TryBattleFormChange(battlerId, FORM_CHANGE_BATTLE_GIGANTAMAX); } // Unsets the flags used for Dynamaxing and reverts max HP if needed. @@ -208,7 +228,7 @@ void UndoDynamax(u16 battlerId) // Weight-based moves (and some other moves in Raids) are blocked by Dynamax. bool32 IsMoveBlockedByDynamax(u16 move) { - // TODO: Raid moves + // TODO: Certain moves are banned in raids. switch (gBattleMoves[move].effect) { case EFFECT_HEAT_CRASH: @@ -221,9 +241,9 @@ bool32 IsMoveBlockedByDynamax(u16 move) // Returns whether a move should be converted into a Max Move. bool32 ShouldUseMaxMove(u16 battlerId, u16 baseMove) { - // TODO: Raids - //if (IsRaidBoss(battlerId)) - // return !IsRaidBossUsingRegularMove(battlerId, baseMove); + // TODO: Raid bosses do not always use Max Moves. + // if (IsRaidBoss(battlerId)) + // return !IsRaidBossUsingRegularMove(battlerId, baseMove); return IsDynamaxed(battlerId) || gBattleStruct->dynamax.toDynamax[battlerId]; } @@ -263,7 +283,7 @@ u16 GetMaxMove(u16 battlerId, u16 baseMove) { move = MOVE_MAX_GUARD; } - else if (gBattleStruct->dynamicMoveType) // unsure of how to deal with Hidden Power + else if (gBattleStruct->dynamicMoveType) { move = GetTypeBasedMaxMove(battlerId, gBattleStruct->dynamicMoveType & DYNAMIC_TYPE_MASK); gBattleStruct->dynamax.splits[battlerId] = gBattleMoves[baseMove].split; diff --git a/src/battle_util.c b/src/battle_util.c index 35aff19e67..34f5db6416 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -10969,7 +10969,7 @@ void RecalcBattlerStats(u32 battler, struct Pokemon *mon) { CalculateMonStats(mon); if (IsDynamaxed(battler) && gChosenActionByBattler[battler] != B_ACTION_SWITCH) - ApplyDynamaxHPMultiplier(battler, mon); + ApplyDynamaxHPMultiplier(mon); CopyMonLevelAndBaseStatsToBattleMon(battler, mon); CopyMonAbilityAndTypesToBattleMon(battler, mon); } diff --git a/test/dynamax.c b/test/dynamax.c index b98c808fc4..8815964289 100644 --- a/test/dynamax.c +++ b/test/dynamax.c @@ -7,11 +7,7 @@ // TEST: Imprison doesn't stop Max Moves. (YES!) // TEST: Max Moves change type as you'd expect with Normalize, Weather Ball, etc. (YES!) // TEST: You use Struggle while Dynamaxed if out of PP. (YES!) -// Refactor code to remove dynamax.usingMaxMove? Might keep for Raids -// Ditto cannot turn into a Gigantamax form. (NO) -// Interactions with a Dynamaxed Pokemon with Magic Bounce. (???) // Dynamax should not reset Speed Swap, Soak, or anything else from form changing. (NO) -// Multi Attack is treated in Max Move power calcs like a Fighting or Poison type move. (NO) // Max Moves cannot be used against allies. (NO) // ============= DYNAMAX AND MAX MOVE INTERACTIONS ===================