summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIdain <luiscarlosholguinperez@outlook.com>2022-03-19 21:16:08 -0400
committerIdain <luiscarlosholguinperez@outlook.com>2022-03-19 21:16:08 -0400
commita38df9c2785bc212b8e32f2701337efcd235a0b4 (patch)
tree9211ba627630b82943020f4e642fe7b774fcc392
parentd73de36e55c3130d5a6e384ccc6b2c5a4259884e (diff)
Refactor tutorial and add new steps
-rw-r--r--Level-cap.md206
1 files changed, 140 insertions, 66 deletions
diff --git a/Level-cap.md b/Level-cap.md
index fbe21ff..751c308 100644
--- a/Level-cap.md
+++ b/Level-cap.md
@@ -1,56 +1,83 @@
-This tutorial guides you through changing the max level. You will add a new variable, modify level cap logic, and optionally remove gaining exp. once a pokemon is the max level.
+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.
-**TODO**:
-Investigate wether one is able to set the level cap by modifying `MAX_LEVEL EQU [0-255]` in `constants/battle_constants.asm`.
-## 1. Add Level Cap variable
-First, we want to add a `wLevelCap` variable to `wram.asm` in the root folder.
-In `wram.asm`, go to line 2668, `wBeverlyFightCount:: db ; unused`.
-Since this byte is unused, you can replace it.
-Replace this line with `wLevelCap:: db`.
-This is the value we'll be checking for the level cap
+## 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
-; fight counts
-wJackFightCount:: db ; d9f2
-- wBeverlyFightCount:: db ; unused
-+ wLevelCap:: db
-wHueyFightCount:: db
-wGavenFightCount:: db
+ _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.
-To set the level cap, write this in a script:
-`loadmem wLevelCap, [0-255] ; number from 0 to 255 representing what your level cap should be`
-## 2. Replace first level cap check
+## 2. Modify the battle engine's level cap logic
-In `engine/pokemon/experience.asm`, go to line 9: `cp LOW(MAX_LEVEL + 1)`.
-This is what we first need to change.
-Before the `ld a, d` on the line before it, add code to load the new level cap into register `b`.
+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
-.next_level
+ 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
- cp LOW(MAX_LEVEL + 1)
jr z, .got_level
call CalcExpAtLevel
+ ...
```
-## 3. Replace the rest of the cap checks
+And in [engine/battle/core.asm](../blob/master/engine/battle/core.asm):
-There are now five more instances of `MAX_LEVEL` that you'll need to change.
-First, go to the `.no_exp_overflow` label located in `engine/battle/core.asm`.
-Instead of storing the `MAX_LEVEL` value, we load our custom max level.
```diff
-.no_exp_overflow
+ .no_exp_overflow
ld a, [wCurPartyMon]
ld e, a
ld d, 0
@@ -61,17 +88,13 @@ Instead of storing the `MAX_LEVEL` value, we load our custom max level.
call GetBaseData
push bc
- ld d, MAX_LEVEL
-+ push af
+ ld a, [wLevelCap]
+ ld d, a
-+ pop af
callfar CalcExpAtLevel
-```
-Next, `ctrl + f` for the next instance of `MAX_LEVEL`, located in the `.not_max_exp` label. You'll notice that the line before this reads `ld a, [hl]` Before this line, load the level cap into register b. Then, after `ld a, [hl]`, add a comparison and restore the `bc` register. Here we use the comparison to control the jump.
+ ...
-```diff
-.not_max_exp
+ .not_max_exp
; Check if the mon leveled up
xor a ; PARTYMON
ld [wMonType], a
@@ -84,16 +107,14 @@ Next, `ctrl + f` for the next instance of `MAX_LEVEL`, located in the `.not_max_
+ push bc
+ ld b, a
ld a, [hl]
+- cp MAX_LEVEL
+ cp b
+ pop bc
-- cp MAX_LEVEL
jp nc, .next_mon
```
-Third, ctrl + f for the next instance of MAX_LEVEL, located in the `AnimateExpBar` label. You'll notice there's a `ld a, [wBattleMonLevel]` before it. Before this, add
-
```diff
-AnimateExpBar:
+ AnimateExpBar:
push bc
ld hl, wCurPartyMon
@@ -105,35 +126,24 @@ AnimateExpBar:
+ push bc
+ ld b, a
ld a, [wBattleMonLevel]
+- cp MAX_LEVEL
+ cp b
+ pop bc
-- cp MAX_LEVEL
jp nc, .finish
-```
-Fourth, ctrl + f for the next instance of MAX_LEVEL. Replace `ld d, MAX_LEVEL` with loading the custom max level.
+ ...
-```diff
- ld [hli], a
- ld [hl], a
-
-.NoOverflow:
-+ push af
+ .NoOverflow:
+- ld d, MAX_LEVEL
+ ld a, [wLevelCap]
+ ld d, a
-+ pop af
callfar CalcExpAtLevel
ldh a, [hProduct + 1]
ld b, a
-```
-Fifth, ctrl + f for the final instance of MAX_LEVEL. You'll notice before it reads `ld a, e`. Before this, lod the level cap into b, then do the comparison on that.
+ ...
-```diff
- ld a, e
- ld d, a
-
-.LoopLevels:
+ .LoopLevels:
+ ld a, [wLevelCap]
+ push bc
+ ld b, a
@@ -147,15 +157,74 @@ Fifth, ctrl + f for the final instance of MAX_LEVEL. You'll notice before it rea
inc a
```
-Now you're done with replacing the max level checks with the level cap checks.
-## 4. Optional: Remove exp. gain text at max level
+## 3. Make Pokémon not level up at the Day Care after reaching new level cap
-You'll notice that the exp. gain text still displays when you reach the level cap.
-To remove this, go to line 7059 in `engine/battle/core.asm`, which should be about the lines of
+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
-.stat_exp_awarded
+ 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 get more experience. If yo 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
@@ -175,18 +244,16 @@ To remove this, go to line 7059 in `engine/battle/core.asm`, which should be abo
ldh [hMultiplicand + 0], a
ldh [hMultiplicand + 1], a
ld a, [wEnemyMonBaseExp]
-```
-Next, go to
+ ...
-```diff
-.EvenlyDivideExpAmongParticipants:
-; count number of battle participants
+ .EvenlyDivideExpAmongParticipants:
+ ; count number of battle participants
ld a, [wBattleParticipantsNotFainted]
ld b, a
ld c, PARTY_LENGTH
ld d, 0
-.count_loop
+ .count_loop
+ push bc
+ push de
+ ld a, [wLevelCap]
@@ -216,3 +283,10 @@ Next, go to
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.
+
+
+