Skip to content

Adding a New Pokemon

JamesComo44 edited this page May 9, 2024 · 12 revisions

This tutorial will cover how to add a new pokemon to the game. Before we begin, we should note that there are some limits on how many Pokemon can be added to the gen 1 games. First, because pokemon indexes are stored as an 8 bit value, we are limited to a maximum of 255 total Pokemon species. To get beyond that, one would need to instead use 16 bit pokemon indexes which would require far reaching changes to the code that are well beyond the scope of this tutorial (at that point just use pokefirered). However there is another limit, because pokemon and trainers use the same indexes, with indexes 200 ($C8) and above being reserved for trainers. This leaves us with only 199 slots available for Pokemon. Though it also won't be covered in this tutorial, this limit is much easier to remove.

Contents

1. Define Pokemon constants

Our first step is to define the constants for our new pokemon. For this tutorial we will be adding Crobat. First add an index constant in constants/pokemon_constants.asm. We will put it in place of one of the empty missingno. slots.

...
 	const PINSIR             ; $1D
 	const TANGELA            ; $1E
-	const_skip               ; $1F
+	const CROBAT             ; $1F
 	const_skip               ; $20
 	const GROWLITHE          ; $21
 	const ONIX               ; $22
...

Next add a pokedex constant in constants/pokedex_constants.asm.

...
 	const DEX_DRAGONITE  ; 149
 	const DEX_MEWTWO     ; 150
 	const DEX_MEW        ; 151
+	const DEX_CROBAT     ; 152

 NUM_POKEMON EQU const_value - 1

Make sure to remember where you placed these constants. We will need to follow the same order later in the tutorial.

2. Base Stats and Moves

Our next step is to add the base data about our pokemon. We will first need to create data/pokemon/base_stats/crobat.asm and fill it with the necessary information (an easy way to do this for Crobat is to copy data/pokemon/base_stats/golbat.asm and update any relevant information).

+	db DEX_CROBAT ; pokedex id
+
+	db  85,  90,  80,  130,  80
+	;   hp  atk  def  spd  spc
+
+	db POISON, FLYING ; type
+	db 90 ; catch rate
+	db 241 ; base exp
+
+	INCBIN "gfx/pokemon/front/crobat.pic", 0, 1 ; sprite dimensions
+	dw CrobatPicFront, CrobatPicBack
+
+	db LEECH_LIFE, SCREECH, BITE, SCREECH ; level 1 learnset
+	db GROWTH_MEDIUM_FAST ; growth rate
+
+	; tm/hm learnset
+	tmhm RAZOR_WIND,   WHIRLWIND,    TOXIC,        TAKE_DOWN,    DOUBLE_EDGE,  \
+	     HYPER_BEAM,   RAGE,         MEGA_DRAIN,   MIMIC,        DOUBLE_TEAM,  \
+	     BIDE,         SWIFT,        REST,         SUBSTITUTE,   FLY
+	; end
+
+	db 0 ; padding

From top to bottom, what it all means:

  • species: This is the index of the pokemon.
  • base stats: HP, Attack, Defense, Speed, and Special, each from 1 to 255.
  • type: The primary and secondary types. Single-type Pokémon just repeat their type twice.
  • catch rate: A factor in the formula for capturing wild Pokémon. Lower rates are harder to capture. The highest value, 255, is used for common Pokémon like Pidgey and Rattata; legendaries like Mewtwo and Zapdos get as low as 3.
  • base experience yield: A factor in the formula for awarding experience. Higher values give more experience. As usual, since this is a single byte (db), its maximum is 255. (Chansey and Blissey both have 255 base exp.)
  • sprite dimensions: The size of the Pokémon's front sprite. We do this by including the first byte of the front sprite file we will add later in the tutorial. This byte that's one of three valid values: $55 for a 40x40-pixel (5x5-tile) sprite, $66 for 48x48 pixels (6x6 tiles), or $77 for 56x56 pixels (7x7 tiles).
  • pointer to front and back sprites: Two 2 byte addresses for where the front and back sprites for this pokemon are located.
  • level 1 learnset: Four bytes for the moves the pokemon starts with at level 1. If the pokemon has less than 4 starting moves, fill in the rest with NO_MOVE.
  • growth rate: The formula that's used to determine experience point thresholds for reaching each level. (The formula coefficients are defined in data/growth_rates.asm.) Valid values:
    • GROWTH_MEDIUM_FAST: exp = L³ (1,000,000 exp = level 100)
    • GROWTH_SLIGHTLY_FAST: exp = (3/4)L³ + 10L² − 30 (849,970 exp = level 100); unused
    • GROWTH_SLIGHTLY_SLOW: exp = (3/4)L³ + 20L² − 70 (949,930 exp = level 100); unused
    • GROWTH_MEDIUM_SLOW: exp = (6/5)L³ − 15L² + 100L − 140 (1,059,860 exp = level 100)
    • GROWTH_FAST: exp = (4/5)L³ (800,000 exp = level 100)
    • GROWTH_SLOW: exp = (5/4)L³ (1,250,000 exp = level 100)
  • TM/HM learnset: A list of which TMs and HMs this Pokémon can learn, passed to the tmhm macro. Valid values are defined in constants/item_constants.asm with the add_tm and add_hm macros.
  • padding: An unused byte that is always 0.

We will then have to add this to data/pokemon/base_stats.asm so that the file is included in the ROM.

...
 INCLUDE "data/pokemon/base_stats/dragonair.asm"
 INCLUDE "data/pokemon/base_stats/dragonite.asm"
 INCLUDE "data/pokemon/base_stats/mewtwo.asm"
+INCLUDE "data/pokemon/base_stats/mew.asm"
+INCLUDE "data/pokemon/base_stats/crobat.asm"
-	assert_table_length NUM_POKEMON - 1 ; discount Mew 
+	assert_table_length NUM_POKEMON

You will notice we also had to include the base data for mew here. This is because, due to mew being added later in development, its base data is located elsewhere on the ROM. However we need to follow the order of our constants we added above, so crobat needs to come after mew. We will then want to remove the original base data in main.asm for mew since we now have it here.

...
 SECTION "bank1", ROMX

 INCLUDE "data/sprites/facings.asm"
 INCLUDE "engine/events/black_out.asm"
-INCLUDE "data/pokemon/mew.asm"
 INCLUDE "engine/battle/safari_zone.asm"
 INCLUDE "engine/movie/title.asm"
...

Since mew's base stats have been moved to be alongside all the other base stats and is no longer a "special case", the GetMonHeader:: routine in home/pokemon.asm no longer needs the "special case" code for mew. So the following lines from that routine can be deleted:

...
 	cp FOSSIL_AERODACTYL ; Aerodactyl fossil
 	jr z, .specialID
-	cp MEW
-	jr z, .mew
 	predef IndexToPokedex   ; convert pokemon ID in [wd11e] to pokedex number
...
...
-	jr .done
-.mew
-	ld hl, MewBaseStats
-	ld de, wMonHeader
-	ld bc, BASE_DATA_SIZE
-	ld a, BANK(MewBaseStats)
-	call FarCopyData
 .done
...

Additionally, when we removed INCLUDE "data/pokemon/mew.asm" from main.asm, that also removed the front and back pic for mew. These two lines can be re-added anywhere that they fit in gfx/pics.asm (or main.asm if you really want):

MewPicFront:: INCBIN "gfx/pokemon/front/mew.pic"
MewPicBack::  INCBIN "gfx/pokemon/back/mewb.pic"

Next we will add moveset and evolution data in data/pokemon/evos_moves.asm. First add its pointer in the pointer table. Keep in mind this must follow the index order from constants/pokemon_constants.asm.

...
 	dw PinsirEvosMoves
 	dw TangelaEvosMoves
-	dw MissingNo1FEvosMoves
+	dw CrobatEvosMoves
 	dw MissingNo20EvosMoves
 	dw GrowlitheEvosMoves
 	dw OnixEvosMoves
...

Then, we add the moveset data. The number is the level at which the pokemon learns that move. Make sure that this list is in increasing order by level, or else it won't work.

...

-MissingNo1FEvosMoves:
+CrobatEvosMoves:
 ; Evolutions
 	db 0
 ; Learnset
+	db 10, SUPERSONIC
+	db 15, BITE
+	db 21, CONFUSE_RAY
+	db 33, WING_ATTACK
+	db 44, HAZE
+	db 55, SLUDGE
 	db 0
...

Since crobat evolves from golbat, we will need to add its evolution data under golbat. Since crobat normally evolves with friendship, which isn't present in gen 1, we will have to use a different evolution method. I will show several options here, first by level.

...
 GolbatEvosMoves:
 ; Evolutions
+	db EVOLVE_LEVEL, 32, CROBAT
 	db 0
...

Next, item evolution. If you'd like to add a new evolution item, the method described in the pokeyellow tutorial also works for pokered.

...
 GolbatEvosMoves:
 ; Evolutions
+	db EVOLVE_ITEM, MOON_STONE, 1, CROBAT
 	db 0
...

The last evolution method in gen 1 is by trade.

...
 GolbatEvosMoves:
 ; Evolutions
+	db EVOLVE_TRADE, 1, CROBAT
 	db 0
...

3. Miscellaneous Data

This next step is pretty easy. There are a few data tables that will need an entry for our new pokemon. We will start with data/pokemon/names.asm. All names are stored as 10 characters, so for shorter names we must use "@" as padding to fill out the 10 character limit.

...
 	db "PINSIR@@@@"
 	db "TANGELA@@@"
-	db "MISSINGNO."
+	db "CROBAT@@@@"
 	db "MISSINGNO."
 	db "GROWLITHE@"
...

Next we will add data for our pokemon's cry in data/pokemon/cries.asm. The first value is the pokemon base cry, the second is the pitch modifier, and the third is the length modifier. For more information on the specifics of what these values do, check out this video.

...
 	mon_cry SFX_CRY_14, $00, $80 ; Pinsir
 	mon_cry SFX_CRY_12, $00, $80 ; Tangela
-	mon_cry SFX_CRY_00, $00, $00 ; MissingNo.
+	mon_cry SFX_CRY_1D, $00, $C0 ; Crobat
 	mon_cry SFX_CRY_00, $00, $00 ; MissingNo.
 	mon_cry SFX_CRY_1F, $20, $40 ; Growlithe
...

Note that both of these tables followed the index order. However the next two tables will be in pokedex order. First is the party icon in data/pokemon/menu_icons.asm.

...
 	nybble ICON_SNAKE     ; Dragonair
 	nybble ICON_SNAKE     ; Dragonite
 	nybble ICON_MON       ; Mewtwo
 	nybble ICON_MON       ; Mew
+	nybble ICON_MON       ; Crobat
 	end_nybble_array NUM_POKEMON

Finally we choose a color palette in data/pokemon/palettes.asm. These color palettes are defined in data/sgb/sgb_palettes.asm if you want to change them, but that won't be covered in this tutorial.

...
 	db PAL_BLUEMON   ; DRAGONAIR
 	db PAL_BROWNMON  ; DRAGONITE
 	db PAL_MEWMON    ; MEWTWO
 	db PAL_MEWMON    ; MEW
+	db PAL_PURPLEMON ; CROBAT
 	assert_table_length NUM_POKEMON + 1

4. Pokedex Entry

Now we need to handle our new pokemon's page in the pokedex. We'll start with adding our pokemon to the pokedex order in data/pokemon/dex_order.asm

...
 	db DEX_PINSIR
 	db DEX_TANGELA
-	db 0 ; MISSINGNO.
+	db DEX_CROBAT
 	db 0 ; MISSINGNO.
 	db DEX_GROWLITHE
 	db DEX_ONIX
...

Next we add the pokedex entry data in data/pokemon/dex_entries.asm. First we add a pointer to the (PokedexEntryPointers) table.

...
 	dw PinsirDexEntry
 	dw TangelaDexEntry
-	dw MissingNoDexEntry
+	dw CrobatDexEntry
 	dw MissingNoDexEntry
 	dw GrowlitheDexEntry
 	dw OnixDexEntry
...

Then we add the species, height, and weight information, as well as a pointer to the pokedex entry text. The species name can be up to 10 characters (technically 11 will still fit in the Pokédex window). Be sure to end it with a "@". The height and weight values are in imperial units. 5,11 = 5 feet 11 inches; 1650 = 165.0 pounds.

...
+CrobatDexEntry:
+	db "BAT@"
+	db 5,11
+	dw 1650
+	text_far _CrobatDexEntry
+	text_end
...

Finally, we add the pokedex entry text in data/pokemon/dex_text.asm. The entry text is in two pages; the first starts with text, the second with page. Each page can fit three lines with 18 characters each. Here it's visual size that matters—"#MON" counts as 7 characters because it prints as "POKéMON". It ends it with dex.

...
+_CrobatDexEntry::
+	text "As a result of its"
+	next "pursuit of faster,"
+	next "yet more silent"
+	
+	page "flight, a new set"
+	next "of wings grew on"
+	next "its hind legs"
+	dex
...

5. Pokemon Pics

For our last step, we will add the front and back sprites of our new pokemon. The front picture will need to be a png file with only four colors and a size of either 40x40, 48x48, or 56x56 pixels. The back picture is a 32x32 file, however the image itself is 28x28 pixels, with the bottom and rightmost four rows left blank. This is because the 28x28 image will be scaled up by 2 to fill the 56x56 space, but the dimensions of the file must be a multiple of 8. You may need to run the palfix.py tool on your png file to get your palettes to look correct in game.

The files will be placed in gfx/pokemon/front and gfx/pokemon/back, respectively. We then include those files in gfx/pics.asm. Where we place them depends on what method we use to load the pokemon pictures. For this method, we must follow the index order.

...
 SECTION "Pics 2", ROMX

+CrobatPicFront::      INCBIN "gfx/pokemon/front/crobat.pic"
+CrobatPicBack::       INCBIN "gfx/pokemon/back/crobatb.pic"
 GrowlithePicFront::   INCBIN "gfx/pokemon/front/growlithe.pic"
 GrowlithePicBack::    INCBIN "gfx/pokemon/back/growlitheb.pic"
 OnixPicFront::        INCBIN "gfx/pokemon/front/onix.pic"
 OnixPicBack::         INCBIN "gfx/pokemon/back/onixb.pic"
...

Because these banks are very full, we will likely have to shift some pokemon down into the next bank. Make sure that as you are doing this they remain in index order.

...
 JynxPicFront::        INCBIN "gfx/pokemon/front/jynx.pic"
 JynxPicBack::         INCBIN "gfx/pokemon/back/jynxb.pic"
-MoltresPicFront::     INCBIN "gfx/pokemon/front/moltres.pic"
-MoltresPicBack::      INCBIN "gfx/pokemon/back/moltresb.pic"


 SECTION "Pics 3", ROMX

+MoltresPicFront::     INCBIN "gfx/pokemon/front/moltres.pic"
+MoltresPicBack::      INCBIN "gfx/pokemon/back/moltresb.pic"
 ArticunoPicFront::    INCBIN "gfx/pokemon/front/articuno.pic"
 ArticunoPicBack::     INCBIN "gfx/pokemon/back/articunob.pic"
...

If you recieve an error saying that a pic section grew too big or that sections would extend past the end of ROMX, you likely need to move more pokemon to the next bank. This may take a few tries, so it helps to get this resolved before moving on to the next step, which is to adjust the code that uncompresses the images. This is located in home/pics.asm. The code uses the last pokemon in each bank, so because we have shifted the banks we will need to update this code to reflect that. That will look something like this, but the specifics will depend on how many pokemon you add and the size of their image files.

...
 	cp TANGELA + 1
 	ld a, BANK("Pics 1")
 	jr c, .GotBank
 	ld a, b
-	cp MOLTRES + 1
+	cp JYNX + 1
 	ld a, BANK("Pics 2")
 	jr c, .GotBank
 	ld a, b
-	cp BEEDRILL + 2
+	cp KAKUNA + 1
 	ld a, BANK("Pics 3")
 	jr c, .GotBank
 	ld a, b
-	cp STARMIE + 1
+	cp PIDGEOT + 1
 	ld a, BANK("Pics 4")
 	jr c, .GotBank
 	ld a, BANK("Pics 5")
...

Again, these specific changes may not be what your code needs to look like. Just make sure the pokemon you are comparing against is the last pokemon in their bank.

And with that, we have added a new pokemon!

Clone this wiki locally