diff --git a/include/level_caps.h b/include/level_caps.h new file mode 100644 index 000000000..c45540807 --- /dev/null +++ b/include/level_caps.h @@ -0,0 +1,20 @@ +#ifndef GUARD_LEVEL_CAP_H +#define GUARD_LEVEL_CAP_H + +#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 */ diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index f15decc7a..621d68d7b 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -28,7 +28,7 @@ #include "bg.h" #include "string_util.h" #include "pokemon_icon.h" -// #include "level_caps.h" +#include "level_caps.h" #include "m4a.h" #include "mail.h" #include "event_data.h" @@ -4236,29 +4236,29 @@ static void Cmd_getexp(void) if (IsValidForBattle(&gPlayerParty[*expMonId])) { if (wasSentOut) - gBattleMoveDamage = gBattleStruct->expValue; // GetSoftLevelCapExpValue(gPlayerParty[*expMonId].level, gBattleStruct->expValue); TODO: Level caps? + 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->expValue; // GetSoftLevelCapExpValue(gPlayerParty[*expMonId].level, gBattleStruct->expShareExpValue); TODO: Level caps? + gBattleMoveDamage += GetSoftLevelCapExpValue(gPlayerParty[*expMonId].level, gBattleStruct->expShareExpValue); } ApplyExperienceMultipliers(&gBattleMoveDamage, *expMonId, gBattlerFainted); - // if (B_EXP_CAP_TYPE == EXP_CAP_HARD && gBattleMoveDamage != 0) - // { - // u32 growthRate = gSpeciesInfo[GetMonData(&gPlayerParty[*expMonId], MON_DATA_SPECIES)].growthRate; - // u32 currentExp = GetMonData(&gPlayerParty[*expMonId], MON_DATA_EXP); - // u32 levelCap = GetCurrentLevelCap(); + if (B_EXP_CAP_TYPE == EXP_CAP_HARD && gBattleMoveDamage != 0) + { + u32 growthRate = gSpeciesInfo[GetMonData(&gPlayerParty[*expMonId], MON_DATA_SPECIES)].growthRate; + u32 currentExp = GetMonData(&gPlayerParty[*expMonId], MON_DATA_EXP); + u32 levelCap = GetCurrentLevelCap(); - // if (GetMonData(&gPlayerParty[*expMonId], MON_DATA_LEVEL) >= levelCap) - // gBattleMoveDamage = 0; - // else if (gExperienceTables[growthRate][levelCap] < currentExp + gBattleMoveDamage) - // gBattleMoveDamage = gExperienceTables[growthRate][levelCap] - currentExp; - // } + if (GetMonData(&gPlayerParty[*expMonId], MON_DATA_LEVEL) >= levelCap) + gBattleMoveDamage = 0; + else if (gExperienceTables[growthRate][levelCap] < currentExp + gBattleMoveDamage) + gBattleMoveDamage = gExperienceTables[growthRate][levelCap] - currentExp; + } if (IsTradedMon(&gPlayerParty[*expMonId])) { diff --git a/src/daycare.c b/src/daycare.c index e08395a42..b9e99cf5c 100644 --- a/src/daycare.c +++ b/src/daycare.c @@ -10,6 +10,7 @@ #include "event_data.h" #include "random.h" #include "constants/moves.h" +#include "level_caps.h" #include "menu.h" #include "new_menu_helpers.h" #include "script.h" @@ -619,7 +620,7 @@ static u16 TakeSelectedPokemonFromDaycare(struct DaycareMon *daycareMon) species = GetBoxMonData(&daycareMon->mon, MON_DATA_SPECIES); BoxMonToMon(&daycareMon->mon, &pokemon); - 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); @@ -668,6 +669,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; } diff --git a/src/level_caps.c b/src/level_caps.c new file mode 100644 index 000000000..e908d55e6 --- /dev/null +++ b/src/level_caps.c @@ -0,0 +1,83 @@ +#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, 14}, + {FLAG_BADGE02_GET, 21}, + {FLAG_BADGE03_GET, 24}, + {FLAG_BADGE04_GET, 29}, + {FLAG_BADGE05_GET, 43}, + {FLAG_BADGE06_GET, 43}, + {FLAG_BADGE07_GET, 47}, + {FLAG_BADGE08_GET, 50}, + {FLAG_SYS_GAME_CLEAR, 63}, + }; + + 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 (level < currentLevelCap) + { + if (B_LEVEL_CAP_EXP_UP) + { + levelDifference = currentLevelCap - level; + if (levelDifference > ARRAY_COUNT(sExpScalingUp)) + return expValue + (expValue / sExpScalingUp[ARRAY_COUNT(sExpScalingUp) - 1]); + else + return expValue + (expValue / sExpScalingUp[levelDifference]); + } + else + { + return expValue; + } + } + else if (B_EXP_CAP_TYPE == EXP_CAP_HARD) + { + return 0; + } + else if (B_EXP_CAP_TYPE == EXP_CAP_SOFT) + { + levelDifference = level - currentLevelCap; + if (levelDifference > ARRAY_COUNT(sExpScalingDown)) + return expValue / sExpScalingDown[ARRAY_COUNT(sExpScalingDown) - 1]; + else + return expValue / sExpScalingDown[levelDifference]; + } + else + { + return expValue; + } +} diff --git a/src/party_menu.c b/src/party_menu.c index 0e9f7de43..41097cfac 100644 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -24,6 +24,7 @@ #include "item.h" #include "item_menu.h" #include "item_use.h" +#include "level_caps.h" #include "link.h" #include "link_rfu.h" #include "load_save.h" @@ -5616,7 +5617,7 @@ void ItemUseCB_RareCandy(u8 taskId, TaskFunc func) u8 holdEffectParam = ItemId_GetHoldEffectParam(*itemPtr); sInitialLevel = GetMonData(mon, MON_DATA_LEVEL); - if (GetMonData(mon, MON_DATA_LEVEL) != MAX_LEVEL) + if (!(B_RARE_CANDY_CAP && sInitialLevel >= GetCurrentLevelCap())) { GetMonLevelUpWindowStats(mon, sLevelUpStatsBefore); cannotUseEffect = ExecuteTableBasedItemEffect(mon, *itemPtr, gPartyMenu.slotId, 0); diff --git a/src/pokemon.c b/src/pokemon.c index e040653d3..2fc2c75f7 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -20,6 +20,7 @@ #include "battle_message.h" #include "battle_util.h" #include "link.h" +#include "level_caps.h" #include "pokemon_animation.h" #include "m4a.h" #include "pokedex.h" @@ -3585,8 +3586,17 @@ bool8 PokemonUseItemEffects(struct Pokemon *mon, u16 item, u8 partyIndex, u8 mov { u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL); dataUnsigned = sExpCandyExperienceTable[param - 1] + GetMonData(mon, MON_DATA_EXP, NULL); - if (dataUnsigned > gExperienceTables[gSpeciesInfo[species].growthRate][MAX_LEVEL]) + + if (B_RARE_CANDY_CAP && B_EXP_CAP_TYPE == EXP_CAP_HARD) + { + u32 currentLevelCap = GetCurrentLevelCap(); + if (dataUnsigned > gExperienceTables[gSpeciesInfo[species].growthRate][currentLevelCap]) + dataUnsigned = gExperienceTables[gSpeciesInfo[species].growthRate][currentLevelCap]; + } + else if (dataUnsigned > gExperienceTables[gSpeciesInfo[species].growthRate][MAX_LEVEL]) + { dataUnsigned = gExperienceTables[gSpeciesInfo[species].growthRate][MAX_LEVEL]; + } } if (dataUnsigned != 0) // Failsafe @@ -5127,37 +5137,24 @@ void PartySpreadPokerus(struct Pokemon *party) } } -static void SetMonExpWithMaxLevelCheck(struct Pokemon *mon, int species, u8 unused, u32 data) -{ - if (data > gExperienceTables[gSpeciesInfo[species].growthRate][MAX_LEVEL]) - { - data = gExperienceTables[gSpeciesInfo[species].growthRate][MAX_LEVEL]; - SetMonData(mon, MON_DATA_EXP, &data); - } -} - bool8 TryIncrementMonLevel(struct Pokemon *mon) { - u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL); - u8 level = GetMonData(mon, MON_DATA_LEVEL, NULL); - u8 newLevel = level + 1; - u32 exp = GetMonData(mon, MON_DATA_EXP, NULL); - - if (level < MAX_LEVEL) + u16 species = GetMonData(mon, MON_DATA_SPECIES, 0); + u8 nextLevel = GetMonData(mon, MON_DATA_LEVEL, 0) + 1; + u32 expPoints = GetMonData(mon, MON_DATA_EXP, 0); + if (expPoints > gExperienceTables[gSpeciesInfo[species].growthRate][MAX_LEVEL]) { - if (exp > gExperienceTables[gSpeciesInfo[species].growthRate][newLevel]) - { - SetMonData(mon, MON_DATA_LEVEL, &newLevel); - SetMonExpWithMaxLevelCheck(mon, species, newLevel, exp); - return TRUE; - } - else - return FALSE; + expPoints = gExperienceTables[gSpeciesInfo[species].growthRate][MAX_LEVEL]; + SetMonData(mon, MON_DATA_EXP, &expPoints); + } + if (nextLevel > GetCurrentLevelCap() || expPoints < gExperienceTables[gSpeciesInfo[species].growthRate][nextLevel]) + { + return FALSE; } else { - SetMonExpWithMaxLevelCheck(mon, species, level, exp); - return FALSE; + SetMonData(mon, MON_DATA_LEVEL, &nextLevel); + return TRUE; } }