1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
|
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.
|