This tutorial guides you through changing the max level cap. In order to achieve this you'll add a new variable, modify the level cap logic, and optionally remove gaining experience once a Pokémon has reached max level. ## Contents 1. [Add a level cap variable and initialize it](#1-add-a-level-cap-variable-and-initialize-it) 2. [Modify the battle engine's level cap logic](#2-modify-the-battle-engines-level-cap-logic) 3. [Make Pokémon not level up at the Day Care after reaching new level cap](#3-make-pok%C3%A9mon-not-level-up-at-the-day-care-after-reaching-new-level-cap) 4. [Make Rare Candy ineffective after reaching new level cap](#4-make-rare-candy-ineffective-after-reaching-new-level-cap) 5. [(Optional) Stop gaining experience at max level](#5-optional-stop-gaining-experience-at-max-level) 6. [Other notes about the level cap](#6-other-notes-about-the-level-cap) ## 1. Add a level cap variable and initialize it First of all, we need to create the variable to store the new level cap. For that we'll edit [wram.asm](../blob/master/wram.asm) and replace one of the many unused bytes; in this case we can choose one of the unused fight counts for rematchable trainers: ```diff ; fight counts wJackFightCount:: db -wBeverlyFightCount:: db ; unused +wLevelCap:: db wHueyFightCount:: db wGavenFightCount:: db ``` And now let's initialize it. We can give it an initial value whenever we start a new game, and for that we'll edit [engine/menus/intro_menu.asm](../blob/master/engine/menus/intro_menu.asm). In this example we'll set it to Lv. 50: ```diff _ResetWRAM: ... ld hl, wMomItemTriggerBalance ld [hl], HIGH(MOM_MONEY >> 8) inc hl ld [hl], HIGH(MOM_MONEY) ; mid inc hl ld [hl], LOW(MOM_MONEY) + ld a, 50 + ld [wLevelCap], a call InitializeNPCNames farcall InitDecorations ... ``` The level cap has been set, but now we'll have to adjust the game's logic to work around it. ## 2. Modify the battle engine's level cap logic We'll have to edit multiple files to accept our new custom level cap. First, let's go to [engine/pokemon/experience.asm](../blob/master/engine/pokemon/experience.asm): ```diff CalcLevel: ld a, [wTempMonSpecies] ld [wCurSpecies], a call GetBaseData ld d, 1 .next_level inc d + ld a, [wLevelCap] + inc a + push bc + ld b, a ld a, d - cp LOW(MAX_LEVEL + 1) + cp b + pop bc jr z, .got_level call CalcExpAtLevel ... ``` And in [engine/battle/core.asm](../blob/master/engine/battle/core.asm): ```diff .no_exp_overflow ld a, [wCurPartyMon] ld e, a ld d, 0 ld hl, wPartySpecies add hl, de ld a, [hl] ld [wCurSpecies], a call GetBaseData push bc - ld d, MAX_LEVEL + ld a, [wLevelCap] + ld d, a callfar CalcExpAtLevel ... .not_max_exp ; Check if the mon leveled up xor a ; PARTYMON ld [wMonType], a predef CopyMonToTempMon callfar CalcLevel pop bc ld hl, MON_LEVEL add hl, bc + ld a, [wLevelCap] + push bc + ld b, a ld a, [hl] - cp MAX_LEVEL + cp b + pop bc jp nc, .next_mon ``` ```diff AnimateExpBar: push bc ld hl, wCurPartyMon ld a, [wCurBattleMon] cp [hl] jp nz, .finish + ld a, [wLevelCap] + push bc + ld b, a ld a, [wBattleMonLevel] - cp MAX_LEVEL + cp b + pop bc jp nc, .finish ... .NoOverflow: - ld d, MAX_LEVEL + ld a, [wLevelCap] + ld d, a callfar CalcExpAtLevel ldh a, [hProduct + 1] ld b, a ... .LoopLevels: + ld a, [wLevelCap] + push bc + ld b, a ld a, e - cp MAX_LEVEL + cp b + pop bc jr nc, .FinishExpBar cp d jr z, .FinishExpBar inc a ``` ## 3. Make Pokémon not level up at the Day Care after reaching new level cap Our Pokémon won't level up past the cap while battling, but they will if they're deposited in the Day Care, so we'll need to also adjust its logic. Edit [engine/events/happiness_egg.asm](../blob/master/engine/events/happiness_egg.asm): ```diff DayCareStep:: ; Raise the experience of Day-Care Pokémon every step cycle. ld a, [wDayCareMan] bit DAYCAREMAN_HAS_MON_F, a jr z, .day_care_lady + ld a, [wLevelCap] + ld b, a ld a, [wBreedMon1Level] ; level - cp MAX_LEVEL + cp b jr nc, .day_care_lady ... .day_care_lady ld a, [wDayCareLady] bit DAYCARELADY_HAS_MON_F, a jr z, .check_egg ld a, [wBreedMon2Level] ; level - cp MAX_LEVEL + cp b jr nc, .check_egg ``` ## 4. Make Rare Candy ineffective after reaching new level cap But aren't we forgetting about another way to level up? That's right, rare candies! They also need to be adjusted to work with our new level cap. Go to [engine/items/item_effects.asm](../blob/master/engine/items/item_effects.asm): ```diff RareCandyEffect: ld b, PARTYMENUACTION_HEALING_ITEM call UseItem_SelectMon jp c, RareCandy_StatBooster_ExitMenu call RareCandy_StatBooster_GetParameters ld a, MON_LEVEL call GetPartyParamLocation + ld a, [wLevelCap] + ld b, a ld a, [hl] - cp MAX_LEVEL + cp b jp nc, NoEffectMessage ``` ## 5. (Optional) Stop gaining experience at max level You'll notice that our max level Pokémon still get accounted for when dividing the experience among the battle participants, even though they can't gain more experience. If you want to modify this you'll have to go to [engine/battle/core.asm](../blob/master/engine/battle/core.asm) and change how it works: ```diff GiveExperiencePoints: ... .stat_exp_awarded inc de inc de dec c jr nz, .stat_exp_loop + pop bc + ld hl, MON_LEVEL + add hl, bc + ld a, [wLevelCap] + push bc + ld b, a + ld a, [hl] + cp b + pop bc + jp nc, .next_mon + push bc xor a ldh [hMultiplicand + 0], a ldh [hMultiplicand + 1], a ld a, [wEnemyMonBaseExp] ... .EvenlyDivideExpAmongParticipants: ; count number of battle participants ld a, [wBattleParticipantsNotFainted] ld b, a ld c, PARTY_LENGTH ld d, 0 .count_loop + push bc + push de + ld a, [wLevelCap] + ld b, a + ld a, [wPartyCount] + cp c + jr c, .no_mon + ld a, c + dec a + ld hl, wPartyMon1Level + call GetPartyLocation + ld a, [hl] +.no_mon + cp b + pop de + pop bc + jr nz, .gains_exp + srl b + ld a, d + jr .no_exp +.gains_exp xor a srl b adc d ld d, a +.no_exp dec c jr nz, .count_loop ``` ## 6. Other notes about the level cap In our example we adjusted the level cap to be Lv. 50, but we could change it to any level ranging from 1 to 255. However, if you change it to high values such as 255 itself then you'll have to modify other parts in the code. For example, there are instances which take the level cap as an input and the output is expected to be an 8-bit value. This tutorial won't cover those cases, but this warning is placed for those who want to venture into it.