Erratic and Fluctuating experience growth rates - pret/pokecrystal GitHub Wiki
A Pokémon's growth rate determines how much experience it needs to reach a given level. Gen 1 and 2 each used four different growth rates, known as Fast, Slow, Medium Fast, and Medium Slow. (Gen 2 also has leftover formula data for two unused growth rates, "Slightly Fast" and "Slightly Slow".) The Fast rate reaches level 100 with just 800,000 experience, while the Slow rate needs 1,250,000.
Gen 3 introduced two new growth rates: Erratic, which needs a mere 600,000 experience to reach level 100; and Fluctuating, which needs 1,640,000. Neither of these rates is used by many Pokémon even in Gen 3; and beyond Gen 3, only Cranidos → Rampardos, Shieldon → Bastiodon, and Finneon → Lumineon are in the Erratic group, and only Drifloon → Drifblim are in the Fluctuating group.
The original growth rate formulas were all cubic polynomials, but the Erratic and Fluctuating formulas are more complicated piecewise quartic polynomials. It's simplest to do the calculating beforehand and store a lookup table of experience amounts for each level in the ROM (the same approach that Gen 3 took for every growth rate).
Contents
- Define growth rate constants
- Define lookup tables of experience amounts
- Look up experience amounts for a given level
- Fix a bank overflow error
1. Define growth rate constants
Edit constants/pokemon_data_constants.asm:
; wBaseGrowthRate values
; GrowthRates indexes (see data/growth_rates.asm)
const_def
const GROWTH_MEDIUM_FAST
const GROWTH_SLIGHTLY_FAST
const GROWTH_SLIGHTLY_SLOW
const GROWTH_MEDIUM_SLOW
const GROWTH_FAST
const GROWTH_SLOW
+ const GROWTH_ERRATIC
+ const GROWTH_FLUCTUATING
DEF NUM_GROWTH_RATES EQU const_value
2. Define lookup tables of experience amounts
Edit data/growth_rates.asm:
GrowthRates:
; entries correspond to GROWTH_* (see constants/pokemon_data_constants.asm)
table_width 4, GrowthRates
growth_rate 1, 1, 0, 0, 0 ; Medium Fast
growth_rate 3, 4, 10, 0, 30 ; Slightly Fast
growth_rate 3, 4, 20, 0, 70 ; Slightly Slow
growth_rate 6, 5, -15, 100, 140 ; Medium Slow
growth_rate 4, 5, 0, 0, 0 ; Fast
growth_rate 5, 4, 0, 0, 0 ; Slow
- assert_table_length NUM_GROWTH_RATES
+ assert_table_length NUM_GROWTH_RATES - 2 ; exclude Erratic and Fluctuating
+
+ErraticExperience:
+ table_width 3, ErraticExperience
+ for n, 1, MAX_LEVEL + 1
+ if n == 1
+ dt 0
+ elif n < 50
+ dt (n**3 * (100 - n)) / 50
+ elif n < 68
+ dt (n**3 * (150 - n)) / 100
+ elif n < 98
+ dt (n**3 * ((1911 - 10 * n) / 3)) / 500
+ else
+ dt (n**3 * (160 - n)) / 100
+ endc
+ endr
+ assert_table_length MAX_LEVEL
+
+FluctuatingExperience:
+ table_width 3, FluctuatingExperience
+ for n, 1, MAX_LEVEL + 1
+ if n == 1
+ dt 0
+ elif n < 15
+ dt (n**3 * ((n + 1) / 3 + 24)) / 50
+ elif n < 36
+ dt (n**3 * (n + 14)) / 50
+ else
+ dt (n**3 * (n / 2 + 32)) / 50
+ endc
+ endr
+ assert_table_length MAX_LEVEL
The values in these tables should match the ones from src/data/pokemon/experience_tables.h in the pokeruby disassembly. We do the computation with macros at assembly time, instead of dynamically calculating while the game runs.
3. Look up experience amounts for a given level
Edit engine/pokemon/experience.asm:
CalcExpAtLevel:
; (a/b)*n**3 + c*n**2 + d*n - e
ld a, [wBaseGrowthRate]
+ cp GROWTH_ERRATIC
+ jp z, .erratic
+ cp GROWTH_FLUCTUATING
+ jp z, .fluctuating
add a
add a
ld c, a
ld b, 0
ld hl, GrowthRates
add hl, bc
; Cube the level
call .LevelSquared
ld a, d
ldh [hMultiplier], a
call Multiply
...
.LevelSquared:
xor a
ldh [hMultiplicand + 0], a
ldh [hMultiplicand + 1], a
ld a, d
ldh [hMultiplicand + 2], a
ldh [hMultiplier], a
jp Multiply
+
+.erratic:
+ ld hl, ErraticExperience
+ jr .lookup_table
+
+.fluctuating:
+ ld hl, FluctuatingExperience
+.lookup_table
+ ld c, d
+ ld b, 0
+ dec c
+ add hl, bc
+ add hl, bc
+ add hl, bc
+ xor a
+ ldh [hProduct + 0], a
+ ld a, [hli]
+ ldh [hProduct + 1], a
+ ld a, [hli]
+ ldh [hProduct + 2], a
+ ld a, [hli]
+ ldh [hProduct + 3], a
+ ret
INCLUDE "data/growth_rates.asm"
4. Fix a bank overflow error
We're done implementing the new growth rates, but make
won't compile the ROM:
Section 'bank14' is too big (max size = 0x4000 bytes).
It turns out that the lookup tables overflowed their ROM bank, so we need to move some of content from bank14
to a different bank, or delete some. In fact, there is some unused content in bank14
that can be deleted to free up space.
Edit main.asm:
SECTION "bank14", ROMX
INCLUDE "engine/pokemon/party_menu.asm"
INCLUDE "engine/events/poisonstep.asm"
INCLUDE "engine/events/sweet_scent.asm"
INCLUDE "engine/events/squirtbottle.asm"
INCLUDE "engine/events/card_key.asm"
INCLUDE "engine/events/basement_key.asm"
INCLUDE "engine/events/sacred_ash.asm"
INCLUDE "engine/pokemon/tempmon.asm"
INCLUDE "engine/pokemon/types.asm"
-INCLUDE "engine/battle/unreferenced_getgen1trainerclassname.asm"
INCLUDE "engine/pokemon/mon_stats.asm"
INCLUDE "engine/link/init_list.asm"
INCLUDE "engine/pokemon/experience.asm"
INCLUDE "engine/pokemon/switchpartymons.asm"
INCLUDE "engine/gfx/load_pics.asm"
INCLUDE "engine/pokemon/move_mon_wo_mail.asm"
INCLUDE "data/pokemon/base_stats.asm"
INCLUDE "data/pokemon/names.asm"
-INCLUDE "data/pokemon/unused_pic_banks.asm"
-
-UnknownEggPic::
-; Another egg pic. This is shifted up a few pixels.
-INCBIN "gfx/unknown/unknown_egg.2bpp.lz"
Now edit home/mon_data.asm to remove the pointless use of UnknownEggPic
:
.egg
-; ????
- ld de, UnknownEggPic
-
; Sprite dimensions
ld b, $55 ; 5x5
ld hl, wBasePicSize
ld [hl], b
-
-; ????
- ld hl, wBasePadding
- ld [hl], e
- inc hl
- ld [hl], d
- inc hl
- ld [hl], e
- inc hl
- ld [hl], d
- jr .end
And finally delete engine/battle/unreferenced_getgen1trainerclassname.asm, data/pokemon/unused_pic_banks.asm, gfx/unknown/unknown_egg.png, and gfx/unknown/unknown_egg.2bpp.lz.a5b6cbfa.
Now you'll be able to use the constants GROWTH_ERRATIC
and GROWTH_FLUCTUATING
when defining new Pokémon in data/pokemon/base_stats.