summaryrefslogtreecommitdiff
path: root/Level-cap.md
blob: acba505a43df46ac9ec839c81f4cc162ddbe32a8 (plain)
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.