summaryrefslogtreecommitdiff
path: root/Replace-stat-experience-with-EVs.md
blob: ef24100389543ab13fe214e70e53e34c7cf6f572 (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
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
Gen 3 replaced stat experience with EVs, which are different in a number of ways. We'll see those differences in this tutorial.

(EVs have an advantage outside of game mechanics: they take up fewer bytes. You'll end up with four unused bytes in the Pokémon data structure which can be used for all kinds of permanent data.)


## Contents

1. [Replace stat experience with EVs in the Pokémon data structure](#1-replace-stat-experience-with-evs-in-the-pokémon-data-structure)
2. [Replace stat experience with EVs in base data](#2-replace-stat-experience-with-evs-in-base-data)
3. [Gain EVs from winning battles](#3-gain-evs-from-winning-battles)
4. [Calculate stats based on EVs](#4-calculate-stats-based-on-evs)
5. [Vitamins give EVs, not stat experience](#5-vitamins-give-evs-not-stat-experience)
6. [Replace Odd Egg and Battle Tower stat experience with EVs](#6-replace-odd-egg-and-battle-tower-stat-experience-with-evs)
7. [Replace `MON_STAT_EXP` with `MON_EVS` everywhere](#7-replace-mon_stat_exp-with-mon_evs-everywhere)
8. [Replace some more labels](#8-replace-some-more-labels)
9. [Remove unused square root code](#9-remove-unused-square-root-code)
10. [Add Zinc to boost Special Defense EVs](#10-add-zinc-to-boost-special-defense-evs)


## 1. Replace stat experience with EVs in the Pokémon data structure

Stat experience for each stat is a two-byte quantity from 0 to 65,535, with a single Special stat experience shared between Special Attack and Special Defense. EVs for each stat are one byte, from 0 to 255 (actually 252), with independent Special Attack and Special Defense quantities.

Edit [macros/wram.asm](../blob/master/macros/wram.asm):

```diff
 box_struct: MACRO
 \1Species::        db
 \1Item::           db
 \1Moves::          ds NUM_MOVES
 \1ID::             dw
 \1Exp::            ds 3
-\1StatExp::
-\1HPExp::          dw
-\1AtkExp::         dw
-\1DefExp::         dw
-\1SpdExp::         dw
-\1SpcExp::         dw
+\1EVs::
+\1HPEV::           db
+\1AtkEV::          db
+\1DefEV::          db
+\1SpdEV::          db
+\1SpclAtkEV::      db
+\1SpclDefEV::      db
+\1Padding::        ds 4
 \1DVs::            dw
 \1PP::             ds NUM_MOVES
 \1Happiness::      db
 \1PokerusStatus::  db
 \1CaughtData::
 \1CaughtTime::
 \1CaughtLevel::    db
 \1CaughtGender::
 \1CaughtLocation:: db
 \1Level::          db
 \1End::
 ENDM
```

And edit [constants/pokemon_data_constants.asm](../blob/master/constants/pokemon_data_constants.asm):

```diff
 ; party_struct members (see macros/wram.asm)
 MON_SPECIES            EQUS "(wPartyMon1Species - wPartyMon1)"
 MON_ITEM               EQUS "(wPartyMon1Item - wPartyMon1)"
 MON_MOVES              EQUS "(wPartyMon1Moves - wPartyMon1)"
 MON_ID                 EQUS "(wPartyMon1ID - wPartyMon1)"
 MON_EXP                EQUS "(wPartyMon1Exp - wPartyMon1)"
-MON_STAT_EXP           EQUS "(wPartyMon1StatExp - wPartyMon1)"
-MON_HP_EXP             EQUS "(wPartyMon1HPExp - wPartyMon1)"
-MON_ATK_EXP            EQUS "(wPartyMon1AtkExp - wPartyMon1)"
-MON_DEF_EXP            EQUS "(wPartyMon1DefExp - wPartyMon1)"
-MON_SPD_EXP            EQUS "(wPartyMon1SpdExp - wPartyMon1)"
-MON_SPC_EXP            EQUS "(wPartyMon1SpcExp - wPartyMon1)"
+MON_EVS                EQUS "(wPartyMon1EVs - wPartyMon1)"
+MON_HP_EV              EQUS "(wPartyMon1HPEV - wPartyMon1)"
+MON_ATK_EV             EQUS "(wPartyMon1AtkEV - wPartyMon1)"
+MON_DEF_EV             EQUS "(wPartyMon1DefEV - wPartyMon1)"
+MON_SPD_EV             EQUS "(wPartyMon1SpdEV - wPartyMon1)"
+MON_SAT_EV             EQUS "(wPartyMon1SpclAtkEV - wPartyMon1)"
+MON_SDF_EV             EQUS "(wPartyMon1SpclDefEV - wPartyMon1)"
+MON_PADDING            EQUS "(wPartyMon1Padding - wPartyMon1)"
 MON_DVS                EQUS "(wPartyMon1DVs - wPartyMon1)"
 ...
 BOXMON_STRUCT_LENGTH   EQUS "(wPartyMon1End - wPartyMon1)"
 PARTYMON_STRUCT_LENGTH EQUS "(wPartyMon1StatsEnd - wPartyMon1)"
 REDMON_STRUCT_LENGTH EQU 44

 ...

+; significant EV values
+MAX_EV EQU 252
```

By replacing the 10 stat experience bytes with 6 EV bytes, we've freed up 4 bytes in `box_struct`. That's valuable space, since it gets saved when Pokémon are deposited in the PC. Making use of it is beyond the scope of this tutorial, so we'll leave it as padding for now.


## 2. Replace stat experience with EVs in base data

When you knock out a Pokémon, the stat experience you gain is equal to its base stats. That doesn't work for EVs; each species has its own set of EV yields, with a gain of 0 to 3 for each stat. That means we can store each stat's gain in two bits, so six stats will fit in two bytes. Conveniently, there are two unused bytes in base data that we can replace.

Edit [wram.asm](../blob/master/wram.asm):

```diff
 ; corresponds to the data/pokemon/base_stats/*.asm contents
 wCurBaseData:: ; d236
 wBaseDexNo:: db ; d236
 wBaseStats:: ; d237
 wBaseHP:: db ; d237
 wBaseAttack:: db ; d238
 wBaseDefense:: db ; d239
 wBaseSpeed:: db ; d23a
 wBaseSpecialAttack:: db ; d23b
 wBaseSpecialDefense:: db ; d23c
+wBaseEVs::
+wBaseHPAtkDefSpdEVs:: db
+wBaseSpAtkSpDefEVs:: db
 wBaseType:: ; d23d
 wBaseType1:: db ; d23d
 wBaseType2:: db ; d23e
 wBaseCatchRate:: db ; d23f
 wBaseExp:: db ; d240
 wBaseItems:: ; d241
 wBaseItem1:: db ; d241
 wBaseItem2:: db ; d242
 wBaseGender:: db ; d243
-wBaseUnknown1:: db ; d244
 wBaseEggSteps:: db ; d245
-wBaseUnknown2:: db ; d246
 wBasePicSize:: db ; d247
 wBasePadding:: ds 4 ; d248
 wBaseGrowthRate:: db ; d24c
 wBaseEggGroups:: db ; d24d
 wBaseTMHM:: flag_array NUM_TM_HM_TUTOR ; d24e
 wCurBaseDataEnd::
```

Edit [data/pokemon/base_stats.asm](../blob/master/data/pokemon/base_stats.asm):

```diff
+evs: MACRO
+	db (\1 << 6) | (\2 << 4) | (\3 << 2) | \4
+	db (\5 << 6) | (\6 << 4)
+ENDM

 tmhm: MACRO
 ...
```

Finally, edit all 251 [data/pokemon/base_stats/\*.asm](../tree/master/data/pokemon/base_stats/) files. With each one, delete the `unknown 1` and `unknown 2` bytes and add `evs` after base stats. For example, here's [data/pokemon/base_stats/chikorita.asm](../blob/master/data/pokemon/base_stats/chikorita.asm):

```diff
 	db CHIKORITA ; 152

 	db  45,  49,  65,  45,  49,  65
+	evs  0,   0,   0,   0,   0,   1
 	;   hp  atk  def  spd  sat  sdf

 	db GRASS, GRASS ; type
 	db 45 ; catch rate
 	db 64 ; base exp
 	db NO_ITEM, NO_ITEM ; items
 	db GENDER_F12_5 ; gender ratio
-	db 100 ; unknown 1
 	db 20 ; step cycles to hatch
-	db 5 ; unknown 2
 	INCBIN "gfx/pokemon/chikorita/front.dimensions"
 	db 0, 0, 0, 0 ; padding
 	db GROWTH_MEDIUM_SLOW ; growth rate
 	dn EGG_MONSTER, EGG_PLANT ; egg groups

 	; tm/hm learnset
 	...
```

You can do this automatically with a Python script. Save this as **base-evs.py** in the same directory as main.asm:

```python
import glob

filenames = glob.glob('data/pokemon/base_stats/*.asm')

for filename in filenames:

	print('Update', filename)

	with open(filename, 'r', encoding='utf8') as file:
		lines = file.readlines()

	with open(filename, 'w', encoding='utf8') as file:
		for line in lines:
			if line in ['\tdb 100 ; unknown 1\n', '\tdb 5 ; unknown 2\n']:
				continue
			if line == '\t;   hp  atk  def  spd  sat  sdf\n':
				file.write('\tevs  0,   0,   0,   0,   0,   0\n')
			file.write(line)
```

Then run `python3 base-evs.py`, just like running `make`. It should output:

```
$ python3 base-evs.py
Update data/pokemon/base_stats/abra.asm
...
Update data/pokemon/base_stats/zubat.asm
```

(If it gives an error "`python3: command not found`", you need to install Python 3. It's available as the `python3` package in Cygwin.)

That will format all the base data files correctly, but with zero EV yields. You'll have to fill in the correct values yourself.


## 3. Gain EVs from winning battles

Edit [engine/battle/core.asm](../blob/master/engine/battle/core.asm):

```diff
GiveExperiencePoints:
	...

-; give stat exp
-	ld hl, MON_STAT_EXP + 1
-	add hl, bc
-	ld d, h
-	ld e, l
-	ld hl, wEnemyMonBaseStats - 1
-	push bc
-	ld c, NUM_EXP_STATS
-.loop1
-	inc hl
-	ld a, [de]
-	add [hl]
-	ld [de], a
-	jr nc, .okay1
-	dec de
-	ld a, [de]
-	inc a
-	jr z, .next
-	ld [de], a
-	inc de
-
-.okay1
-	push hl
-	push bc
-	ld a, MON_PKRUS
-	call GetPartyParamLocation
-	ld a, [hl]
-	and a
-	pop bc
-	pop hl
-	jr z, .skip
-	ld a, [de]
-	add [hl]
-	ld [de], a
-	jr nc, .skip
-	dec de
-	ld a, [de]
-	inc a
-	jr z, .next
-	ld [de], a
-	inc de
-	jr .skip
-
-.next
-	ld a, $ff
-	ld [de], a
-	inc de
-	ld [de], a
-
-.skip
-	inc de
-	inc de
-	dec c
-	jr nz, .loop1
+; Give EVs
+; e = 0 for no Pokerus, 1 for Pokerus
+	ld e, 0
+	ld hl, MON_PKRUS
+	add hl, bc
+	ld a, [hl]
+	and a
+	jr z, .no_pokerus
+	inc e
+.no_pokerus
+	ld hl, MON_EVS
+	add hl, bc
+	push bc
+	ld a, [wEnemyMonSpecies]
+	ld [wCurSpecies], a
+	call GetBaseData
+; EV yield format: %hhaaddss %ttff0000
+; h = hp, a = atk, d = def, s = spd
+; t = sat, f = sdf, 0 = unused bits
+	ld a, [wBaseHPAtkDefSpdEVs]
+	ld b, a
+	ld c, 6 ; six EVs
+.ev_loop
+	rlc b
+	rlc b
+	ld a, b
+	and %11
+	bit 0, e
+	jr z, .no_pokerus_boost
+	add a
+.no_pokerus_boost
+	add [hl]
+	jr c, .ev_overflow
+	cp MAX_EV + 1
+	jr c, .got_ev
+.ev_overflow
+	ld a, MAX_EV
+.got_ev
+	ld [hli], a
+	dec c
+	jr z, .evs_done
+; Use the second byte for Sp.Atk and Sp.Def
+	ld a, c
+	cp 2 ; two stats left, Sp.Atk and Sp.Def
+	jr nz, .ev_loop
+	ld a, [wBaseSpAtkSpDefEVs]
+	ld b, a
+	jr .ev_loop
+.evs_done
```

Now instead of gaining the enemy's base stats toward your stat experience, you'll gain their base EV yields toward your EV totals. Having Pokérus will double EV gain.


## 4. Calculate stats based on EVs

Edit [engine/pokemon/move_mon.asm](../blob/master/engine/pokemon/move_mon.asm):

```diff
 CalcMonStats:
 ; Calculates all 6 Stats of a mon
-; b: Take into account stat EXP if TRUE
+; b: Take into account EVs if TRUE
 ; 'c' counts from 1-6 and points with 'wBaseStats' to the base value
-; hl is the path to the Stat EXP
+; hl is the path to the EVs
 ; de points to where the final stats will be saved

 	ld c, STAT_HP - 1 ; first stat
 .loop
 	inc c
 	call CalcMonStatC
 	ldh a, [hMultiplicand + 1]
 	ld [de], a
 	inc de
 	ldh a, [hMultiplicand + 2]
 	ld [de], a
 	inc de
 	ld a, c
 	cp STAT_SDEF ; last stat
 	jr nz, .loop
 	ret

 CalcMonStatC:
 ; 'c' is 1-6 and points to the BaseStat
 ; 1: HP
 ; 2: Attack
 ; 3: Defense
 ; 4: Speed
 ; 5: SpAtk
 ; 6: SpDef
 	push hl
 	push de
 	push bc
 	ld a, b
 	ld d, a
 	push hl
 	ld hl, wBaseStats
 	dec hl ; has to be decreased, because 'c' begins with 1
 	ld b, 0
 	add hl, bc
 	ld a, [hl]
 	ld e, a
 	pop hl
 	push hl
- 	ld a, c
- 	cp STAT_SDEF ; last stat
- 	jr nz, .not_spdef
- 	dec hl
- 	dec hl
-
- .not_spdef
- 	sla c
 	ld a, d
 	and a
 	jr z, .no_stat_exp
 	add hl, bc
-	push de
-	ld a, [hld]
-	ld e, a
-	ld d, [hl]
-	farcall GetSquareRoot
-	pop de
+	ld a, [hl]
+	ld b, a

 .no_stat_exp
-	srl c
 	pop hl
 	push bc
-	ld bc, MON_DVS - MON_HP_EXP + 1
+	ld bc, MON_DVS - MON_HP_EV + 1
 	add hl, bc
 	pop bc
 	...
```

The `CalcMonStatC` implements these formulas for stat values:

- *HP* = (((*base* + *IV*) × 2 + √*exp* / 4) × *level*) / 100 + *level* + 10
- *stat* = (((*base* + *IV*) × 2 + √*exp* / 4) × *level*) / 100 + 5

In those formulas, division rounds down and square root rounds up (for example, √12 = 3.4641… rounds to 4). [Order of operations](https://en.wikipedia.org/wiki/Order_of_operations) is standard PEMDAS.

Anyway, we've just replaced √*exp* in those formulas with simply *EV*.

This change has consequences for progressing through the game. Square roots are nonlinear, so early gains to stat experience were contributing relatively larger boosts to stats. But EVs are linear, so gaining 4 EVs will be just as beneficial no matter how many you already had.

For example, 50 EVs are equivalent to 50² = 2,500 stat exp, and 100 EVs are equivalent to 100² = 10,000 stat exp. But getting from 50 EVs to 100 takes the same effort as from 0 to 50, whereas getting from 2,500 to 10,000 stat exp means gaining another 7,500 stat exp: three times as much effort as the first 2,500.

Eventually this won't matter, since the maximum 252 EVs or 65,535 stat exp both result in the same stats (252 / 4 = √65,535 / 4 = 63). But you may notice your Pokémon stats growing more slowly at first, and more quickly later on than you're used to.


## 5. Vitamins give EVs, not stat experience

Edit [engine/items/item_effects.asm](../blob/master/engine/items/item_effects.asm):

```diff
 VitaminEffect:
 	ld b, PARTYMENUACTION_HEALING_ITEM
 	call UseItem_SelectMon

 	jp c, RareCandy_StatBooster_ExitMenu

 	call RareCandy_StatBooster_GetParameters

-	call GetStatExpRelativePointer
+	call GetEVRelativePointer

-	ld a, MON_STAT_EXP
+	ld a, MON_EVS
 	call GetPartyParamLocation

 	add hl, bc
 	ld a, [hl]
 	cp 100
 	jr nc, NoEffectMessage

 	add 10
 	ld [hl], a
 	call UpdateStatsAfterItem

-	call GetStatExpRelativePointer
+	call GetEVRelativePointer

 	ld hl, StatStrings
 	add hl, bc
+	add hl, bc
 	ld a, [hli]
 	ld h, [hl]
 	ld l, a
 	ld de, wStringBuffer2
 	ld bc, ITEM_NAME_LENGTH
 	call CopyBytes

 	...

 StatStrings:
 	dw .health
 	dw .attack
 	dw .defense
 	dw .speed
-	dw .special
+	dw .sp_atk

 .health  db "HEALTH@"
 .attack  db "ATTACK@"
 .defense db "DEFENSE@"
 .speed   db "SPEED@"
-.special db "SPECIAL@"
+.sp_atk  db "SPCL.ATK@"

-GetStatExpRelativePointer:
+GetEVRelativePointer:
 	ld a, [wCurItem]
 	ld hl, Table_eeeb
 	...

 Table_eeeb:
-	db HP_UP,    MON_HP_EXP - MON_STAT_EXP
-	db PROTEIN, MON_ATK_EXP - MON_STAT_EXP
-	db IRON,    MON_DEF_EXP - MON_STAT_EXP
-	db CARBOS,  MON_SPD_EXP - MON_STAT_EXP
-	db CALCIUM, MON_SPC_EXP - MON_STAT_EXP
+	db HP_UP,    MON_HP_EV - MON_EVS
+	db PROTEIN, MON_ATK_EV - MON_EVS
+	db IRON,    MON_DEF_EV - MON_EVS
+	db CARBOS,  MON_SPD_EV - MON_EVS
+	db CALCIUM, MON_SAT_EV - MON_EVS
```

Vitamins used to give 2,560 stat experience, up to a maximum of 25,600. Now they give 10 EVs, up to a maximum of 100. Conveniently, the vitamin code already used the values 10 and 100, because those are the high bytes of 2,560 and 25,600.

Due to that convenience, this mostly involved changing label and constant names. The only real adjustment needed was the offset to `StatStrings`: stat experience and string pointers were both two-byte values, but now EVs are one byte, so we needed a second `add hl, bc` to get the stat string corresponding to an EV.

We also replaced "SPECIAL" with "SPCL.ATK" since Calcium only affects the Special Attack EV. The same should be done for the description of Calcium.

Edit [data/items/descriptions.asm](../blob/master/data/items/descriptions.asm):

```diff
 CalciumDesc:
-	db   "Ups SPECIAL stats"
+	db   "Raises SPCL.ATK"
 	next "of one #MON.@"
```


## 6. Replace Odd Egg and Battle Tower stat experience with EVs

First, edit [data/events/odd_eggs.asm](../blob/master/data/events/odd_eggs.asm). Make this same replacement 14 times, once for each hard-coded Odd Egg Pokémon structure:

```diff
-	; Stat exp
-	bigdw 0
-	bigdw 0
-	bigdw 0
-	bigdw 0
-	bigdw 0
+	db 0, 0, 0, 0, 0, 0 ; EVs
+	db 0, 0, 0, 0 ; padding
```

Next, edit [data/battle_tower/parties.asm](../blob/master/data/battle_tower/parties.asm). This is trickier for two reasons. One, there are 210 Pokémon structures instead of 14. Two, they have nonzero stat experience, and their hard-coded stats need to match their new EV values. For example:

```diff
	db JOLTEON
	db MIRACLEBERRY
	db THUNDERBOLT, HYPER_BEAM, SHADOW_BALL, ROAR
	dw 0 ; OT ID
	dt 1000 ; Exp
-	; Stat exp
-	bigdw 50000
-	bigdw 40000
-	bigdw 40000
-	bigdw 35000
-	bigdw 40000
+	db 224, 200, 200, 188, 200, 200 ; EVs
+	db 0, 0, 0, 0 ; padding
	dn 13, 13, 11, 13 ; DVs
	db 15, 5, 15, 20 ; PP
	db 100 ; Happiness
	db 0, 0, 0 ; Pokerus, Caught data
	db 10 ; Level
	db 0, 0 ; Status
	bigdw 41 ; HP
	bigdw 41 ; Max HP
	bigdw 25 ; Atk
	bigdw 24 ; Def
	bigdw 37 ; Spd
	bigdw 34 ; SAtk
	bigdw 31 ; SDef
	db "SANDA-SU@@@"
```

Numerically speaking, you just have to take the square root of each stat experience value and round up to an integer EV; but you have to do this for 210 × 5 values, and insert padding bytes.

You can do this automatically with a Python script. Save this as **bt-evs.py** in the same directory as main.asm:

```python
from math import sqrt, ceil

def derive_ev(stat_exp_line):
	stat_exp = int(stat_exp_line[len('\tbigdw '):])
	return str(int(ceil(sqrt(stat_exp))))

filename = 'data/battle_tower/parties.asm'

with open(filename, 'r', encoding='utf8') as file:
	lines = file.readlines()

with open(filename, 'w', encoding='utf8') as file:
	i = 0
	while i < len(lines):
		line = lines[i]

		if line != '\t; Stat exp\n':
			file.write(line)
			i += 1
			continue

		exp_lines = lines[i+1:i+6]
		evs = [derive_ev(exp_line) for exp_line in exp_lines]
		evs.append(evs[-1]) # Special -> Sp.Atk and Sp.Def
		file.write('\tdb {} ; EVs\n'.format(', '.join(evs)))
		file.write('\tdb 0, 0, 0, 0 ; padding\n')
		i += 6

print('Done!')
```

Then run `python3 bt-evs.py`. It should output:

```
$ python3 battle-tower-evs.py
Done!
```


## 7. Replace `MON_STAT_EXP` with `MON_EVS` everywhere

Replace every occurrence of `MON_STAT_EXP` with `MON_EVS` in these files:

- [engine/battle/core.asm](../blob/master/engine/battle/core.asm) again (two, in `LoadEnemyMon` and `GiveExperiencePoints`)
- [engine/pokemon/move_mon.asm](../blob/master/engine/pokemon/move_mon.asm) again (five; three in `GeneratePartyMonStats`, one in `SendGetMonIntoFromBox`, one in `ComputeNPCTrademonStats`
- [engine/items/item_effects.asm](../blob/master/engine/items/item_effects.asm) again (one, in `UpdateStatsAfterItem`)
- [engine/events/battle_tower/battle_tower.asm](../blob/master/engine/events/battle_tower/battle_tower.asm) (one, in `ValidateBTParty`)
- [engine/link/link.asm](../blob/master/engine/link/link.asm) (three; one in `Link_PrepPartyData_Gen1`, two in `Function2868a`)
- [engine/pokemon/breeding.asm](../blob/master/engine/pokemon/breeding.asm) (one, in `HatchEggs`)
- [engine/pokemon/correct_party_errors.asm](../blob/master/engine/pokemon/correct_party_errors.asm) (one, in `Unreferenced_CorrectPartyErrors`)
- [engine/pokemon/tempmon.asm](../blob/master/engine/pokemon/tempmon.asm) (one, in `_TempMonStatsCalculation`)
- [mobile/mobile_46.asm](../blob/master/mobile/mobile_46.asm) (two; one in `Function11b483`, one in `Function11b6b4`)

Most of the `MON_STAT_EXP` occurrences are part of an argument passed to `CalcMonStats`.


## 8. Replace some more labels

Edit [engine/events/daycare.asm](../blob/master/engine/events/daycare.asm):

```diff
 DayCare_InitBreeding:
 	...
 	xor a
-	ld b, wEggMonDVs - wEggMonStatExp
-	ld hl, wEggMonStatExp
+	ld b, wEggMonDVs - wEggMonEVs
+	ld hl, wEggMonEVs
 .loop2
 	ld [hli], a
 	dec b
 	jr nz, .loop2
```

We're technically done now; EVs will work behind the scenes just like stat experience did. But there's room for more improvement.


## 9. Remove unused square root code

The only place `GetSquareRoot` was used was in `CalcMonStatC`. Without that, we can safely remove it.

Delete [engine/math/get_square_root.asm](../blob/master/engine/math/get_square_root.asm).

Then edit [main.asm](../blob/master/main.asm):

```diff
-INCLUDE "engine/math/get_square_root.asm"
```


## 10. Add Zinc to boost Special Defense EVs

Now that Calcium only boosts Special Attack EVs, we need Zinc for Special Defense. Follow [this tutorial](Add-a-new-item) to add a new item.

First, add the essential data. Replace `ITEM_89` with `ZINC`; give it a name, description, and attributes (`9800, HELD_NONE, 0, CANT_SELECT, ITEM, ITEMMENU_PARTY, ITEMMENU_NOUSE`); and give it the `VitaminEffect`. (`ITEM_89` is not in `TimeCapsule_CatchRateItems`.)

Then edit [engine/items/item_effects.asm](../blob/master/engine/items/item_effects.asm) again:

```diff
 StatStrings:
 	dw .health
 	dw .attack
 	dw .defense
 	dw .speed
 	dw .sp_atk
+	dw .sp_def
 
 .health  db "HEALTH@"
 .attack  db "ATTACK@"
 .defense db "DEFENSE@"
 .speed   db "SPEED@"
 .sp_atk  db "SPCL.ATK@"
+.sp_def  db "SPCL.DEF@"
 
 ...
 
 Table_eeeb:
 	db HP_UP,    MON_HP_EV - MON_EVS
 	db PROTEIN, MON_ATK_EV - MON_EVS
 	db IRON,    MON_DEF_EV - MON_EVS
 	db CARBOS,  MON_SPD_EV - MON_EVS
 	db CALCIUM, MON_SAT_EV - MON_EVS
+	db ZINC,    MON_SDF_EV - MON_EVS
```

That's all!

![Screenshot](screenshots/zinc.png)

TODO: limit total EVs to 510.

TODO: add Macho Brace.