Skip to content

Color party menu icons by species

Logan Hansen edited this page Feb 19, 2024 · 29 revisions

The tutorial to add a new party menu icon explains how to add a different icon for each Pokémon species, but the icons are all still red. This tutorial will explain how to color each species' icon from a set of eight palettes. (Coloring them uniquely, with the same palettes as their battle sprites, is beyond the scope of this tutorial.)

Contents

  1. Define the eight icon palettes
  2. Assign the palettes to Pokémon
  3. Allow sprite animations to have dynamic palette attributes
  4. Make party icons' sprite animation data use dynamic palettes
  5. Load the correct icon palettes for species and shininess
  6. Make the overworld Fly animation use the right palette
  7. Day care Pokémons use their custom palette
  8. Make the Move Deleter's delete move menu display shiny palettes
  9. Fix a bank overflow error

1. Define the eight icon palettes

First we'll define the constants we need for our eight palettes. Let's edit constants/icon_constants.asm, adding them at the end of the file:

+; party menu icon palettes
+	const_def
+	const PAL_ICON_RED    ; 0
+	const PAL_ICON_BLUE   ; 1
+	const PAL_ICON_GREEN  ; 2
+	const PAL_ICON_BROWN  ; 3
+	const PAL_ICON_PINK   ; 4
+	const PAL_ICON_GRAY   ; 5
+	const PAL_ICON_TEAL   ; 6
+	const PAL_ICON_PURPLE ; 7

Then edit gfx/stats/party_menu_ob.pal to add the respective colors; delete the entire content and replace it with this:

+	RGB 27,31,27, 31,19,10, 31,07,01, 00,00,00 ; red
+	RGB 27,31,27, 31,19,10, 10,09,31, 00,00,00 ; blue
+	RGB 27,31,27, 31,19,10, 07,23,03, 00,00,00 ; green
+	RGB 27,31,27, 31,19,10, 15,10,03, 00,00,00 ; brown
+	RGB 27,31,27, 31,19,10, 29,05,13, 00,00,00 ; pink
+	RGB 27,31,27, 31,19,10, 13,13,13, 00,00,00 ; gray
+	RGB 27,31,27, 31,19,10, 03,23,21, 00,00,00 ; teal
+	RGB 27,31,27, 31,19,10, 18,04,18, 00,00,00 ; purple

Finally, edit engine/gfx/color.asm:

 InitPartyMenuOBPals:
 	ld hl, PartyMenuOBPals
 	ld de, wOBPals1
-	ld bc, 2 palettes
+	ld bc, 8 palettes
 	ld a, BANK(wOBPals1)
 	call FarCopyWRAM
 	ret

These will be the eight possible colors for all 251 Pokémon (and Eggs). You can choose any colors you want, but the first two will have to be red and blue respectively, since the Fly map uses those for the player sprite at the same time as it shows the flying Pokémon. (InitPartyMenuOBPals used to only load those first two, since they were all that was needed: Chris and the Pokémon were red, Kris was blue.)

(If you've followed the tutorial to edit the player colors, and/or the tutorial to add a third player choice, that's fine; just make sure that the possible player colors all correspond to identical Pokémon icon colors.)

2. Assign the palettes to Pokémon

Create data/pokemon/menu_icon_pals.asm:

Click here to view code
+MACRO icon_pals
+	dn PAL_ICON_\1, PAL_ICON_\2
+ENDM
+
+MonMenuIconPals:
+	table_width 1, MonMenuIconPals
+	;         normal, shiny
+	icon_pals TEAL, GREEN   ; BULBASAUR
+	icon_pals TEAL, GREEN   ; IVYSAUR
+	icon_pals TEAL, GREEN   ; VENUSAUR
+	icon_pals RED, BROWN    ; CHARMANDER
+	icon_pals RED, PINK     ; CHARMELEON
+	icon_pals RED, PURPLE   ; CHARIZARD
+	icon_pals BLUE, GREEN   ; SQUIRTLE
+	icon_pals BLUE, GREEN   ; WARTORTLE
+	icon_pals BLUE, TEAL    ; BLASTOISE
+	icon_pals GREEN, BROWN  ; CATERPIE
+	icon_pals GREEN, BROWN  ; METAPOD
+	icon_pals BLUE, PINK    ; BUTTERFREE
+	icon_pals RED, PINK     ; WEEDLE
+	icon_pals BROWN, GREEN  ; KAKUNA
+	icon_pals RED, TEAL     ; BEEDRILL
+	icon_pals BROWN, GREEN  ; PIDGEY
+	icon_pals BROWN, GREEN  ; PIDGEOTTO
+	icon_pals BROWN, GREEN  ; PIDGEOT
+	icon_pals PURPLE, GREEN ; RATTATA
+	icon_pals BROWN, RED    ; RATICATE
+	icon_pals BROWN, BROWN  ; SPEAROW
+	icon_pals BROWN, GREEN  ; FEAROW
+	icon_pals PURPLE, GREEN ; EKANS
+	icon_pals PURPLE, GREEN ; ARBOK
+	icon_pals RED, PINK     ; PIKACHU
+	icon_pals RED, BROWN    ; RAICHU
+	icon_pals BROWN, TEAL   ; SANDSHREW
+	icon_pals BROWN, RED    ; SANDSLASH
+	icon_pals BLUE, PINK    ; NIDORAN_F
+	icon_pals BLUE, PINK    ; NIDORINA
+	icon_pals BLUE, GREEN   ; NIDOQUEEN
+	icon_pals PURPLE, BLUE  ; NIDORAN_M
+	icon_pals PURPLE, BLUE  ; NIDORINO
+	icon_pals PURPLE, BLUE  ; NIDOKING
+	icon_pals PINK, GREEN   ; CLEFAIRY
+	icon_pals PINK, GREEN   ; CLEFABLE
+	icon_pals RED, BROWN    ; VULPIX
+	icon_pals BROWN, GRAY   ; NINETALES
+	icon_pals PINK, GREEN   ; JIGGLYPUFF
+	icon_pals PINK, GREEN   ; WIGGLYTUFF
+	icon_pals BLUE, GREEN   ; ZUBAT
+	icon_pals BLUE, GREEN   ; GOLBAT
+	icon_pals GREEN, BROWN  ; ODDISH
+	icon_pals RED, BROWN    ; GLOOM
+	icon_pals RED, BROWN    ; VILEPLUME
+	icon_pals RED, GREEN    ; PARAS
+	icon_pals RED, GREEN    ; PARASECT
+	icon_pals PURPLE, BLUE  ; VENONAT
+	icon_pals PURPLE, BLUE  ; VENOMOTH
+	icon_pals BROWN, BLUE   ; DIGLETT
+	icon_pals BROWN, BLUE   ; DUGTRIO
+	icon_pals BROWN, PINK   ; MEOWTH
+	icon_pals BROWN, PINK   ; PERSIAN
+	icon_pals BROWN, BLUE   ; PSYDUCK
+	icon_pals BLUE, TEAL    ; GOLDUCK
+	icon_pals BROWN, GREEN  ; MANKEY
+	icon_pals BROWN, GREEN  ; PRIMEAPE
+	icon_pals RED, GREEN    ; GROWLITHE
+	icon_pals RED, GREEN    ; ARCANINE
+	icon_pals BLUE, BLUE    ; POLIWAG
+	icon_pals BLUE, BLUE    ; POLIWHIRL
+	icon_pals BLUE, TEAL    ; POLIWRATH
+	icon_pals BROWN, PINK   ; ABRA
+	icon_pals BROWN, PINK   ; KADABRA
+	icon_pals BROWN, PINK   ; ALAKAZAM
+	icon_pals GRAY, GREEN   ; MACHOP
+	icon_pals GRAY, GREEN   ; MACHOKE
+	icon_pals GRAY, GREEN   ; MACHAMP
+	icon_pals GREEN, BROWN  ; BELLSPROUT
+	icon_pals GREEN, BROWN  ; WEEPINBELL
+	icon_pals GREEN, BROWN  ; VICTREEBEL
+	icon_pals BLUE, TEAL    ; TENTACOOL
+	icon_pals BLUE, GREEN   ; TENTACRUEL
+	icon_pals BROWN, RED    ; GEODUDE
+	icon_pals BROWN, RED    ; GRAVELER
+	icon_pals BROWN, RED    ; GOLEM
+	icon_pals RED, BROWN    ; PONYTA
+	icon_pals RED, PURPLE   ; RAPIDASH
+	icon_pals PINK, PURPLE  ; SLOWPOKE
+	icon_pals PINK, PURPLE  ; SLOWBRO
+	icon_pals BLUE, BROWN   ; MAGNEMITE
+	icon_pals BLUE, BROWN   ; MAGNETON
+	icon_pals BROWN, GRAY   ; FARFETCH_D
+	icon_pals BROWN, GREEN  ; DODUO
+	icon_pals BROWN, GREEN  ; DODRIO
+	icon_pals BLUE, GRAY    ; SEEL
+	icon_pals BLUE, GRAY    ; DEWGONG
+	icon_pals PURPLE, GREEN ; GRIMER
+	icon_pals PURPLE, GREEN ; MUK
+	icon_pals PURPLE, BROWN ; SHELLDER
+	icon_pals PURPLE, BLUE  ; CLOYSTER
+	icon_pals PURPLE, BLUE  ; GASTLY
+	icon_pals PURPLE, BLUE  ; HAUNTER
+	icon_pals PURPLE, PINK  ; GENGAR
+	icon_pals GRAY, GREEN   ; ONIX
+	icon_pals BROWN, PINK   ; DROWZEE
+	icon_pals BROWN, PINK   ; HYPNO
+	icon_pals RED, GREEN    ; KRABBY
+	icon_pals RED, GREEN    ; KINGLER
+	icon_pals RED, BLUE     ; VOLTORB
+	icon_pals RED, BLUE     ; ELECTRODE
+	icon_pals PINK, GREEN   ; EXEGGCUTE
+	icon_pals GREEN, BROWN  ; EXEGGUTOR
+	icon_pals BROWN, TEAL   ; CUBONE
+	icon_pals BROWN, GREEN  ; MAROWAK
+	icon_pals BROWN, GREEN  ; HITMONLEE
+	icon_pals BROWN, GREEN  ; HITMONCHAN
+	icon_pals PINK, GREEN   ; LICKITUNG
+	icon_pals PURPLE, BLUE  ; KOFFING
+	icon_pals PURPLE, BLUE  ; WEEZING
+	icon_pals GRAY, BROWN   ; RHYHORN
+	icon_pals GRAY, GRAY    ; RHYDON
+	icon_pals PINK, GREEN   ; CHANSEY
+	icon_pals BLUE, GREEN   ; TANGELA
+	icon_pals BROWN, GRAY   ; KANGASKHAN
+	icon_pals BLUE, PINK    ; HORSEA
+	icon_pals BLUE, PINK    ; SEADRA
+	icon_pals RED, BROWN    ; GOLDEEN
+	icon_pals RED, BROWN    ; SEAKING
+	icon_pals BROWN, BLUE   ; STARYU
+	icon_pals PURPLE, BLUE  ; STARMIE
+	icon_pals PINK, GREEN   ; MR__MIME
+	icon_pals GREEN, GREEN  ; SCYTHER
+	icon_pals PURPLE, PINK  ; JYNX
+	icon_pals BROWN, GREEN  ; ELECTABUZZ
+	icon_pals RED, PINK     ; MAGMAR
+	icon_pals BROWN, BLUE   ; PINSIR
+	icon_pals BROWN, GREEN  ; TAUROS
+	icon_pals RED, GREEN    ; MAGIKARP
+	icon_pals BLUE, RED     ; GYARADOS
+	icon_pals BLUE, PINK    ; LAPRAS
+	icon_pals PINK, BLUE    ; DITTO
+	icon_pals BROWN, GRAY   ; EEVEE
+	icon_pals BLUE, PURPLE  ; VAPOREON
+	icon_pals BROWN, GREEN  ; JOLTEON
+	icon_pals RED, BROWN    ; FLAREON
+	icon_pals PINK, PURPLE  ; PORYGON
+	icon_pals BLUE, GRAY    ; OMANYTE
+	icon_pals BLUE, GRAY    ; OMASTAR
+	icon_pals BROWN, GREEN  ; KABUTO
+	icon_pals BROWN, GREEN  ; KABUTOPS
+	icon_pals GRAY, PURPLE  ; AERODACTYL
+	icon_pals BROWN, BLUE   ; SNORLAX
+	icon_pals BLUE, BLUE    ; ARTICUNO
+	icon_pals BROWN, RED    ; ZAPDOS
+	icon_pals RED, PINK     ; MOLTRES
+	icon_pals BLUE, PURPLE  ; DRATINI
+	icon_pals BLUE, PURPLE  ; DRAGONAIR
+	icon_pals RED, GREEN    ; DRAGONITE
+	icon_pals PURPLE, GREEN ; MEWTWO
+	icon_pals PINK, BLUE    ; MEW
+	icon_pals GREEN, BROWN  ; CHIKORITA
+	icon_pals GREEN, BROWN  ; BAYLEEF
+	icon_pals GREEN, BROWN  ; MEGANIUM
+	icon_pals RED, PURPLE   ; CYNDAQUIL
+	icon_pals RED, PURPLE   ; QUILAVA
+	icon_pals RED, PURPLE   ; TYPHLOSION
+	icon_pals BLUE, GREEN   ; TOTODILE
+	icon_pals BLUE, GREEN   ; CROCONAW
+	icon_pals BLUE, TEAL    ; FERALIGATR
+	icon_pals BROWN, PINK   ; SENTRET
+	icon_pals BROWN, PINK   ; FURRET
+	icon_pals BROWN, GREEN  ; HOOTHOOT
+	icon_pals BROWN, GREEN  ; NOCTOWL
+	icon_pals RED, BROWN    ; LEDYBA
+	icon_pals RED, BROWN    ; LEDIAN
+	icon_pals GREEN, PURPLE ; SPINARAK
+	icon_pals PINK, PURPLE   ; ARIADOS
+	icon_pals PURPLE, PINK  ; CROBAT
+	icon_pals BLUE, TEAL    ; CHINCHOU
+	icon_pals BLUE, TEAL    ; LANTURN
+	icon_pals RED, BROWN    ; PICHU
+	icon_pals PINK, GREEN   ; CLEFFA
+	icon_pals PINK, GREEN   ; IGGLYBUFF
+	icon_pals RED, BLUE     ; TOGEPI
+	icon_pals RED, BLUE     ; TOGETIC
+	icon_pals GREEN, GREEN  ; NATU
+	icon_pals GREEN, GREEN  ; XATU
+	icon_pals BLUE, BLUE    ; MAREEP
+	icon_pals PINK, PINK    ; FLAAFFY
+	icon_pals BROWN, GRAY   ; AMPHAROS
+	icon_pals GREEN, BLUE   ; BELLOSSOM
+	icon_pals BLUE, GREEN   ; MARILL
+	icon_pals BLUE, BROWN   ; AZUMARILL
+	icon_pals GREEN, PINK   ; SUDOWOODO
+	icon_pals GREEN, GRAY   ; POLITOED
+	icon_pals PINK, GREEN   ; HOPPIP
+	icon_pals GREEN, PURPLE ; SKIPLOOM
+	icon_pals BLUE, PINK    ; JUMPLUFF
+	icon_pals PURPLE, PINK  ; AIPOM
+	icon_pals GREEN, BROWN  ; SUNKERN
+	icon_pals GREEN, BROWN  ; SUNFLORA
+	icon_pals RED, BLUE     ; YANMA
+	icon_pals BLUE, PINK    ; WOOPER
+	icon_pals BLUE, PURPLE  ; QUAGSIRE
+	icon_pals PINK, GREEN   ; ESPEON
+	icon_pals BROWN, BLUE   ; UMBREON
+	icon_pals BLUE, PURPLE  ; MURKROW
+	icon_pals PINK, BLUE    ; SLOWKING
+	icon_pals PURPLE, GREEN ; MISDREAVUS
+	icon_pals GRAY, BLUE    ; UNOWN
+	icon_pals BLUE, PURPLE  ; WOBBUFFET
+	icon_pals PINK, BLUE    ; GIRAFARIG
+	icon_pals GREEN, RED    ; PINECO
+	icon_pals RED, GREEN    ; FORRETRESS
+	icon_pals BLUE, PINK    ; DUNSPARCE
+	icon_pals PINK, BLUE    ; GLIGAR
+	icon_pals GRAY, BROWN   ; STEELIX
+	icon_pals PINK, BLUE    ; SNUBBULL
+	icon_pals PURPLE, GRAY  ; GRANBULL
+	icon_pals BLUE, PINK    ; QWILFISH
+	icon_pals RED, GREEN    ; SCIZOR
+	icon_pals RED, BLUE     ; SHUCKLE
+	icon_pals BLUE, PURPLE  ; HERACROSS
+	icon_pals BLUE, PINK    ; SNEASEL
+	icon_pals BROWN, GREEN  ; TEDDIURSA
+	icon_pals BROWN, GREEN  ; URSARING
+	icon_pals RED, GRAY     ; SLUGMA
+	icon_pals RED, BLUE     ; MAGCARGO
+	icon_pals BROWN, BLUE   ; SWINUB
+	icon_pals BROWN, GREEN  ; PILOSWINE
+	icon_pals PINK, BLUE    ; CORSOLA
+	icon_pals BLUE, GRAY    ; REMORAID
+	icon_pals RED, GREEN    ; OCTILLERY
+	icon_pals RED, PINK     ; DELIBIRD
+	icon_pals BLUE, TEAL    ; MANTINE
+	icon_pals RED, GREEN    ; SKARMORY
+	icon_pals RED, BLUE     ; HOUNDOUR
+	icon_pals RED, BLUE     ; HOUNDOOM
+	icon_pals BLUE, PURPLE  ; KINGDRA
+	icon_pals BLUE, TEAL    ; PHANPY
+	icon_pals BLUE, BROWN   ; DONPHAN
+	icon_pals PINK, BLUE    ; PORYGON2
+	icon_pals BROWN, GREEN  ; STANTLER
+	icon_pals BROWN, GREEN  ; SMEARGLE
+	icon_pals PINK, BLUE    ; TYROGUE
+	icon_pals BROWN, PURPLE ; HITMONTOP
+	icon_pals PURPLE, PINK  ; SMOOCHUM
+	icon_pals BROWN, BROWN  ; ELEKID
+	icon_pals RED, BROWN    ; MAGBY
+	icon_pals PINK, BLUE    ; MILTANK
+	icon_pals PINK, PINK    ; BLISSEY
+	icon_pals BROWN, BROWN  ; RAIKOU
+	icon_pals RED, RED      ; ENTEI
+	icon_pals BLUE, BLUE    ; SUICUNE
+	icon_pals GREEN, GREEN  ; LARVITAR
+	icon_pals BLUE, PURPLE  ; PUPITAR
+	icon_pals GREEN, PURPLE ; TYRANITAR
+	icon_pals BLUE, PINK    ; LUGIA
+	icon_pals RED, BROWN    ; HO_OH
+	icon_pals GREEN, PINK   ; CELEBI
+	assert_table_length NUM_POKEMON
+
+	icon_pals RED,    RED   ; 252
+	icon_pals GREEN,  BLUE  ; EGG
+	icon_pals RED,    RED   ; 254

Then edit engine/gfx/mon_icons.asm:

 INCLUDE "data/pokemon/menu_icons.asm"
+
+INCLUDE "data/pokemon/menu_icon_pals.asm"

 INCLUDE "data/icon_pointers.asm"

 INCLUDE "gfx/icons.asm"

This defines the normal and shiny color for each Pokémon. Note that EGG (index 253, or $FD) also has an entry; if you give it a different shiny color (here BLUE instead of RED) you'll be able to tell shiny Eggs apart.

3. Allow sprite animations to have dynamic palette attributes

The sprite animation engine has certain data for Pokémon icons, Pokémon holding items, and Pokémon holding Mail. This data is accessed at certain points in the code, like the party menu and the Fly map, and is hard-coded to use PAL_OW_RED. To change the color based on species, we need to achieve two sub-goals: get the right color corresponding to the current Pokémon species and apply that color to the sprite animation; and don't then force the animation to be red. Let's take those in reverse order; it's easier.

Edit engine/sprite_anims/core.asm:

Note: For older versions of the pokecrystal disassembly, you'll have to modify engine/gfx/sprites.asm instead (file name and location was changed as of Sep 18, 2023).

 UpdateAnimFrame:
 	...
 	; fourth byte: attributes
 	; [de] = GetSpriteOAMAttr([hl])
+	ld a, [hl]
+	cp -1
+	jr z, .skip_attributes
 	call GetSpriteOAMAttr
 	ld [de], a
+.skip_attributes
 	inc hl
 	inc de
 	ld a, e
 	ld [wCurSpriteOAMAddr], a
 	cp LOW(wShadowOAMEnd)
 	jr nc, .reached_the_end
 	dec c
 	jr nz, .loop
 	pop bc
 	jr .done

Now we'll be able to load a species-appropriate palette index, and then load some sprite animation data; if that data uses −1 for the attribute value, it will just apply the already-loaded palette index instead. We'll see where that −1 value gets used in the next step.

4. Make party icons' sprite animation data use dynamic palettes

First, edit constants/sprite_anim_constants.asm:

 ; SpriteAnimOAMData indexes (see data/sprite_anims/oam.asm)
 	const_def
 	const SPRITE_ANIM_OAMSET_RED_WALK_1                 ; 00
 	const SPRITE_ANIM_OAMSET_RED_WALK_2                 ; 01
 	...
 	const SPRITE_ANIM_OAMSET_PARTY_MON_WITH_MAIL_1      ; 3d
 	const SPRITE_ANIM_OAMSET_PARTY_MON_WITH_MAIL_2      ; 3e
 	const SPRITE_ANIM_OAMSET_PARTY_MON_WITH_ITEM_1      ; 3f
 	const SPRITE_ANIM_OAMSET_PARTY_MON_WITH_ITEM_2      ; 40
 	...
 	const SPRITE_ANIM_OAMSET_GAMEFREAK_LOGO_10          ; 8a
 	const SPRITE_ANIM_OAMSET_GAMEFREAK_LOGO_11          ; 8b
+	const SPRITE_ANIM_OAMSET_PARTY_MON_1                ; 8c
+	const SPRITE_ANIM_OAMSET_PARTY_MON_2                ; 8d

We're going to be updating the data soon for those SPRITE_ANIM_OAMSET_PARTY_MON_WITH_MAIL_* and SPRITE_ANIM_OAMSET_PARTY_MON_WITH_ITEM_* indexes; but the SPRITE_ANIM_OAMSET_RED_WALK_* indexes are used for more things than just Pokémon, so we're creating new SPRITE_ANIM_OAMSET_PARTY_MON_* indexes for plain party icons.

Edit data/sprite_anims/framesets.asm:

 .Frameset_PartyMon:
-	oamframe SPRITE_ANIM_OAMSET_RED_WALK_1,  8
-	oamframe SPRITE_ANIM_OAMSET_RED_WALK_2,  8
+	oamframe SPRITE_ANIM_OAMSET_PARTY_MON_1,  8
+	oamframe SPRITE_ANIM_OAMSET_PARTY_MON_2,  8
 	oamrestart

 .Frameset_PartyMonWithMail:
 	oamframe SPRITE_ANIM_OAMSET_PARTY_MON_WITH_MAIL_1,  8
 	oamframe SPRITE_ANIM_OAMSET_PARTY_MON_WITH_MAIL_2,  8
 	oamrestart

 .Frameset_PartyMonWithItem:
 	oamframe SPRITE_ANIM_OAMSET_PARTY_MON_WITH_ITEM_1,  8
 	oamframe SPRITE_ANIM_OAMSET_PARTY_MON_WITH_ITEM_2,  8
 	oamrestart

 .Frameset_PartyMonFast:
-	oamframe SPRITE_ANIM_OAMSET_RED_WALK_1,  4
-	oamframe SPRITE_ANIM_OAMSET_RED_WALK_2,  4
+	oamframe SPRITE_ANIM_OAMSET_PARTY_MON_1,  4
+	oamframe SPRITE_ANIM_OAMSET_PARTY_MON_2,  4
 	oamrestart

 .Frameset_PartyMonWithMailFast:
 	oamframe SPRITE_ANIM_OAMSET_PARTY_MON_WITH_MAIL_1,  4
 	oamframe SPRITE_ANIM_OAMSET_PARTY_MON_WITH_MAIL_2,  4
 	oamrestart

 .Frameset_PartyMonWithItemFast:
 	oamframe SPRITE_ANIM_OAMSET_PARTY_MON_WITH_ITEM_1,  4
 	oamframe SPRITE_ANIM_OAMSET_PARTY_MON_WITH_ITEM_2,  4
 	oamrestart

 .Frameset_RedWalk:
 	oamframe SPRITE_ANIM_OAMSET_RED_WALK_1,  8
 	oamframe SPRITE_ANIM_OAMSET_RED_WALK_2,  8
 	oamframe SPRITE_ANIM_OAMSET_RED_WALK_1,  8
 	oamframe SPRITE_ANIM_OAMSET_RED_WALK_2,  8, OAM_X_FLIP
 	oamrestart

And edit data/sprite_anims/oam.asm:

 SpriteAnimOAMData:
 ; entries correspond to SPRITE_ANIM_OAMSET_* constants
	table_width 3, SpriteAnimOAMData
 	; vtile offset, data pointer
 	spriteanimoam $00, .OAMData_RedWalk                  ; SPRITE_ANIM_OAMSET_RED_WALK_1
 	spriteanimoam $04, .OAMData_RedWalk                  ; SPRITE_ANIM_OAMSET_RED_WALK_2
 	...
 	spriteanimoam $00, .OAMData_PartyMonWithMail1        ; SPRITE_ANIM_OAMSET_PARTY_MON_WITH_MAIL_1
 	spriteanimoam $00, .OAMData_PartyMonWithMail2        ; SPRITE_ANIM_OAMSET_PARTY_MON_WITH_MAIL_2
 	spriteanimoam $00, .OAMData_PartyMonWithItem1        ; SPRITE_ANIM_OAMSET_PARTY_MON_WITH_ITEM_1
 	spriteanimoam $00, .OAMData_PartyMonWithItem2        ; SPRITE_ANIM_OAMSET_PARTY_MON_WITH_ITEM_2
 	...
 	spriteanimoam $04, .OAMData_GameFreakLogo4_11        ; SPRITE_ANIM_OAMSET_GAMEFREAK_LOGO_10
 	spriteanimoam $00, .OAMData_GameFreakLogo4_11        ; SPRITE_ANIM_OAMSET_GAMEFREAK_LOGO_11
+	spriteanimoam $00, .OAMData_PartyMon                 ; SPRITE_ANIM_OAMSET_PARTY_MON_1
+	spriteanimoam $04, .OAMData_PartyMon                 ; SPRITE_ANIM_OAMSET_PARTY_MON_2
	assert_table_length NUM_SPRITE_ANIM_OAMSETS

 ...

 .OAMData_PartyMonWithMail1:
 	db 4
-	dbsprite -1, -1,  0,  0, $00, PAL_OW_RED
-	dbsprite  0, -1,  0,  0, $01, PAL_OW_RED
-	dbsprite -1,  0,  0,  0, $08, PAL_OW_RED
-	dbsprite  0,  0,  0,  0, $03, PAL_OW_RED
+	dbsprite -1, -1,  0,  0, $00, -1
+	dbsprite  0, -1,  0,  0, $01, -1
+	dbsprite -1,  0,  0,  0, $08, PAL_ICON_RED
+	dbsprite  0,  0,  0,  0, $03, -1

 .OAMData_PartyMonWithMail2:
 	db 4
-	dbsprite -1, -1,  0,  0, $04, PAL_OW_RED
-	dbsprite  0, -1,  0,  0, $05, PAL_OW_RED
-	dbsprite -1,  0,  0,  0, $08, PAL_OW_RED
-	dbsprite  0,  0,  0,  0, $07, PAL_OW_RED
+	dbsprite -1, -1,  0,  0, $04, -1
+	dbsprite  0, -1,  0,  0, $05, -1
+	dbsprite -1,  0,  0,  0, $08, PAL_ICON_RED
+	dbsprite  0,  0,  0,  0, $07, -1

 .OAMData_PartyMonWithItem1:
 	db 4
-	dbsprite -1, -1,  0,  0, $00, PAL_OW_RED
-	dbsprite  0, -1,  0,  0, $01, PAL_OW_RED
-	dbsprite -1,  0,  0,  0, $09, PAL_OW_RED
-	dbsprite  0,  0,  0,  0, $03, PAL_OW_RED
+	dbsprite -1, -1,  0,  0, $00, -1
+	dbsprite  0, -1,  0,  0, $01, -1
+	dbsprite -1,  0,  0,  0, $09, PAL_ICON_RED
+	dbsprite  0,  0,  0,  0, $03, -1

 .OAMData_PartyMonWithItem2:
 	db 4
-	dbsprite -1, -1,  0,  0, $04, PAL_OW_RED
-	dbsprite  0, -1,  0,  0, $05, PAL_OW_RED
-	dbsprite -1,  0,  0,  0, $09, PAL_OW_RED
-	dbsprite  0,  0,  0,  0, $07, PAL_OW_RED
+	dbsprite -1, -1,  0,  0, $04, -1
+	dbsprite  0, -1,  0,  0, $05, -1
+	dbsprite -1,  0,  0,  0, $09, PAL_ICON_RED
+	dbsprite  0,  0,  0,  0, $07, -1
+
+.OAMData_PartyMon:
+	db 4
+	dbsprite -1, -1,  0,  0, $00, -1
+	dbsprite  0, -1,  0,  0, $01, -1
+	dbsprite -1,  0,  0,  0, $02, -1
+	dbsprite  0,  0,  0,  0, $03, -1

Now Pokémon icon sprites will use whatever palette index was already loaded, except for their held item or Mail graphics, which will use PAL_ICON_RED. Of course, items and Mail could use different colors instead, but that's also out of the scope of the tutorial.

5. Load the correct icon palettes for species and shininess

Edit engine/gfx/mon_icons.asm again:

 LoadOverworldMonIcon:
 	ld a, e
 	call ReadMonMenuIcon
 	ld l, a
 	ld h, 0
 	add hl, hl
 	ld de, IconPointers
 	add hl, de
 	ld a, [hli]
 	ld e, a
 	ld d, [hl]
 	ld b, BANK(Icons)
 	ld c, 8
 	ret
+
+SetMenuMonIconColor:
+	push hl
+	push de
+	push bc
+	push af
+
+	ld a, [wTempIconSpecies]
+	ld [wCurPartySpecies], a
+	call GetMenuMonIconPalette
+	ld hl, wShadowOAMSprite00Attributes
+	jr _ApplyMenuMonIconColor
+
+SetMenuMonIconColor_NoShiny:
+	push hl
+	push de
+	push bc
+	push af
+
+	ld a, [wTempIconSpecies]
+	ld [wCurPartySpecies], a
+	and a
+	call GetMenuMonIconPalette_PredeterminedShininess
+	ld hl, wShadowOAMSprite00Attributes
+	jr _ApplyMenuMonIconColor
+
+LoadPartyMenuMonIconColors:
+	push hl
+	push de
+	push bc
+	push af
+
+	ld a, [wPartyCount]
+	sub c
+	ld [wCurPartyMon], a
+	ld e, a
+	ld d, 0
+
+	ld hl, wPartyMon1Item
+	call GetPartyLocation
+	ld a, [hl]
+	ld [wCurIconMonHasItemOrMail], a
+
+	ld hl, wPartySpecies
+	add hl, de
+	ld a, [hl]
+	ld [wCurPartySpecies], a
+	ld a, MON_DVS
+	call GetPartyParamLocation
+	call GetMenuMonIconPalette
+	ld hl, wShadowOAMSprite00Attributes
+	push af
+	ld a, [wCurPartyMon]
+	swap a
+	ld d, 0
+	ld e, a
+	add hl, de
+	pop af
+
+	ld de, 4
+	ld [hl], a ; top left
+	add hl, de
+	ld [hl], a ; top right
+	add hl, de
+	push hl
+	add hl, de
+	ld [hl], a ; bottom right
+	pop hl
+	ld d, a
+	ld a, [wCurIconMonHasItemOrMail]
+	and a
+	ld a, PAL_OW_RED ; item or mail color
+	jr nz, .ok
+	ld a, d
+.ok
+	ld [hl], a ; bottom left
+	jr _FinishMenuMonIconColor
+
+_ApplyMenuMonIconColor:
+	ld c, 4
+	ld de, 4
+.loop
+	ld [hl], a
+	add hl, de
+	dec c
+	jr nz, .loop
+	; fallthrough
+_FinishMenuMonIconColor:
+	pop af
+	pop bc
+	pop de
+	pop hl
+	ret
+
+GetMenuMonIconPalette::
+	ld c, l
+	ld b, h
+	farcall CheckShininess
+GetMenuMonIconPalette_PredeterminedShininess:
+	push af
+	ld a, [wCurPartySpecies]
+	dec a
+	ld c, a
+	ld b, 0
+	ld hl, MonMenuIconPals
+	add hl, bc
+	ld e, [hl]
+	pop af
+	ld a, e
+	jr c, .shiny
+	swap a
+.shiny
+	and $f
+	ld e, a
+	ret
 InitPartyMenuIcon:
+	call LoadPartyMenuMonIconColors
 	ld a, [wCurIconTile]
 	push af
 	ldh a, [hObjectStructIndex]
 	ld hl, wPartySpecies
 	ld e, a
 	ld d, 0
 	add hl, de
 	ld a, [hl]
 	call ReadMonMenuIcon
 	ld [wCurIcon], a
 	call GetMemIconGFX
 	...
 NamingScreen_InitAnimatedMonIcon:
+	ld hl, wTempMonDVs
+	call SetMenuMonIconColor
 	ld a, [wTempIconSpecies]
 	call ReadMonMenuIcon
 	ld [wCurIcon], a
 	xor a
 	call GetIconGFX
 	depixel 4, 4, 4, 0
 	ld a, SPRITE_ANIM_INDEX_PARTY_MON
 	call _InitSpriteAnimStruct
 	ld hl, SPRITEANIMSTRUCT_ANIM_SEQ_ID
 	add hl, bc
 	ld [hl], SPRITE_ANIM_SEQ_NULL
 	ret

 MoveList_InitAnimatedMonIcon:
+	ld a, MON_DVS
+	call GetPartyParamLocation
+	call SetMenuMonIconColor
 	ld a, [wTempIconSpecies]
 	call ReadMonMenuIcon
 	ld [wCurIcon], a
 	xor a
 	call GetIconGFX
 	ld d, 3 * 8 + 2 ; depixel 3, 4, 2, 4
 	ld e, 4 * 8 + 4
 	ld a, SPRITE_ANIM_INDEX_PARTY_MON
 	call _InitSpriteAnimStruct
 	ld hl, SPRITEANIMSTRUCT_ANIM_SEQ_ID
 	add hl, bc
 	ld [hl], SPRITE_ANIM_SEQ_NULL
 	ret

 Trade_LoadMonIconGFX:
 	ld a, [wTempIconSpecies]
 	call ReadMonMenuIcon
 	ld [wCurIcon], a
 	ld a, $62
 	ld [wCurIconTile], a
 	call GetMemIconGFX
 	ret

 GetSpeciesIcon:
 ; Load species icon into VRAM at tile a
 	push de
+	ld a, MON_DVS
+	call GetPartyParamLocation
+	call SetMenuMonIconColor
 	ld a, [wTempIconSpecies]
 	call ReadMonMenuIcon
 	ld [wCurIcon], a
 	pop de
 	ld a, e
 	call GetIconGFX
 	ret

 FlyFunction_GetMonIcon:
 	push de
 	ld a, [wTempIconSpecies]
 	call ReadMonMenuIcon
 	ld [wCurIcon], a
 	pop de
 	ld a, e
 	call GetIcon_a
 	ret

Also we need to edit engine/menus/naming_screen.asm to load the appropiate DV values to wTempMonDVs (which is used by NamingScreen_InitAnimatedMonIcon in the previous file we edited), and this depends whether the Pokémon we receive/catch ends up in the player's party or in a box. For this, let's to go NamingScreen:

 NamingScreen:
	...

 .Pokemon:
	ld a, [wCurPartySpecies]
	ld [wTempIconSpecies], a

+	; Is it a PartyMon or a BoxMon?
+	ld a, [wMonType]
+	and a
+	jr z, .party_mon
+
+	ld hl, sBoxMon1DVs
+	ld a, BANK(sBox)
+	call OpenSRAM
+	jr .start
+
+.party_mon
+	ld a, MON_DVS
+	call GetPartyParamLocation
+.start
+	ld de, wTempMonDVs
+	ld a, [hli]
+	ld [de], a
+	inc de
+	ld a, [hl]
+	ld [de], a
+	ld a, [wMonType]
+	cp BOXMON
+	call z, CloseSRAM
 	ld hl, LoadMenuMonIcon
 	ld a, BANK(LoadMenuMonIcon)
 	ld e, MONICON_NAMINGSCREEN
 	rst FarCall
	...

And finally edit ram/wram.asm:

 wHandlePlayerStep:: db

-	ds 1
+wCurIconMonHasItemOrMail:: db

 wPartyMenuActionText:: db

That code will load the correct icon colors on certain screens: the party menu, nickname screen, move list, and Fly map. Notably, the trade animation and overworld Fly animation do not load the new palettes. That's because those screens use some of the sprite palettes for their own elements (the trade tube, and other overworld sprites), so not all the possible species would have their correct colors available. There are different possible ways to overcome this, such as redesigning the trade and Fly animations, or converting some icon palettes on those screens, but that's left as an exercise for the reader. ;)

6. Make the overworld Fly animation use the right palette

So far, the overworld fly animation still uses the OBJ 0 palette (the red one). An easy way to fix it is to override the colors of this palette. We will make the palette change happen at the same time as the Pokémon sprite is loaded, which happens in FlyFunction_GetMonIcon in engine/gfx/mon_icons.asm

 FlyFunction_GetMonIcon:
 	push de
 	ld a, [wTempIconSpecies]
 	call ReadMonMenuIcon
 	ld [wCurIcon], a
 	pop de
 	ld a, e
 	call GetIcon_a
 
+	; Edit the OBJ 0 palette so that the flying Pokémon has the right colors.
+	ld a, [wTempIconSpecies]
+	ld [wCurPartySpecies], a
+	ld a, MON_DVS
+	call GetPartyParamLocation
+	call GetMenuMonIconPalette
+	add a
+	add a
+	add a
+	ld e, a
+	farcall SetFirstOBJPalette
	ret

We're calling SetFirstOBJPalette but it doesn't exist yet, so let's create and put it at the end of engine/gfx/color.asm:

 FemalePokegearPals:
 INCLUDE "gfx/pokegear/pokegear_f.pal"
+
+; Input: E must contain the offset of the selected palette from PartyMenuOBPals.
+SetFirstOBJPalette::
+	ld hl, PartyMenuOBPals
+	ld d, 0
+	add hl, de
+ 	ld de, wOBPals1
+	ld bc, 1 palettes
+	ld a, BANK(wOBPals1)
+	call FarCopyWRAM
+	ld a, TRUE
+ 	ldh [hCGBPalUpdate], a
+ 	jp ApplyPals

Now we need to reset the OBJ 0 palette to its red color after the player landed. To do this, edit engine/events/overworld.asm

 .ReturnFromFly:
+	ld e, PAL_OW_RED
+	farcall SetFirstOBJPalette
	farcall RespawnPlayer

Please note that this method is not compatible with "Make overworld sprites darker at night".

7. Day care Pokémons use their custom palette

Warning: Before proceeding, double check that you added ld e, a at the end of GetMenuMonIconPalette from Chapter 5 of this tutorial.

The Day Care is on Route 34. On this Route, we can make use of 2 OBJ palettes that are unused in vanilla: PAL_OBJ_PINK and PAL_OBJ_ROCK.

We will make each Pokémon at the day care use one of those palettes.

Edit maps/Route34.asm

-	object_event 14, 18, SPRITE_DAY_CARE_MON_1, SPRITEMOVEDATA_POKEMON, 2, 2, -1, -1, 0, OBJECTTYPE_SCRIPT, 0, DayCareMon1Script, EVENT_DAY_CARE_MON_1
-	object_event 17, 19, SPRITE_DAY_CARE_MON_2, SPRITEMOVEDATA_POKEMON, 2, 2, -1, -1, 0, OBJECTTYPE_SCRIPT, 0, DayCareMon2Script, EVENT_DAY_CARE_MON_2
+	object_event 14, 18, SPRITE_DAY_CARE_MON_1, SPRITEMOVEDATA_POKEMON, 2, 2, -1, -1, PAL_NPC_PINK, OBJECTTYPE_SCRIPT, 0, DayCareMon1Script, EVENT_DAY_CARE_MON_1
+	object_event 17, 19, SPRITE_DAY_CARE_MON_2, SPRITEMOVEDATA_POKEMON, 2, 2, -1, -1, PAL_NPC_ROCK, OBJECTTYPE_SCRIPT, 0, DayCareMon2Script, EVENT_DAY_CARE_MON_2

Now we need to change the colors of those palettes whenever we enter Route 34.

Add the following code at the end of LoadMapPals in engine/gfx/color.asm

.morn_day
	ld de, wBGPals1 palette PAL_BG_ROOF color 1
	ld bc, 4
	ld a, BANK(wBGPals1)
	call FarCopyWRAM
+
+	; Day Care outdoor palettes
+	ld a, [wMapGroup]
+	cp GROUP_ROUTE_34
+	ret nz
+
+	ld a, [wMapNumber]
+	cp MAP_ROUTE_34
+	ret nz
+
+	ld a, BANK(wBreedMon1Species)
+	ld hl, wBreedMon1Species
+	call GetFarWRAMByte
+	and a
+	jr z, .day_care_mon_2
+	ld [wCurPartySpecies], a
+
+	ld hl, wBreedMon1DVs ; HL now points to the params of the wBreedMon1, which is needed by GetMenuMonIconPalette to determine if it's shiny.
+	ld de, GetMenuMonIconPalette
+	ld a, BANK(GetMenuMonIconPalette)
+	call FarCall_de
+	ld a, e
+	add a
+	add a
+	add a
+	ld e, a
+	ld d, 0
+	ld hl, PartyMenuOBPals
+	add hl, de
+
+	inc hl
+	inc hl
+
+	ld de, wOBPals1 palette PAL_OW_PINK + 2
+	ld bc, 1 palettes - 2
+	ld a, BANK(wOBPals1)
+	call FarCopyWRAM
+
+.day_care_mon_2
+	ld a, BANK(wBreedMon2Species)
+	ld hl, wBreedMon2Species
+	call GetFarWRAMByte
+	and a
+	ret z
+	ld [wCurPartySpecies], a
+
+	ld hl, wBreedMon2DVs ; HL now points to the params of the wBreedMon2, which is needed by GetMenuMonIconPalette to determine if it's shiny.
+	ld de, GetMenuMonIconPalette
+	ld a, BANK(GetMenuMonIconPalette)
+	call FarCall_de
+	ld a, e
+	add a
+	add a
+	add a
+	ld e, a
+	ld d, 0
+	ld hl, PartyMenuOBPals
+	add hl, de
+
+	inc hl
+	inc hl
+
+	ld de, wOBPals1 palette PAL_OW_ROCK + 2
+	ld bc, 1 palettes - 2
+	ld a, BANK(wOBPals1)
+	call FarCopyWRAM
	ret

8. Make the Move Deleter's delete move menu display shiny palettes

The Move Deleter's delete move menu has a small problem that may go unnoticed. It will load the correct palette for all normal Pokémon, but will not show some shiny palettes (it may even show a palette not even defined in this tutorial). Thankfully, this is an easy fix.

Edit engine/pokemon/mon_menu.asm:

ChooseMoveToDelete:
	ld hl, wOptions
	ld a, [hl]
	push af
	set NO_TEXT_SCROLL, [hl]
-	call LoadFontsBattleExtra
+	farcall LoadPartyMenuGFX
	call .ChooseMoveToDelete
	pop bc
	ld a, b
	ld [wOptions], a
	push af
	call ClearBGPalettes
	pop af
	ret

9. Fix a bank overflow error

We're done implementing the new palettes, but probably we'll have this error when building the ROM:

 ERROR: main.asm(324) -> engine/gfx/mon_icons.asm(556) -> gfx/icons.asm(40):
    Section 'bank23' is too big (max size = 0x4000 bytes, reached 0x403F).

All that code and data we added has overflowed section "bank23", which layout.link tells us is in ROM bank $23. (Okay, duh, but not every section is named after its bank.) We need to move some content to a different bank.

It turns out that engine/gfx/mon_icons.asm INCLUDEs gfx/icons.asm, which is just all the icon graphics. They're loaded by bank anyway, so they can freely be placed in any bank without causing errors.

Edit gfx/icons.asm:

+SECTION "Mon Icons", ROMX
+
 Icons: ; used only for BANK(Icons)
 
 NullIcon:
 PoliwagIcon:      INCBIN "gfx/icons/poliwag.2bpp"
 JigglypuffIcon:   INCBIN "gfx/icons/jigglypuff.2bpp"
 DiglettIcon:      INCBIN "gfx/icons/diglett.2bpp"
...

Now it builds correctly!

party-menu-icon-colors

This is totally compatible with the tutorial to add a unique icon for each Pokémon; in that tutorial, we just split gfx/icons.asm into more than one section, and change how the graphics' bank is determined.

Clone this wiki locally