diff --git a/include/battle.h b/include/battle.h index 27f7be5173..99c86181f6 100644 --- a/include/battle.h +++ b/include/battle.h @@ -589,7 +589,6 @@ struct DynamaxData u8 dynamaxTurns[MAX_BATTLERS_COUNT]; u16 baseMoves[MAX_BATTLERS_COUNT]; // base move of Max Move u16 lastUsedBaseMove; - u16 levelUpHP; }; struct BattleGimmickData diff --git a/include/battle_dynamax.h b/include/battle_dynamax.h index 20cd4d5e79..9957c37a10 100644 --- a/include/battle_dynamax.h +++ b/include/battle_dynamax.h @@ -58,7 +58,7 @@ enum MaxMoveEffect bool32 CanDynamax(u32 battler); bool32 IsGigantamaxed(u32 battler); -void ApplyDynamaxHPMultiplier(u32 battler, struct Pokemon* mon); +void ApplyDynamaxHPMultiplier(struct Pokemon* mon); void ActivateDynamax(u32 battler); u16 GetNonDynamaxHP(u32 battler); u16 GetNonDynamaxMaxHP(u32 battler); diff --git a/include/battle_util.h b/include/battle_util.h index 5c151665b9..1eacdaa03d 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -286,7 +286,7 @@ u32 GetBattlerMoveTargetType(u32 battler, u32 move); bool32 CanTargetBattler(u32 battlerAtk, u32 battlerDef, u16 move); void CopyMonLevelAndBaseStatsToBattleMon(u32 battler, struct Pokemon *mon); void CopyMonAbilityAndTypesToBattleMon(u32 battler, struct Pokemon *mon); -void RecalcBattlerStats(u32 battler, struct Pokemon *mon); +void RecalcBattlerStats(u32 battler, struct Pokemon *mon, bool32 isDynamaxing); bool32 IsAlly(u32 battlerAtk, u32 battlerDef); bool32 IsGen6ExpShareEnabled(void); bool32 MoveHasAdditionalEffect(u32 move, u32 moveEffect); diff --git a/include/pokemon.h b/include/pokemon.h index 8aca6c58d5..f5493542ae 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -910,5 +910,6 @@ const u8 *GetMoveAnimationScript(u16 moveId); void UpdateDaysPassedSinceFormChange(u16 days); void TrySetDayLimitToFormChange(struct Pokemon *mon); u32 CheckDynamicMoveType(struct Pokemon *mon, u32 move, u32 battler); +uq4_12_t GetDynamaxLevelHPMultiplier(u32 dynamaxLevel, bool32 inverseMultiplier); #endif // GUARD_POKEMON_H diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index 982c9e4f27..059836561c 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -1410,6 +1410,14 @@ void Task_PlayerController_RestoreBgmAfterCry(u8 taskId) #define tExpTask_gainedExp_2 data[4] // Stored as two half-words containing a word. #define tExpTask_frames data[10] +static void DynamaxModifyHPLevelUp(struct Pokemon *mon, u32 battler, u32 oldMaxHP) +{ + ApplyDynamaxHPMultiplier(mon); + gBattleScripting.levelUpHP = GetMonData(mon, MON_DATA_MAX_HP) - oldMaxHP; // overwrite levelUpHP since it overflows + gBattleMons[battler].hp += gBattleScripting.levelUpHP; + SetMonData(mon, MON_DATA_HP, &gBattleMons[battler].hp); +} + static s32 GetTaskExpValue(u8 taskId) { return (u16)(gTasks[taskId].tExpTask_gainedExp_1) | (gTasks[taskId].tExpTask_gainedExp_2 << 16); @@ -1428,21 +1436,16 @@ static void Task_GiveExpToMon(u8 taskId) u8 level = GetMonData(mon, MON_DATA_LEVEL); u32 currExp = GetMonData(mon, MON_DATA_EXP); u32 nextLvlExp = gExperienceTables[gSpeciesInfo[species].growthRate][level + 1]; + u32 oldMaxHP = GetMonData(mon, MON_DATA_MAX_HP); if (currExp + gainedExp >= nextLvlExp) { SetMonData(mon, MON_DATA_EXP, &nextLvlExp); - gBattleStruct->dynamax.levelUpHP = GetMonData(mon, MON_DATA_HP) \ - + UQ_4_12_TO_INT((gBattleScripting.levelUpHP * UQ_4_12(1.5)) + UQ_4_12_ROUND); CalculateMonStats(mon); // Reapply Dynamax HP multiplier after stats are recalculated. if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX && monId == gBattlerPartyIndexes[battler]) - { - ApplyDynamaxHPMultiplier(battler, mon); - gBattleMons[battler].hp = gBattleStruct->dynamax.levelUpHP; - SetMonData(mon, MON_DATA_HP, &gBattleMons[battler].hp); - } + DynamaxModifyHPLevelUp(mon, battler, oldMaxHP); gainedExp -= nextLvlExp - currExp; BtlController_EmitTwoReturnValues(battler, BUFFER_B, RET_VALUE_LEVELED_UP, gainedExp); @@ -1491,6 +1494,7 @@ static void Task_GiveExpWithExpBar(u8 taskId) { u8 level; u16 species; + u32 oldMaxHP; s32 currExp, expOnNextLvl, newExpPoints; if (gTasks[taskId].tExpTask_frames < 13) @@ -1502,31 +1506,27 @@ static void Task_GiveExpWithExpBar(u8 taskId) u8 monId = gTasks[taskId].tExpTask_monId; s32 gainedExp = GetTaskExpValue(taskId); u8 battler = gTasks[taskId].tExpTask_battler; + struct Pokemon *mon = &gPlayerParty[monId]; newExpPoints = MoveBattleBar(battler, gHealthboxSpriteIds[battler], EXP_BAR, 0); SetHealthboxSpriteVisible(gHealthboxSpriteIds[battler]); if (newExpPoints == -1) // The bar has been filled with given exp points. { m4aSongNumStop(SE_EXP); - level = GetMonData(&gPlayerParty[monId], MON_DATA_LEVEL); - currExp = GetMonData(&gPlayerParty[monId], MON_DATA_EXP); - species = GetMonData(&gPlayerParty[monId], MON_DATA_SPECIES); + level = GetMonData(mon, MON_DATA_LEVEL); + currExp = GetMonData(mon, MON_DATA_EXP); + species = GetMonData(mon, MON_DATA_SPECIES); + oldMaxHP = GetMonData(mon, MON_DATA_MAX_HP); expOnNextLvl = gExperienceTables[gSpeciesInfo[species].growthRate][level + 1]; if (currExp + gainedExp >= expOnNextLvl) { - SetMonData(&gPlayerParty[monId], MON_DATA_EXP, &expOnNextLvl); - gBattleStruct->dynamax.levelUpHP = GetMonData(&gPlayerParty[monId], MON_DATA_HP) \ - + UQ_4_12_TO_INT((gBattleScripting.levelUpHP * UQ_4_12(1.5)) + UQ_4_12_ROUND); - CalculateMonStats(&gPlayerParty[monId]); + SetMonData(mon, MON_DATA_EXP, &expOnNextLvl); + CalculateMonStats(mon); // Reapply Dynamax HP multiplier after stats are recalculated. if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX && monId == gBattlerPartyIndexes[battler]) - { - ApplyDynamaxHPMultiplier(battler, &gPlayerParty[monId]); - gBattleMons[battler].hp = gBattleStruct->dynamax.levelUpHP; - SetMonData(&gPlayerParty[monId], MON_DATA_HP, &gBattleMons[battler].hp); - } + DynamaxModifyHPLevelUp(mon, battler, oldMaxHP); gainedExp -= expOnNextLvl - currExp; BtlController_EmitTwoReturnValues(battler, BUFFER_B, RET_VALUE_LEVELED_UP, gainedExp); @@ -1535,7 +1535,7 @@ static void Task_GiveExpWithExpBar(u8 taskId) else { currExp += gainedExp; - SetMonData(&gPlayerParty[monId], MON_DATA_EXP, &currExp); + SetMonData(mon, MON_DATA_EXP, &currExp); gBattlerControllerFuncs[battler] = Controller_WaitForString; DestroyTask(taskId); } diff --git a/src/battle_dynamax.c b/src/battle_dynamax.c index 17425b51d8..0f55b33dab 100644 --- a/src/battle_dynamax.c +++ b/src/battle_dynamax.c @@ -123,22 +123,22 @@ bool32 CanDynamax(u32 battler) // Returns whether a battler is transformed into a Gigantamax form. bool32 IsGigantamaxed(u32 battler) { - struct Pokemon *mon = &GetSideParty(GetBattlerSide(battler))[gBattlerPartyIndexes[battler]]; + struct Pokemon *mon = GetPartyBattlerData(battler); if ((gSpeciesInfo[gBattleMons[battler].species].isGigantamax) && GetMonData(mon, MON_DATA_GIGANTAMAX_FACTOR)) return TRUE; return FALSE; } // Applies the HP Multiplier for Dynamaxed Pokemon and Raid Bosses. -void ApplyDynamaxHPMultiplier(u32 battler, struct Pokemon* mon) +void ApplyDynamaxHPMultiplier(struct Pokemon* mon) { if (GetMonData(mon, MON_DATA_SPECIES) == SPECIES_SHEDINJA) return; else { - u32 scale = 150 + 5 * GetMonData(mon, MON_DATA_DYNAMAX_LEVEL); - u32 hp = (GetMonData(mon, MON_DATA_HP) * scale + 99) / 100; - u32 maxHP = (GetMonData(mon, MON_DATA_MAX_HP) * scale + 99) / 100; + uq4_12_t multiplier = GetDynamaxLevelHPMultiplier(GetMonData(mon, MON_DATA_DYNAMAX_LEVEL), FALSE); + u32 hp = UQ_4_12_TO_INT((GetMonData(mon, MON_DATA_HP) * multiplier) + UQ_4_12_ROUND); + u32 maxHP = UQ_4_12_TO_INT((GetMonData(mon, MON_DATA_MAX_HP) * multiplier) + UQ_4_12_ROUND); SetMonData(mon, MON_DATA_HP, &hp); SetMonData(mon, MON_DATA_MAX_HP, &maxHP); } @@ -151,8 +151,9 @@ u16 GetNonDynamaxHP(u32 battler) return gBattleMons[battler].hp; else { - u16 mult = UQ_4_12(1.0/1.5); // placeholder - u16 hp = UQ_4_12_TO_INT((gBattleMons[battler].hp * mult) + UQ_4_12_ROUND); + struct Pokemon *mon = GetPartyBattlerData(battler); + uq4_12_t mult = GetDynamaxLevelHPMultiplier(GetMonData(mon, MON_DATA_DYNAMAX_LEVEL), TRUE); + u32 hp = UQ_4_12_TO_INT((gBattleMons[battler].hp * mult) + UQ_4_12_ROUND); return hp; } } @@ -164,8 +165,9 @@ u16 GetNonDynamaxMaxHP(u32 battler) return gBattleMons[battler].maxHP; else { - u16 mult = UQ_4_12(1.0/1.5); // placeholder - u16 maxHP = UQ_4_12_TO_INT((gBattleMons[battler].maxHP * mult) + UQ_4_12_ROUND); + struct Pokemon *mon = GetPartyBattlerData(battler); + uq4_12_t mult = GetDynamaxLevelHPMultiplier(GetMonData(mon, MON_DATA_DYNAMAX_LEVEL), TRUE); + u32 maxHP = UQ_4_12_TO_INT((gBattleMons[battler].maxHP * mult) + UQ_4_12_ROUND); return maxHP; } } @@ -202,7 +204,7 @@ void UndoDynamax(u32 battler) if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX) { struct Pokemon *mon = (side == B_SIDE_PLAYER) ? &gPlayerParty[monId] : &gEnemyParty[monId]; - u16 mult = UQ_4_12(1.0/1.5); // placeholder + uq4_12_t mult = GetDynamaxLevelHPMultiplier(GetMonData(mon, MON_DATA_DYNAMAX_LEVEL), TRUE); gBattleMons[battler].hp = UQ_4_12_TO_INT((GetMonData(mon, MON_DATA_HP) * mult + 1) + UQ_4_12_ROUND); // round up SetMonData(mon, MON_DATA_HP, &gBattleMons[battler].hp); CalculateMonStats(mon); @@ -508,10 +510,10 @@ void BS_UpdateDynamax(void) { NATIVE_ARGS(); u32 battler = gBattleScripting.battler; - struct Pokemon *mon = &GetSideParty(GetBattlerSide(battler))[gBattlerPartyIndexes[battler]]; + struct Pokemon *mon = GetPartyBattlerData(battler); if (!IsGigantamaxed(battler)) // RecalcBattlerStats will get called on form change. - RecalcBattlerStats(battler, mon); + RecalcBattlerStats(battler, mon, GetActiveGimmick(battler) == GIMMICK_DYNAMAX); UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], mon, HEALTHBOX_ALL); gBattlescriptCurrInstr = cmd->nextInstr; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 31ae0f2646..dd4e504a1d 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -10157,7 +10157,7 @@ static void Cmd_various(void) // Change stats. else if (cmd->case_ == 1) { - RecalcBattlerStats(battler, mon); + RecalcBattlerStats(battler, mon, FALSE); } // Update healthbox. else diff --git a/src/battle_util.c b/src/battle_util.c index 54d6862c48..c35c072b77 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -11083,7 +11083,7 @@ bool32 TryBattleFormChange(u32 battler, u32 method) TryToSetBattleFormChangeMoves(&party[monId], method); SetMonData(&party[monId], MON_DATA_SPECIES, &targetSpecies); gBattleMons[battler].species = targetSpecies; - RecalcBattlerStats(battler, &party[monId]); + RecalcBattlerStats(battler, &party[monId], method == FORM_CHANGE_BATTLE_GIGANTAMAX); return TRUE; } else if (gBattleStruct->changedSpecies[side][monId] != SPECIES_NONE) @@ -11108,7 +11108,7 @@ bool32 TryBattleFormChange(u32 battler, u32 method) // Reverts the original species TryToSetBattleFormChangeMoves(&party[monId], method); SetMonData(&party[monId], MON_DATA_SPECIES, &gBattleStruct->changedSpecies[side][monId]); - RecalcBattlerStats(battler, &party[monId]); + RecalcBattlerStats(battler, &party[monId], method == FORM_CHANGE_BATTLE_GIGANTAMAX); // Battler data is not updated with regular form's ability, not doing so could cause wrong ability activation. if (method == FORM_CHANGE_FAINT) gBattleMons[battler].ability = abilityForm; @@ -11696,11 +11696,28 @@ void CopyMonAbilityAndTypesToBattleMon(u32 battler, struct Pokemon *mon) gBattleMons[battler].types[2] = TYPE_MYSTERY; } -void RecalcBattlerStats(u32 battler, struct Pokemon *mon) +void RecalcBattlerStats(u32 battler, struct Pokemon *mon, bool32 isDynamaxing) { + u32 hp = GetMonData(mon, MON_DATA_HP); + u32 oldMaxHp = GetMonData(mon, MON_DATA_MAX_HP); CalculateMonStats(mon); if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX && gChosenActionByBattler[battler] != B_ACTION_SWITCH) - ApplyDynamaxHPMultiplier(battler, mon); + { + ApplyDynamaxHPMultiplier(mon); + u32 newMaxHp = GetMonData(mon, MON_DATA_MAX_HP); + if (!isDynamaxing) + { + if (newMaxHp > oldMaxHp) // restore hp gained from changing form, without this, dynamaxed form changes are calculated incorrectly + { + hp += (newMaxHp - oldMaxHp); + SetMonData(mon, MON_DATA_HP, &hp); + } + else + { + SetMonData(mon, MON_DATA_HP, &hp); + } + } + } CopyMonLevelAndBaseStatsToBattleMon(battler, mon); CopyMonAbilityAndTypesToBattleMon(battler, mon); } diff --git a/src/pokemon.c b/src/pokemon.c index c5162ecfd0..d6ed5020b8 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -6978,3 +6978,10 @@ u32 CheckDynamicMoveType(struct Pokemon *mon, u32 move, u32 battler) return moveType; return gMovesInfo[move].type; } + +uq4_12_t GetDynamaxLevelHPMultiplier(u32 dynamaxLevel, bool32 inverseMultiplier) +{ + if (inverseMultiplier) + return UQ_4_12(1.0/(1.5 + 0.05 * dynamaxLevel)); + return UQ_4_12(1.5 + 0.05 * dynamaxLevel); +} diff --git a/test/battle/gimmick/dynamax.c b/test/battle/gimmick/dynamax.c index f910f551a4..0e3ae532bf 100644 --- a/test/battle/gimmick/dynamax.c +++ b/test/battle/gimmick/dynamax.c @@ -7,7 +7,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamax increases HP and max HP by 1.5x", u16 hp) u32 dynamax; PARAMETRIZE { dynamax = GIMMICK_NONE; } PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; } - GIVEN { // TODO: Dynamax level + GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -25,6 +25,49 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamax increases HP and max HP by 1.5x", u16 hp) } } +SINGLE_BATTLE_TEST("(DYNAMAX) Dynamax Level increases HP and max HP multipliers by 0.05 for each level", u16 hp) +{ + u32 dynamax, level; + PARAMETRIZE { dynamax = GIMMICK_NONE; level = 0; } + PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; level = 0; } + PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; level = 1; } + PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; level = 2; } + PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; level = 3; } + PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; level = 4; } + PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; level = 5; } + PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; level = 6; } + PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; level = 7; } + PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; level = 8; } + PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; level = 9; } + PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; level = 10; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { DynamaxLevel(level); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TACKLE, gimmick: dynamax); MOVE(opponent, MOVE_CELEBRATE); } + } SCENE { + if (dynamax) { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_DYNAMAX_GROWTH, player); + MESSAGE("Wobbuffet used Max Strike!"); + } + MESSAGE("The opposing Wobbuffet used Celebrate!"); + } THEN { + results[i].hp = player->hp; + } FINALLY { + EXPECT_MUL_EQ(results[0].hp, Q_4_12(1.5), results[1].hp); + EXPECT_MUL_EQ(results[0].hp, Q_4_12(1.55), results[2].hp); + EXPECT_MUL_EQ(results[0].hp, Q_4_12(1.6), results[3].hp); + EXPECT_MUL_EQ(results[0].hp, Q_4_12(1.65), results[4].hp); + EXPECT_MUL_EQ(results[0].hp, Q_4_12(1.7), results[5].hp); + EXPECT_MUL_EQ(results[0].hp, Q_4_12(1.75), results[6].hp); + EXPECT_MUL_EQ(results[0].hp, Q_4_12(1.8), results[7].hp); + EXPECT_MUL_EQ(results[0].hp, Q_4_12(1.85), results[8].hp); + EXPECT_MUL_EQ(results[0].hp, Q_4_12(1.9), results[9].hp); + EXPECT_MUL_EQ(results[0].hp, Q_4_12(1.95), results[10].hp); + EXPECT_MUL_EQ(results[0].hp, Q_4_12(2.0), results[11].hp); + } +} + SINGLE_BATTLE_TEST("(DYNAMAX) Dynamax expires after three turns", u16 hp) { u32 dynamax; @@ -38,8 +81,8 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamax expires after three turns", u16 hp) TURN { MOVE(player, MOVE_TACKLE); } // 2nd max move TURN { MOVE(player, MOVE_TACKLE); } // 3rd max move } SCENE { - int i; - for (i = 0; i < DYNAMAX_TURNS_COUNT; ++i) { + int j; + for (j = 0; j < DYNAMAX_TURNS_COUNT; ++j) { if (dynamax) MESSAGE("Wobbuffet used Max Strike!"); else @@ -55,6 +98,49 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamax expires after three turns", u16 hp) } } +SINGLE_BATTLE_TEST("(DYNAMAX) Dynamax expires after three turns and correctly converts HP according to Dynamax Level") +{ + u32 dynamaxLevel, dynamax; + u16 capturedHP, finalHP; + s16 capturedDamage; + PARAMETRIZE { dynamaxLevel = 0; dynamax = GIMMICK_NONE; } + PARAMETRIZE { dynamaxLevel = 0; dynamax = GIMMICK_DYNAMAX; } + PARAMETRIZE { dynamaxLevel = 1; dynamax = GIMMICK_DYNAMAX; } + PARAMETRIZE { dynamaxLevel = 2; dynamax = GIMMICK_DYNAMAX; } + PARAMETRIZE { dynamaxLevel = 3; dynamax = GIMMICK_DYNAMAX; } + PARAMETRIZE { dynamaxLevel = 4; dynamax = GIMMICK_DYNAMAX; } + PARAMETRIZE { dynamaxLevel = 5; dynamax = GIMMICK_DYNAMAX; } + PARAMETRIZE { dynamaxLevel = 6; dynamax = GIMMICK_DYNAMAX; } + PARAMETRIZE { dynamaxLevel = 7; dynamax = GIMMICK_DYNAMAX; } + PARAMETRIZE { dynamaxLevel = 8; dynamax = GIMMICK_DYNAMAX; } + PARAMETRIZE { dynamaxLevel = 9; dynamax = GIMMICK_DYNAMAX; } + PARAMETRIZE { dynamaxLevel = 10; dynamax = GIMMICK_DYNAMAX; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { DynamaxLevel(dynamaxLevel); HP(200); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE, gimmick: dynamax); } + TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_TACKLE); } + TURN { } + } SCENE { + if (dynamax) + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_DYNAMAX_GROWTH, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + if (dynamax) + HP_BAR(player, captureHP: &capturedHP); + else + HP_BAR(player, captureDamage: &capturedDamage); + if (dynamax) + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player); + } THEN { + finalHP = player->hp; + if (dynamax) + EXPECT_MUL_EQ(finalHP, GetDynamaxLevelHPMultiplier(dynamaxLevel, FALSE), capturedHP); + EXPECT_LE(finalHP, 200); + EXPECT_GE(finalHP, 200 - capturedDamage); + } +} + SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon cannot be flinched") { GIVEN { @@ -311,6 +397,47 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon lose their substitutes") } } +SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon that changes forms does not gain HP") +{ + u16 capturedHP, finalHP; + GIVEN { + PLAYER(SPECIES_GRENINJA_BATTLE_BOND) { Ability(ABILITY_BATTLE_BOND); HP(100); Speed(100); } + OPPONENT(SPECIES_CATERPIE) { HP(1); Speed(1000); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(10); } + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); SEND_OUT(opponent, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_DYNAMAX_GROWTH, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + HP_BAR(player, captureHP: &capturedHP); + ANIMATION(ANIM_TYPE_MOVE, MOVE_MAX_STRIKE, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player); + } THEN { + finalHP = player->hp; + EXPECT_EQ(capturedHP, finalHP); + } +} + +SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon that changes forms does not gain HP unless the new form gains Max HP") +{ + u32 hp = 1, maxHP = 200; + u32 species; + PARAMETRIZE { species = SPECIES_ZYGARDE_10_POWER_CONSTRUCT; } + PARAMETRIZE { species = SPECIES_ZYGARDE_50_POWER_CONSTRUCT; } + GIVEN { + PLAYER(species) { Ability(ABILITY_POWER_CONSTRUCT); HP(hp); MaxHP(maxHP); DynamaxLevel(0); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_DYNAMAX_GROWTH, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_MAX_STRIKE, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player); + } THEN { + EXPECT_MUL_EQ(maxHP - hp, GetDynamaxLevelHPMultiplier(0, FALSE), player->maxHP - player->hp); + } +} + SINGLE_BATTLE_TEST("(DYNAMAX) Max Moves deal 1/4 damage through protect", s16 damage) { bool32 protected;