Add B_LEVEL_UP_NOTIFICATION to improve player QoL when performing multiple level ups (#4901)

Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com>
This commit is contained in:
psf 2025-04-12 01:23:48 -07:00 committed by GitHub
parent 1344d8e9dd
commit e146940f25
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 52 additions and 13 deletions

View File

@ -396,6 +396,7 @@ struct BattleCallbacksStack
struct StatsArray
{
u16 stats[NUM_STATS];
u16 level;
};
struct BattleResources

View File

@ -16,6 +16,7 @@
#define B_SPLIT_EXP GEN_LATEST // In Gen6+, all participating mon get full experience.
#define B_SCALED_EXP GEN_LATEST // In Gen5 and Gen7+, experience is weighted by level difference.
#define B_UNEVOLVED_EXP_MULTIPLIER GEN_LATEST // In Gen6+, if the Pokémon is at or past the level where it would be able to evolve, but it has not, it gets a ~1.2 multiplier to EXP gain. Only applies to Pokémon with EVO_LEVEL method.
#define B_LEVEL_UP_NOTIFICATION GEN_LATEST // In Gen9+, if the Pokémon gets enough experience to level up multiple times, the message is only displayed once.
// Stat settings
#define B_BADGE_BOOST GEN_LATEST // In Gen4+, Gym Badges no longer boost a Pokémon's stats.

View File

@ -658,6 +658,7 @@ void SetMonMoveSlot(struct Pokemon *mon, u16 move, u8 slot);
void SetBattleMonMoveSlot(struct BattlePokemon *mon, u16 move, u8 slot);
void GiveMonInitialMoveset(struct Pokemon *mon);
void GiveBoxMonInitialMoveset(struct BoxPokemon *boxMon);
u16 MonTryLearningNewMoveAtLevel(struct Pokemon *mon, bool32 firstMove, u32 level);
u16 MonTryLearningNewMove(struct Pokemon *mon, bool8 firstMove);
void DeleteFirstMoveAndGiveMoveToMon(struct Pokemon *mon, u16 move);
void DeleteFirstMoveAndGiveMoveToBoxMon(struct BoxPokemon *boxMon, u16 move);

View File

@ -1453,11 +1453,13 @@ 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 expAfterGain = currExp + gainedExp;
u32 oldMaxHP = GetMonData(mon, MON_DATA_MAX_HP);
if (currExp + gainedExp >= nextLvlExp)
if (expAfterGain >= nextLvlExp)
{
SetMonData(mon, MON_DATA_EXP, &nextLvlExp);
SetMonData(mon, MON_DATA_EXP, (B_LEVEL_UP_NOTIFICATION >= GEN_9) ? &expAfterGain : &nextLvlExp);
CalculateMonStats(mon);
// Reapply Dynamax HP multiplier after stats are recalculated.
@ -1465,7 +1467,7 @@ static void Task_GiveExpToMon(u8 taskId)
DynamaxModifyHPLevelUp(mon, battler, oldMaxHP);
gainedExp -= nextLvlExp - currExp;
BtlController_EmitTwoReturnValues(battler, BUFFER_B, RET_VALUE_LEVELED_UP, gainedExp);
BtlController_EmitTwoReturnValues(battler, BUFFER_B, RET_VALUE_LEVELED_UP, (B_LEVEL_UP_NOTIFICATION >= GEN_9) ? 0 : gainedExp);
if (IsDoubleBattle() == TRUE
&& (monId == gBattlerPartyIndexes[battler] || monId == gBattlerPartyIndexes[BATTLE_PARTNER(battler)]))
@ -1509,7 +1511,7 @@ static void Task_PrepareToGiveExpWithExpBar(u8 taskId)
static void Task_GiveExpWithExpBar(u8 taskId)
{
u8 level;
u32 level, expAfterGain;
u16 species;
u32 oldMaxHP;
s32 currExp, expOnNextLvl, newExpPoints;
@ -1536,9 +1538,14 @@ static void Task_GiveExpWithExpBar(u8 taskId)
oldMaxHP = GetMonData(mon, MON_DATA_MAX_HP);
expOnNextLvl = gExperienceTables[gSpeciesInfo[species].growthRate][level + 1];
if (currExp + gainedExp >= expOnNextLvl)
expAfterGain = currExp + gainedExp;
if (expAfterGain >= expOnNextLvl)
{
SetMonData(mon, MON_DATA_EXP, &expOnNextLvl);
if (B_LEVEL_UP_NOTIFICATION >= GEN_9)
SetMonData(mon, MON_DATA_EXP, &expAfterGain);
else
SetMonData(mon, MON_DATA_EXP, &expOnNextLvl);
CalculateMonStats(mon);
// Reapply Dynamax HP multiplier after stats are recalculated.
@ -1546,7 +1553,7 @@ static void Task_GiveExpWithExpBar(u8 taskId)
DynamaxModifyHPLevelUp(mon, battler, oldMaxHP);
gainedExp -= expOnNextLvl - currExp;
BtlController_EmitTwoReturnValues(battler, BUFFER_B, RET_VALUE_LEVELED_UP, gainedExp);
BtlController_EmitTwoReturnValues(battler, BUFFER_B, RET_VALUE_LEVELED_UP, (B_LEVEL_UP_NOTIFICATION >= GEN_9) ? 0 : gainedExp);
gTasks[taskId].func = Task_LaunchLvlUpAnim;
}
else

View File

@ -5100,6 +5100,7 @@ static void Cmd_getexp(void)
u32 holdEffect;
s32 i; // also used as stringId
u8 *expMonId = &gBattleStruct->expGetterMonId;
u32 currLvl;
gBattlerFainted = GetBattlerForBattleScript(cmd->battler);
@ -5314,7 +5315,8 @@ static void Cmd_getexp(void)
if (gBattleControllerExecFlags == 0)
{
gBattleResources->bufferB[gBattleStruct->expGetterBattlerId][0] = 0;
if (GetMonData(&gPlayerParty[*expMonId], MON_DATA_HP) && GetMonData(&gPlayerParty[*expMonId], MON_DATA_LEVEL) != MAX_LEVEL)
currLvl = GetMonData(&gPlayerParty[*expMonId], MON_DATA_LEVEL);
if (GetMonData(&gPlayerParty[*expMonId], MON_DATA_HP) && currLvl != MAX_LEVEL)
{
gBattleResources->beforeLvlUp->stats[STAT_HP] = GetMonData(&gPlayerParty[*expMonId], MON_DATA_MAX_HP);
gBattleResources->beforeLvlUp->stats[STAT_ATK] = GetMonData(&gPlayerParty[*expMonId], MON_DATA_ATK);
@ -5322,6 +5324,7 @@ static void Cmd_getexp(void)
gBattleResources->beforeLvlUp->stats[STAT_SPEED] = GetMonData(&gPlayerParty[*expMonId], MON_DATA_SPEED);
gBattleResources->beforeLvlUp->stats[STAT_SPATK] = GetMonData(&gPlayerParty[*expMonId], MON_DATA_SPATK);
gBattleResources->beforeLvlUp->stats[STAT_SPDEF] = GetMonData(&gPlayerParty[*expMonId], MON_DATA_SPDEF);
gBattleResources->beforeLvlUp->level = currLvl;
BtlController_EmitExpUpdate(gBattleStruct->expGetterBattlerId, BUFFER_A, *expMonId, gBattleStruct->battlerExpReward);
MarkBattlerForControllerExec(gBattleStruct->expGetterBattlerId);
@ -8643,10 +8646,32 @@ static void Cmd_handlelearnnewmove(void)
{
CMD_ARGS(const u8 *learnedMovePtr, const u8 *nothingToLearnPtr, bool8 isFirstMove);
u16 learnMove = MOVE_NONE;
u32 monId = gBattleStruct->expGetterMonId;
u16 learnMove = MonTryLearningNewMove(&gPlayerParty[monId], cmd->isFirstMove);
while (learnMove == MON_ALREADY_KNOWS_MOVE)
learnMove = MonTryLearningNewMove(&gPlayerParty[monId], FALSE);
if (B_LEVEL_UP_NOTIFICATION >= GEN_9)
{
u32 currLvl = GetMonData(&gPlayerParty[monId], MON_DATA_LEVEL);
while (gBattleResources->beforeLvlUp->level <= currLvl)
{
learnMove = MonTryLearningNewMoveAtLevel(&gPlayerParty[monId], cmd->isFirstMove, gBattleResources->beforeLvlUp->level);
while (learnMove == MON_ALREADY_KNOWS_MOVE)
learnMove = MonTryLearningNewMoveAtLevel(&gPlayerParty[monId], FALSE, gBattleResources->beforeLvlUp->level);
if (learnMove != MOVE_NONE)
break;
gBattleResources->beforeLvlUp->level++;
}
}
else
{
learnMove = MonTryLearningNewMove(&gPlayerParty[monId], cmd->isFirstMove);
while (learnMove == MON_ALREADY_KNOWS_MOVE)
learnMove = MonTryLearningNewMove(&gPlayerParty[monId], FALSE);
}
if (learnMove == MOVE_NONE || RECORDED_WILD_BATTLE)
{

View File

@ -1982,11 +1982,10 @@ void GiveBoxMonInitialMoveset(struct BoxPokemon *boxMon) //Credit: AsparagusEdua
}
}
u16 MonTryLearningNewMove(struct Pokemon *mon, bool8 firstMove)
u16 MonTryLearningNewMoveAtLevel(struct Pokemon *mon, bool32 firstMove, u32 level)
{
u32 retVal = MOVE_NONE;
u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
u8 level = GetMonData(mon, MON_DATA_LEVEL, NULL);
const struct LevelUpMove *learnset = GetSpeciesLevelUpLearnset(species);
// since you can learn more than one move per level
@ -2034,6 +2033,11 @@ u16 MonTryLearningNewMove(struct Pokemon *mon, bool8 firstMove)
return retVal;
}
u16 MonTryLearningNewMove(struct Pokemon *mon, bool8 firstMove)
{
return MonTryLearningNewMoveAtLevel(mon, firstMove, GetMonData(mon, MON_DATA_LEVEL, NULL));
}
void DeleteFirstMoveAndGiveMoveToMon(struct Pokemon *mon, u16 move)
{
s32 i;