New Feature: Level Caps (#3632)
* New Feature: Level Caps * B_LEVEL_CAP_EXP_UP and fixes * 1 exp fix for hard level caps * remove 1 exp hack * Reviews applied * fix u8/u16 --------- Co-authored-by: Bassoonian <iasperbassoonian@gmail.com>
This commit is contained in:
parent
4a3954ff34
commit
c56acb944b
37
include/level_caps.h
Normal file
37
include/level_caps.h
Normal file
@ -0,0 +1,37 @@
|
||||
#ifndef GUARD_LEVEL_CAP_H
|
||||
#define GUARD_LEVEL_CAP_H
|
||||
|
||||
// experience (soft-)caps
|
||||
|
||||
#define EXP_CAP_NONE 0 // Regular behavior, no level caps are applied
|
||||
#define EXP_CAP_HARD 1 // Pokémon with a level >= the level cap cannot gain any experience
|
||||
#define EXP_CAP_SOFT 2 // Pokémon with a level >= the level cap will gain reduced experience
|
||||
|
||||
#define LEVEL_CAP_NONE 0 // No level cap, only applicable if B_EXP_CAP_TYPE is EXP_CAP_NONE
|
||||
#define LEVEL_CAP_FLAG_LIST 1 // Level cap is chosen according to the first unset flag in `sLevelCapFlagMap`
|
||||
#define LEVEL_CAP_VARIABLE 2 // Level cap is chosen according to the contents of the event variable specified by B_LEVEL_CAP_VARIABLE
|
||||
|
||||
#define B_EXP_CAP_TYPE EXP_CAP_NONE // [EXP_CAP_NONE, EXP_CAP_HARD, EXP_CAP_SOFT] choose the type of level cap to apply
|
||||
#define B_LEVEL_CAP_TYPE LEVEL_CAP_NONE // [LEVEL_CAP_NONE, LEVEL_CAP_FLAG_LIST, LEVEL_CAP_VARIABLE] choose the method to derive the level cap
|
||||
#define B_LEVEL_CAP_VARIABLE 0 // event variable used to derive level cap if B_LEVEL_CAP_TYPE is set to LEVEL_CAP_VARIABLE
|
||||
|
||||
#define B_RARE_CANDY_CAP FALSE // If set to true, Rare Candies can't be used to go over the level cap
|
||||
#define B_LEVEL_CAP_EXP_UP FALSE // If set to true, mons under level cap will receive more experience
|
||||
|
||||
#if B_EXP_CAP_TYPE != EXP_CAP_NONE && B_EXP_CAP_TYPE != EXP_CAP_HARD && B_EXP_CAP_TYPE != EXP_CAP_SOFT
|
||||
#error "Invalid choice for B_EXP_CAP_TYPE, must be of [EXP_CAP_NONE, EXP_CAP_HARD, EXP_CAP_SOFT]"
|
||||
#endif
|
||||
|
||||
#if B_EXP_CAP_TYPE == EXP_CAP_HARD || B_EXP_CAP_TYPE == EXP_CAP_SOFT
|
||||
#if B_LEVEL_CAP_TYPE != LEVEL_CAP_FLAG_LIST && B_LEVEL_CAP_TYPE != LEVEL_CAP_VARIABLE
|
||||
#error "Invalid choice for B_LEVEL_CAP_TYPE, must be of [LEVEL_CAP_FLAG_LIST, LEVEL_CAP_VARIABLE]"
|
||||
#endif
|
||||
#if B_LEVEL_CAP_TYPE == LEVEL_CAP_VARIABLE && B_LEVEL_CAP_VARIABLE == 0
|
||||
#error "B_LEVEL_CAP_TYPE set to LEVEL_CAP_VARIABLE, but no variable chosen for B_LEVEL_CAP_VARIABLE, set B_LEVEL_CAP_VARIABLE to a valid event variable"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
u32 GetCurrentLevelCap(void);
|
||||
u32 GetSoftLevelCapExpValue(u32 level, u32 expValue);
|
||||
|
||||
#endif /* GUARD_LEVEL_CAP_H */
|
||||
@ -39,6 +39,7 @@
|
||||
#include "constants/songs.h"
|
||||
#include "constants/trainers.h"
|
||||
#include "constants/rgb.h"
|
||||
#include "level_caps.h"
|
||||
|
||||
static void PlayerBufferExecCompleted(u32 battler);
|
||||
static void PlayerHandleLoadMonSprite(u32 battler);
|
||||
@ -2145,7 +2146,7 @@ void PlayerHandleExpUpdate(u32 battler)
|
||||
u8 monId = gBattleResources->bufferA[battler][1];
|
||||
s32 taskId, expPointsToGive;
|
||||
|
||||
if (GetMonData(&gPlayerParty[monId], MON_DATA_LEVEL) >= MAX_LEVEL)
|
||||
if (GetMonData(&gPlayerParty[monId], MON_DATA_LEVEL) >= GetCurrentLevelCap())
|
||||
{
|
||||
PlayerBufferExecCompleted(battler);
|
||||
}
|
||||
|
||||
@ -33,6 +33,7 @@
|
||||
#include "constants/rgb.h"
|
||||
#include "constants/songs.h"
|
||||
#include "constants/items.h"
|
||||
#include "level_caps.h"
|
||||
|
||||
enum
|
||||
{ // Corresponds to gHealthboxElementsGfxTable (and the tables after it) in graphics.c
|
||||
@ -2687,7 +2688,7 @@ static void MoveBattleBarGraphically(u8 battlerId, u8 whichBar)
|
||||
&gBattleSpritesDataPtr->battleBars[battlerId].currValue,
|
||||
array, B_EXPBAR_PIXELS / 8);
|
||||
level = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerId]], MON_DATA_LEVEL);
|
||||
if (level == MAX_LEVEL)
|
||||
if (level >= GetCurrentLevelCap())
|
||||
{
|
||||
for (i = 0; i < 8; i++)
|
||||
array[i] = 0;
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
#include "bg.h"
|
||||
#include "string_util.h"
|
||||
#include "pokemon_icon.h"
|
||||
#include "level_caps.h"
|
||||
#include "m4a.h"
|
||||
#include "mail.h"
|
||||
#include "event_data.h"
|
||||
@ -4239,14 +4240,14 @@ static void Cmd_getexp(void)
|
||||
if (IsValidForBattle(&gPlayerParty[*expMonId]))
|
||||
{
|
||||
if (wasSentOut)
|
||||
gBattleMoveDamage = gBattleStruct->expValue;
|
||||
gBattleMoveDamage = GetSoftLevelCapExpValue(gPlayerParty[*expMonId].level, gBattleStruct->expValue);
|
||||
else
|
||||
gBattleMoveDamage = 0;
|
||||
|
||||
if ((holdEffect == HOLD_EFFECT_EXP_SHARE || IsGen6ExpShareEnabled())
|
||||
&& (B_SPLIT_EXP < GEN_6 || gBattleMoveDamage == 0)) // only give exp share bonus in later gens if the mon wasn't sent out
|
||||
{
|
||||
gBattleMoveDamage += gBattleStruct->expShareExpValue;
|
||||
gBattleMoveDamage += GetSoftLevelCapExpValue(gPlayerParty[*expMonId].level, gBattleStruct->expShareExpValue);;
|
||||
}
|
||||
|
||||
ApplyExperienceMultipliers(&gBattleMoveDamage, *expMonId, gBattlerFainted);
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#include "battle.h"
|
||||
#include "daycare.h"
|
||||
#include "string_util.h"
|
||||
#include "level_caps.h"
|
||||
#include "mail.h"
|
||||
#include "pokemon_storage_system.h"
|
||||
#include "event_data.h"
|
||||
@ -316,7 +317,7 @@ static u16 TakeSelectedPokemonFromDaycare(struct DaycareMon *daycareMon)
|
||||
species = newSpecies;
|
||||
}
|
||||
|
||||
if (GetMonData(&pokemon, MON_DATA_LEVEL) != MAX_LEVEL)
|
||||
if (GetMonData(&pokemon, MON_DATA_LEVEL) < GetCurrentLevelCap())
|
||||
{
|
||||
experience = GetMonData(&pokemon, MON_DATA_EXP) + daycareMon->steps;
|
||||
SetMonData(&pokemon, MON_DATA_EXP, &experience);
|
||||
@ -365,6 +366,8 @@ static u8 GetNumLevelsGainedFromSteps(struct DaycareMon *daycareMon)
|
||||
|
||||
levelBefore = GetLevelFromBoxMonExp(&daycareMon->mon);
|
||||
levelAfter = GetLevelAfterDaycareSteps(&daycareMon->mon, daycareMon->steps);
|
||||
if (levelAfter > GetCurrentLevelCap())
|
||||
levelAfter = GetCurrentLevelCap();
|
||||
return levelAfter - levelBefore;
|
||||
}
|
||||
|
||||
|
||||
71
src/level_caps.c
Normal file
71
src/level_caps.c
Normal file
@ -0,0 +1,71 @@
|
||||
#include "global.h"
|
||||
#include "battle.h"
|
||||
#include "event_data.h"
|
||||
#include "level_caps.h"
|
||||
#include "pokemon.h"
|
||||
|
||||
|
||||
u32 GetCurrentLevelCap(void)
|
||||
{
|
||||
static const u32 sLevelCapFlagMap[][2] =
|
||||
{
|
||||
{FLAG_BADGE01_GET, 15},
|
||||
{FLAG_BADGE02_GET, 19},
|
||||
{FLAG_BADGE03_GET, 24},
|
||||
{FLAG_BADGE04_GET, 29},
|
||||
{FLAG_BADGE05_GET, 31},
|
||||
{FLAG_BADGE06_GET, 33},
|
||||
{FLAG_BADGE07_GET, 42},
|
||||
{FLAG_BADGE08_GET, 46},
|
||||
{FLAG_IS_CHAMPION, 58},
|
||||
};
|
||||
|
||||
u32 i;
|
||||
|
||||
if (B_LEVEL_CAP_TYPE == LEVEL_CAP_FLAG_LIST)
|
||||
{
|
||||
for (i = 0; i < ARRAY_COUNT(sLevelCapFlagMap); i++)
|
||||
{
|
||||
if (!FlagGet(sLevelCapFlagMap[i][0]))
|
||||
return sLevelCapFlagMap[i][1];
|
||||
}
|
||||
}
|
||||
else if (B_LEVEL_CAP_TYPE == LEVEL_CAP_VARIABLE)
|
||||
{
|
||||
return VarGet(B_LEVEL_CAP_VARIABLE);
|
||||
}
|
||||
|
||||
return MAX_LEVEL;
|
||||
}
|
||||
|
||||
u32 GetSoftLevelCapExpValue(u32 level, u32 expValue)
|
||||
{
|
||||
static const u32 sExpScalingDown[5] = { 4, 8, 16, 32, 64 };
|
||||
static const u32 sExpScalingUp[5] = { 16, 8, 4, 2, 1 };
|
||||
|
||||
u32 levelDifference;
|
||||
u32 currentLevelCap = GetCurrentLevelCap();
|
||||
|
||||
if (B_EXP_CAP_TYPE == EXP_CAP_NONE)
|
||||
return expValue;
|
||||
|
||||
if (B_LEVEL_CAP_EXP_UP && level < currentLevelCap)
|
||||
{
|
||||
levelDifference = currentLevelCap - level;
|
||||
if (levelDifference > ARRAY_COUNT(sExpScalingDown))
|
||||
return expValue + (expValue / sExpScalingUp[ARRAY_COUNT(sExpScalingDown) - 1]);
|
||||
else
|
||||
return expValue + (expValue / sExpScalingUp[levelDifference]);
|
||||
}
|
||||
else if (B_EXP_CAP_TYPE == EXP_CAP_SOFT && level >= currentLevelCap)
|
||||
{
|
||||
levelDifference = level - currentLevelCap;
|
||||
if (levelDifference > ARRAY_COUNT(sExpScalingDown))
|
||||
return expValue / sExpScalingDown[ARRAY_COUNT(sExpScalingDown) - 1];
|
||||
else
|
||||
return expValue / sExpScalingDown[levelDifference];
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
|
||||
}
|
||||
@ -31,6 +31,7 @@
|
||||
#include "item.h"
|
||||
#include "item_menu.h"
|
||||
#include "item_use.h"
|
||||
#include "level_caps.h"
|
||||
#include "link.h"
|
||||
#include "link_rfu.h"
|
||||
#include "mail.h"
|
||||
@ -5512,7 +5513,7 @@ void ItemUseCB_RareCandy(u8 taskId, TaskFunc task)
|
||||
u8 holdEffectParam = ItemId_GetHoldEffectParam(*itemPtr);
|
||||
|
||||
sInitialLevel = GetMonData(mon, MON_DATA_LEVEL);
|
||||
if (sInitialLevel != MAX_LEVEL)
|
||||
if (!(B_RARE_CANDY_CAP && sInitialLevel >= GetCurrentLevelCap()))
|
||||
{
|
||||
BufferMonStatsToTaskData(mon, arrayPtr);
|
||||
cannotUseEffect = ExecuteTableBasedItemEffect(mon, *itemPtr, gPartyMenu.slotId, 0);
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
#include "field_weather.h"
|
||||
#include "graphics.h"
|
||||
#include "item.h"
|
||||
#include "level_caps.h"
|
||||
#include "link.h"
|
||||
#include "main.h"
|
||||
#include "overworld.h"
|
||||
@ -5054,7 +5055,7 @@ bool8 TryIncrementMonLevel(struct Pokemon *mon)
|
||||
expPoints = gExperienceTables[gSpeciesInfo[species].growthRate][MAX_LEVEL];
|
||||
SetMonData(mon, MON_DATA_EXP, &expPoints);
|
||||
}
|
||||
if (nextLevel > MAX_LEVEL || expPoints < gExperienceTables[gSpeciesInfo[species].growthRate][nextLevel])
|
||||
if (nextLevel > GetCurrentLevelCap() || expPoints < gExperienceTables[gSpeciesInfo[species].growthRate][nextLevel])
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user