Edit the male and female player colors - pret/pokecrystal GitHub Wiki

The male player, Chris, is red, and the female player, Kris, is blue. This affects color palettes in a number of different places, including the introductory naming screen, the trainer card, the overworld sprite, the Town Map sprite, and the Magnet Train cutscene. It can be tricky to find and edit all of these places.

As an example, we'll be making Chris gold (with a brown overworld sprite) and Kris teal (with a green overworld sprite).

Note: For older versions of the pokecrystal disassembly, please check the file history of this tutorial for changes as many changes have occurred since the creation of this tutorial.

Contents

  1. Edit the overworld sprite colors
  2. Edit the Pokégear colors
  3. Define sprite animation constants
  4. Edit the sprite animation data
  5. Use the edited sprite animation data
  6. Sync the Pokégear and overworld palettes
  7. Edit the trainer constants
  8. Create trainer sprite palettes
  9. Give the catch tutorial Dude his own palette
  10. Edit the trainer card colors

1. Edit the overworld sprite colors

Let's start with the overworld sprites. They don't need any custom colors, since you already have a small set of colors to choose from: red, blue, green, brown, or pink. (Although nothing actually uses pink, and you can change it to something else by editing gfx/overworld/npc_sprites.pal. I favor purple, since so many default NPCs could have used it anyway: Eusine, Will, Koga, Janine, the Psychics, the Pokémaniacs...)

Edit data/sprites/sprites.asm:

 OverworldSprites:
 ; entries correspond to SPRITE_* constants
	table_width NUM_SPRITEDATA_FIELDS, OverworldSprites
-	overworld_sprite ChrisSpriteGFX, 12, WALKING_SPRITE, PAL_OW_RED
-	overworld_sprite ChrisBikeSpriteGFX, 12, WALKING_SPRITE, PAL_OW_RED
+	overworld_sprite ChrisSpriteGFX, 12, WALKING_SPRITE, PAL_OW_BROWN
+	overworld_sprite ChrisBikeSpriteGFX, 12, WALKING_SPRITE, PAL_OW_BROWN
 	...
-	overworld_sprite KrisSpriteGFX, 12, WALKING_SPRITE, PAL_OW_BLUE
-	overworld_sprite KrisBikeSpriteGFX, 12, WALKING_SPRITE, PAL_OW_BLUE
+	overworld_sprite KrisSpriteGFX, 12, WALKING_SPRITE, PAL_OW_GREEN
+	overworld_sprite KrisBikeSpriteGFX, 12, WALKING_SPRITE, PAL_OW_GREEN
 	...

This affects the default colors of the sprites for Chris and Kris when some random object_event uses them, but it turns out that the player's object is initialized elsewhere.

Edit engine/overworld/player_object.asm:

 SpawnPlayer:
 	ld a, -1
 	ld [wObjectFollow_Leader], a
 	ld [wObjectFollow_Follower], a
 	ld a, PLAYER
 	ld hl, PlayerObjectTemplate
 	call CopyPlayerObjectTemplate
 	ld b, PLAYER
 	call PlayerSpawn_ConvertCoords
 	ld a, PLAYER_OBJECT
 	call GetMapObject
 	ld hl, MAPOBJECT_COLOR
 	add hl, bc
-	ln e, PAL_NPC_RED, OBJECTTYPE_SCRIPT
+	ln e, PAL_NPC_BROWN, OBJECTTYPE_SCRIPT
 	ld a, [wPlayerSpriteSetupFlags]
 	bit PLAYERSPRITESETUP_FEMALE_TO_MALE_F, a
 	jr nz, .ok
 	ld a, [wPlayerGender]
 	bit PLAYERGENDER_FEMALE_F, a
 	jr z, .ok
-	ln e, PAL_NPC_BLUE, OBJECTTYPE_SCRIPT
+	ln e, PAL_NPC_GREEN, OBJECTTYPE_SCRIPT

 .ok
 	...

There's also a brief moment after Prof. Oak's introductory speech, when your sprite gets shrunk to overworld size and you appear in your room, which has its own color setup.

Edit engine/menus/intro_menu.asm:

-	ld b, PAL_OW_RED
+	ld b, PAL_OW_BROWN
 	ld a, [wPlayerGender]
 	bit PLAYERGENDER_FEMALE_F, a
 	jr z, .male
-	ld b, PAL_OW_BLUE
+	ld b, PAL_OW_GREEN
 .male
 	ld a, b

Of course, before your sprite is shrunk, you see its full-size trainer picture, which will also need color changing; but we'll get to that later.

One more thing needs editing for the overworld colors. When you trade or battle in the Cable Club, a girl player will appear as a boy for compatibility reasons.

Edit maps/Pokecenter2F.asm:

-	setval (PAL_NPC_RED << 4)
+	setval (PAL_NPC_BROWN << 4)
 	special SetPlayerPalette
-	setval (PAL_NPC_BLUE << 4)
+	setval (PAL_NPC_GREEN << 4)
 	special SetPlayerPalette

There are two instances of PAL_NPC_RED and three of PAL_NPC_BLUE, all of which need editing.

2. Edit the Pokégear colors

The Pokégear's card icons, and the cities on the Town Map, change color depending on your gender.

Edit gfx/pokegear/pokegear.pal:

 ; city (boy)
 	RGB 28, 31, 20
-	RGB 31, 15, 00
-	RGB 15, 07, 00
+	RGB 31, 23, 06
+	RGB 23, 16, 00
 	RGB 00, 00, 00

Edit gfx/pokegear/pokegear_f.pal:

 ; city (girl)
 	RGB 28, 31, 20
-	RGB 10, 18, 31
-	RGB 13, 06, 31
+	RGB 08, 25, 24
+	RGB 00, 16, 18
 	RGB 00, 00, 00

I've made them gold and green respectively for the boy and girl, but these colors are independent of any of the sprite colors, so you can freely choose them.

3. Define sprite animation constants

There are various parts of the game which look like they're using standard overworld NPC sprites—the Magnet Train ride animation, the Town Map, the naming screen—but actually they're using custom animations. The sprite animation system is somewhat complicated, with layers of abstract data referring to each other, so let's take it step by step.

Edit constants/sprite_anim_constants.asm:

; SpriteAnimObjects indexes (see data/sprite_anims/objects.asm)
	const_def
	const SPRITE_ANIM_OBJ_PARTY_MON                 ; 00
 	...
	const SPRITE_ANIM_OBJ_RED_WALK                  ; 0a
 	...
-	const SPRITE_ANIM_OBJ_MAGNET_TRAIN_RED          ; 15
+	const SPRITE_ANIM_OBJ_MAGNET_TRAIN_BROWN        ; 15
 	...
-	const SPRITE_ANIM_OBJ_BLUE_WALK                 ; 1e
+	const SPRITE_ANIM_OBJ_GREEN_WALK                ; 1e
-	const SPRITE_ANIM_OBJ_MAGNET_TRAIN_BLUE         ; 1f
+	const SPRITE_ANIM_OBJ_MAGNET_TRAIN_GREEN        ; 1f
 	...
 	const SPRITE_ANIM_OBJ_CELEBI                    ; 2c
+	const SPRITE_ANIM_OBJ_BROWN_WALK
DEF NUM_SPRITE_ANIM_OBJS EQU const_value
; SpriteAnimFrameData indexes (see data/sprite_anims/framesets.asm)
 	const_def
 	const SPRITE_ANIM_FRAMESET_00                       ; 00
 	...
 	const SPRITE_ANIM_FRAMESET_RED_WALK                 ; 11
 	...
-	const SPRITE_ANIM_FRAMESET_MAGNET_TRAIN_RED         ; 1b
+	const SPRITE_ANIM_FRAMESET_MAGNET_TRAIN_BROWN       ; 1b
 	...
-	const SPRITE_ANIM_FRAMESET_BLUE_WALK                ; 2d
+	const SPRITE_ANIM_FRAMESET_GREEN_WALK               ; 2d
-	const SPRITE_ANIM_FRAMESET_MAGNET_TRAIN_BLUE        ; 2e
+	const SPRITE_ANIM_FRAMESET_MAGNET_TRAIN_GREEN       ; 2e
 	...
 	const SPRITE_ANIM_FRAMESET_CELEBI_RIGHT             ; 41
+	const SPRITE_ANIM_FRAMESET_BROWN_WALK
NUM_SPRITE_ANIM_FRAMESETS EQU const_value
 ; 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_MAGNET_TRAIN_RED_1         ; 41
-	const SPRITE_ANIM_OAMSET_MAGNET_TRAIN_RED_2         ; 42
+	const SPRITE_ANIM_OAMSET_MAGNET_TRAIN_BROWN_1       ; 41
+	const SPRITE_ANIM_OAMSET_MAGNET_TRAIN_BROWN_2       ; 42
 	...
-	const SPRITE_ANIM_OAMSET_BLUE_WALK_1                ; 63
-	const SPRITE_ANIM_OAMSET_BLUE_WALK_2                ; 64
+	const SPRITE_ANIM_OAMSET_GREEN_WALK_1               ; 63
+	const SPRITE_ANIM_OAMSET_GREEN_WALK_2               ; 64
-	const SPRITE_ANIM_OAMSET_MAGNET_TRAIN_BLUE_1        ; 65
-	const SPRITE_ANIM_OAMSET_MAGNET_TRAIN_BLUE_2        ; 66
+	const SPRITE_ANIM_OAMSET_MAGNET_TRAIN_GREEN_1       ; 65
+	const SPRITE_ANIM_OAMSET_MAGNET_TRAIN_GREEN_2       ; 66
 	...
 	const SPRITE_ANIM_OAMSET_GAMEFREAK_LOGO_11          ; 8b
+	const SPRITE_ANIM_OAMSET_BROWN_WALK_1
+	const SPRITE_ANIM_OAMSET_BROWN_WALK_2
NUM_SPRITE_ANIM_OAMSETS EQU const_value

As we'll see next, the SpriteAnimObjects entries refer to SpriteAnimFrameData entries, the SpriteAnimFrameData entries refer to SpriteAnimOAMData entries, and finally the SpriteAnimOAMData declare the actual colors that get used.

Notice that we simply rename the BLUE constants to GREEN, but have to add some new ones for BROWN instead of renaming everything RED. This is because the BLUE data was added just for Crystal version, and it's only used for Kris, but the RED data has certain uses besides for Chris.

4. Edit the sprite animation data

Now we'll edit the data files that correspond to those constants, as implied by the comments above each list.

Edit data/sprite_anims/objects.asm:

SpriteAnimObjects:
; entries correspond to SPRITE_ANIM_OBJ_* constants (see constants/sprite_anim_constants.asm)
	table_width 3, SpriteAnimObjects
	; frameset, sequence, tile
; SPRITE_ANIM_OBJ_PARTY_MON
	db SPRITE_ANIM_FRAMESET_PARTY_MON,                 SPRITE_ANIM_FUNC_PARTY_MON,                 SPRITE_ANIM_DICT_DEFAULT
 	...
-; SPRITE_ANIM_OBJ_MAGNET_TRAIN_RED
-	db SPRITE_ANIM_FRAMESET_MAGNET_TRAIN_RED,          SPRITE_ANIM_FUNC_NULL,                      SPRITE_ANIM_DICT_DEFAULT
+; SPRITE_ANIM_OBJ_MAGNET_TRAIN_BROWN
+	db SPRITE_ANIM_FRAMESET_MAGNET_TRAIN_BROWN,        SPRITE_ANIM_FUNC_NULL,                      SPRITE_ANIM_DICT_DEFAULT
 	...
-; SPRITE_ANIM_OBJ_BLUE_WALK
-	db SPRITE_ANIM_FRAMESET_BLUE_WALK,                 SPRITE_ANIM_FUNC_NULL,                      SPRITE_ANIM_DICT_DEFAULT
+; SPRITE_ANIM_OBJ_GREEN_WALK
+	db SPRITE_ANIM_FRAMESET_GREEN_WALK,                SPRITE_ANIM_FUNC_NULL,                      SPRITE_ANIM_DICT_DEFAULT
-; SPRITE_ANIM_OBJ_MAGNET_TRAIN_BLUE
-	db SPRITE_ANIM_FRAMESET_MAGNET_TRAIN_BLUE,         SPRITE_ANIM_FUNC_NULL,                      SPRITE_ANIM_DICT_DEFAULT
+; SPRITE_ANIM_OBJ_MAGNET_TRAIN_GREEN
+	db SPRITE_ANIM_FRAMESET_MAGNET_TRAIN_GREEN,        SPRITE_ANIM_FUNC_NULL,                      SPRITE_ANIM_DICT_DEFAULT
 	...
 ; SPRITE_ANIM_OBJ_CELEBI
	db SPRITE_ANIM_FRAMESET_CELEBI_LEFT,               SPRITE_ANIM_FUNC_NULL,                      SPRITE_ANIM_DICT_DEFAULT
+; SPRITE_ANIM_OBJ_BROWN_WALK
+	db SPRITE_ANIM_FRAMESET_BROWN_WALK,                SPRITE_ANIM_FUNC_NULL,                      SPRITE_ANIM_DICT_DEFAULT
	assert_table_length NUM_SPRITE_ANIM_OBJS

Edit data/sprite_anims/framesets.asm:

 SpriteAnimFrameData:
 ; entries correspond to SPRITE_ANIM_FRAMESET_* constants
	table_width 2, SpriteAnimFrameData
 	dw .Frameset_00
 	...
 	dw .Frameset_RedWalk
 	...
-	dw .Frameset_MagnetTrainRed
+	dw .Frameset_MagnetTrainBrown
 	...
-	dw .Frameset_BlueWalk
-	dw .Frameset_MagnetTrainBlue
+	dw .Frameset_GreenWalk
+	dw .Frameset_MagnetTrainGreen
 	...
 	dw .Frameset_CelebiRight
+	dw .Frameset_BrownWalk
	assert_table_length NUM_SPRITE_ANIM_FRAMESETS
-.Frameset_BlueWalk:
-	oamframe SPRITE_ANIM_OAMSET_BLUE_WALK_1,  8
-	oamframe SPRITE_ANIM_OAMSET_BLUE_WALK_2,  8
-	oamframe SPRITE_ANIM_OAMSET_BLUE_WALK_1,  8
-	oamframe SPRITE_ANIM_OAMSET_BLUE_WALK_2,  8, OAM_X_FLIP
+.Frameset_GreenWalk:
+	oamframe SPRITE_ANIM_OAMSET_GREEN_WALK_1,  8
+	oamframe SPRITE_ANIM_OAMSET_GREEN_WALK_2,  8
+	oamframe SPRITE_ANIM_OAMSET_GREEN_WALK_1,  8
+	oamframe SPRITE_ANIM_OAMSET_GREEN_WALK_2,  8, OAM_X_FLIP
 	oamrestart

-.Frameset_MagnetTrainBlue:
-	oamframe SPRITE_ANIM_OAMSET_MAGNET_TRAIN_BLUE_1,  8
-	oamframe SPRITE_ANIM_OAMSET_MAGNET_TRAIN_BLUE_2,  8
-	oamframe SPRITE_ANIM_OAMSET_MAGNET_TRAIN_BLUE_1,  8
-	oamframe SPRITE_ANIM_OAMSET_MAGNET_TRAIN_BLUE_2,  8, OAM_X_FLIP
+.Frameset_MagnetTrainGreen:
+	oamframe  SPRITE_ANIM_OAMSET_MAGNET_TRAIN_GREEN_1,  8
+	oamframe SPRITE_ANIM_OAMSET_MAGNET_TRAIN_GREEN_2,  8
+	oamframe SPRITE_ANIM_OAMSET_MAGNET_TRAIN_GREEN_1,  8
+	oamframe SPRITE_ANIM_OAMSET_MAGNET_TRAIN_GREEN_2,  8, OAM_X_FLIP
 	oamrestart
-.Frameset_MagnetTrainRed:
-	oamframe SPRITE_ANIM_OAMSET_MAGNET_TRAIN_RED_1,  8
-	oamframe SPRITE_ANIM_OAMSET_MAGNET_TRAIN_RED_2,  8
-	oamframe SPRITE_ANIM_OAMSET_MAGNET_TRAIN_RED_1,  8
-	oamframe SPRITE_ANIM_OAMSET_MAGNET_TRAIN_RED_2,  8, OAM_X_FLIP
+.Frameset_MagnetTrainBrown:
+	oamframe SPRITE_ANIM_OAMSET_MAGNET_TRAIN_BROWN_1,  8
+	oamframe SPRITE_ANIM_OAMSET_MAGNET_TRAIN_BROWN_2,  8
+	oamframe SPRITE_ANIM_OAMSET_MAGNET_TRAIN_BROWN_1,  8
+	oamframe SPRITE_ANIM_OAMSET_MAGNET_TRAIN_BROWN_2,  8, OAM_X_FLIP
 	oamrestart
+.Frameset_BrownWalk:
+	oamframe SPRITE_ANIM_OAMSET_BROWN_WALK_1,  8
+	oamframe SPRITE_ANIM_OAMSET_BROWN_WALK_2,  8
+	oamframe SPRITE_ANIM_OAMSET_BROWN_WALK_1,  8
+	oamframe SPRITE_ANIM_OAMSET_BROWN_WALK_2,  8, OAM_X_FLIP
+	oamrestart

And edit data/sprite_anims/oam.asm:

MACRO spriteanimoam
; vtile offset, data pointer
	db \1
	dw \2
ENDM

SpriteAnimOAMData:
; entries correspond to SPRITE_ANIM_OAMSET_* constants (see constants/sprite_anim_constants.asm)
	table_width 3, SpriteAnimOAMData
 	spriteanimoam $00, .OAMData_RedWalk                  ; SPRITE_ANIM_OAMSET_RED_WALK_1
 	spriteanimoam $04, .OAMData_RedWalk                  ; SPRITE_ANIM_OAMSET_RED_WALK_2
 	...
-	spriteanimoam $00, .OAMData_MagnetTrainRed           ; SPRITE_ANIM_OAMSET_MAGNET_TRAIN_RED_1
-	spriteanimoam $04, .OAMData_MagnetTrainRed           ; SPRITE_ANIM_OAMSET_MAGNET_TRAIN_RED_2
+	spriteanimoam $00, .OAMData_MagnetTrainBrown         ; SPRITE_ANIM_OAMSET_MAGNET_TRAIN_BROWN_1
+	spriteanimoam $04, .OAMData_MagnetTrainBrown         ; SPRITE_ANIM_OAMSET_MAGNET_TRAIN_BROWN_2
 	...
-	spriteanimoam $00, .OAMData_BlueWalk                 ; SPRITE_ANIM_OAMSET_BLUE_WALK_1
-	spriteanimoam $04, .OAMData_BlueWalk                 ; SPRITE_ANIM_OAMSET_BLUE_WALK_2
+	spriteanimoam $00, .OAMData_GreenWalk                ; SPRITE_ANIM_OAMSET_GREEN_WALK_1
+	spriteanimoam $04, .OAMData_GreenWalk                ; SPRITE_ANIM_OAMSET_GREEN_WALK_2
-	spriteanimoam $00, .OAMData_MagnetTrainBlue          ; SPRITE_ANIM_OAMSET_MAGNET_TRAIN_BLUE_1
-	spriteanimoam  $04, .OAMData_MagnetTrainBlue          ; SPRITE_ANIM_OAMSET_MAGNET_TRAIN_BLUE_2
+	spriteanimoam $00, .OAMData_MagnetTrainGreen         ; SPRITE_ANIM_OAMSET_MAGNET_TRAIN_GREEN_1
+	spriteanimoam $04, .OAMData_MagnetTrainGreen         ; SPRITE_ANIM_OAMSET_MAGNET_TRAIN_GREEN_2
 	...
 	spriteanimoam $00, .OAMData_GameFreakLogo4_11        ; SPRITE_ANIM_OAMSET_GAMEFREAK_LOGO_11
+	spriteanimoam $00, .OAMData_BrownWalk                ; SPRITE_ANIM_OAMSET_BROWN_WALK_1
+	spriteanimoam $04, .OAMData_BrownWalk                ; SPRITE_ANIM_OAMSET_BROWN_WALK_2
	assert_table_length NUM_SPRITE_ANIM_OAMSETS
-.OAMData_BlueWalk:
+.OAMData_GreenWalk:
 	db 4
-	dbsprite -1, -1,  0,  0, $00, PAL_OW_BLUE
-	dbsprite  0, -1,  0,  0, $01, PAL_OW_BLUE
-	dbsprite -1,  0,  0,  0, $02, PAL_OW_BLUE
-	dbsprite  0,  0,  0,  0, $03, PAL_OW_BLUE
+	dbsprite -1, -1,  0,  0, $00, PAL_OW_GREEN
+	dbsprite  0, -1,  0,  0, $01, PAL_OW_GREEN
+	dbsprite -1,  0,  0,  0, $02, PAL_OW_GREEN
+	dbsprite  0,  0,  0,  0, $03, PAL_OW_GREEN

-.OAMData_MagnetTrainBlue:
+.OAMData_MagnetTrainGreen:
 	db 4
-	dbsprite -1, -1,  0,  0, $00, PAL_OW_BLUE | PRIORITY
-	dbsprite  0, -1,  0,  0, $01, PAL_OW_BLUE | PRIORITY
-	dbsprite -1,  0,  0,  0, $02, PAL_OW_BLUE | PRIORITY
-	dbsprite  0,  0,  0,  0, $03, PAL_OW_BLUE | PRIORITY
+	dbsprite -1, -1,  0,  0, $00, PAL_OW_GREEN | PRIORITY
+	dbsprite  0, -1,  0,  0, $01, PAL_OW_GREEN | PRIORITY
+	dbsprite -1,  0,  0,  0, $02, PAL_OW_GREEN | PRIORITY
+	dbsprite  0,  0,  0,  0, $03, PAL_OW_GREEN | PRIORITY
+.OAMData_BrownWalk:
+	db 4
+	dbsprite -1, -1,  0,  0, $00, PAL_OW_BROWN
+	dbsprite  0, -1,  0,  0, $01, PAL_OW_BROWN
+	dbsprite -1,  0,  0,  0, $02, PAL_OW_BROWN
+	dbsprite  0,  0,  0,  0, $03, PAL_OW_BROWN
+
+.OAMData_MagnetTrainBrown:
+	db 4
+	dbsprite -1, -1,  0,  0, $00, PAL_OW_BROWN | PRIORITY
+	dbsprite  0, -1,  0,  0, $01, PAL_OW_BROWN | PRIORITY
+	dbsprite -1,  0,  0,  0, $02, PAL_OW_BROWN | PRIORITY
+	dbsprite  0,  0,  0,  0, $03, PAL_OW_BROWN | PRIORITY

Most of those changes were just renaming labels to avoid confusion. The only part that has a real effect on the game is the final part where the colors change.

5. Use the edited sprite animation data

This will mostly be changing RED to BROWN and BLUE to GREEN, now that we have the appropriate data definitions for it.

Edit engine/events/magnet_train.asm:

 .InitPlayerSpriteAnim:
 	ld d, (8 + 2) * TILE_WIDTH + 5
 	ld a, [wMagnetTrainPlayerSpriteInitX]
 	ld e, a
-	ld b, SPRITE_ANIM_OBJ_MAGNET_TRAIN_RED
+	ld b, SPRITE_ANIM_OBJ_MAGNET_TRAIN_BROWN
 	ldh a, [rSVBK]
 	push af
 	ld a, BANK(wPlayerGender)
 	ldh [rSVBK], a
 	ld a, [wPlayerGender]
 	bit PLAYERGENDER_FEMALE_F, a
 	jr z, .got_gender
-	ld b, SPRITE_ANIM_OBJ_MAGNET_TRAIN_BLUE
+	ld b, SPRITE_ANIM_OBJ_MAGNET_TRAIN_GREEN

Edit engine/pokegear/pokegear.asm:

 PokegearMap_InitPlayerIcon:
 	push af
 	depixel 0, 0
-	ld b, SPRITE_ANIM_OBJ_RED_WALK
+	ld b, SPRITE_ANIM_OBJ_BROWN_WALK
 	ld a, [wPlayerGender]
 	bit PLAYERGENDER_FEMALE_F, a
 	jr z, .got_gender
-	ld b, SPRITE_ANIM_OBJ_BLUE_WALK
+	ld b, SPRITE_ANIM_OBJ_GREEN_WALK
 .got_gender
 	...
 Pokedex_GetArea:
 	...
 .ShowPlayerLoop:
 	...
 	push bc
-	ld c, PAL_OW_RED
+	ld c, PAL_OW_BROWN
 	ld a, [wPlayerGender]
 	bit PLAYERGENDER_FEMALE_F, a
 	jr z, .male
-	inc c ; PAL_OW_BLUE
+	ld c, PAL_OW_GREEN
 .male
 	ld a, c
 	ld [hli], a ; attributes
 	pop bc
 	jr .ShowPlayerLoop
 TownMapPlayerIcon:
 ; Draw the player icon at town map location in a
 	...
 ; Animation/palette
 	depixel 0, 0
-	ld b, SPRITE_ANIM_OBJ_RED_WALK ; Male
+	ld b, SPRITE_ANIM_OBJ_BROWN_WALK ; Male
 	ld a, [wPlayerGender]
 	bit PLAYERGENDER_FEMALE_F, a
 	jr z, .got_gender
-	ld b, SPRITE_ANIM_OBJ_BLUE_WALK ; Female
+	ld b, SPRITE_ANIM_OBJ_GREEN_WALK ; Female
 .got_gender
 	...

Edit engine/menus/naming_screen.asm:

 .LoadSprite:
 	...
 	ld b, SPRITE_ANIM_OBJ_RED_WALK
+	ld a, d
+	cp HIGH(ChrisSpriteGFX)
+	jr nz, .not_chris
+	ld a, e
+	cp LOW(ChrisSpriteGFX)
+	jr nz, .not_chris
+	ld b, SPRITE_ANIM_OBJ_BROWN_WALK
+	jr .not_kris
+.not_chris
 	ld a, d
 	cp HIGH(KrisSpriteGFX)
 	jr nz, .not_kris
 	ld a, e
 	cp LOW(KrisSpriteGFX)
 	jr nz, .not_kris
-	ld b, SPRITE_ANIM_OBJ_BLUE_WALK
+	ld b, SPRITE_ANIM_OBJ_GREEN_WALK
 .not_kris
 	ld a, b
 	depixel 4, 4, 4, 0
 	call InitSpriteAnimStruct
 	ret

Notice how the naming screen does not simply change SPRITE_ANIM_OBJ_RED_WALK to SPRITE_ANIM_OBJ_BROWN_WALK; it only does so if the current sprite is using ChrisSpriteGFX. That's because the default SPRITE_ANIM_OBJ_RED_WALK also gets used for naming other things, including your rival, your Pokémon, and Bill's PC boxes.

6. Sync the Pokégear and overworld palettes

The previous step should make the Town Map use the correct colors, but there's actually one more thing to do for it. The colors available within the party menu and Pokégear aren't the same as the ones for the overworld. By default, they didn't have to be; all the party Pokémon icons are red, and the Town Map icons can only be red or blue. But it's more convenient to have all the overworld colors available, and it won't break anything to do so.

Edit gfx/stats/party_menu_ob.pal:

 	RGB 27, 31, 27
 	RGB 31, 19, 10
-	RGB 31, 07, 04
+	RGB 31, 07, 01
 	RGB 00, 00, 00

 	RGB 27, 31, 27
 	RGB 31, 19, 10
-	RGB 10, 14, 20
+	RGB 10, 09, 31
 	RGB 00, 00, 00

 	RGB 27, 31, 27
 	RGB 31, 19, 10
-	RGB 31, 07, 04
+	RGB 07, 23, 03
 	RGB 00, 00, 00

 	RGB 27, 31, 27
 	RGB 31, 19, 10
-	RGB 31, 07, 04
+	RGB 15, 10, 03
 	RGB 00, 00, 00

 	RGB 27, 31, 27
 	RGB 31, 19, 10
-	RGB 31, 07, 04
+	RGB 30, 10, 06
 	RGB 00, 00, 00

 	...

These are the same red, blue, green, brown, and pink colors as in gfx/overworld/npc_sprites.pal. So if you edit anything in one file, make the same edit in the other.

7. Edit the trainer constants

We're finally getting to edit the "trainer sprite" colors. Of course, the players Chris and Kris don't really have trainer data defined, but they use the same color table as trainers for their intro sprites, Trainer Cards, and battle back sprites.

Edit constants/trainer_constants.asm:

 ; trainer class ids
 ; `trainerclass` indexes are for:
 ; - TrainerClassNames (see data/trainers/class_names.asm)
 ; - TrainerClassAttributes (see data/trainers/attributes.asm)
 ; - TrainerClassDVs (see data/trainers/dvs.asm)
 ; - TrainerGroups (see data/trainers/party_pointers.asm)
 ; - TrainerEncounterMusic (see data/trainers/encounter_music.asm)
 ; - TrainerPicPointers (see data/trainers/pic_pointers.asm)
 ; - TrainerPalettes (see data/trainers/palettes.asm)
 ; - BTTrainerClassSprites (see data/trainers/sprites.asm)
 ; - BTTrainerClassGenders (see data/trainers/genders.asm)
 ; trainer constants are Trainers indexes, for the sub-tables of TrainerGroups (see data/trainers/parties.asm)
 CHRIS EQU __trainer_class__
 	trainerclass TRAINER_NONE ; 0
 	const PHONECONTACT_MOM
 	const PHONECONTACT_BIKESHOP
 	const PHONECONTACT_BILL
 	const PHONECONTACT_ELM
 	const PHONECONTACT_BUENA
NUM_NONTRAINER_PHONECONTACTS EQU const_value - 1

-DEF KRIS EQU __trainer_class__
 	trainerclass FALKNER ; 1
 	const FALKNER1

 	...

 	trainerclass MYSTICALMAN ; 43
 	const EUSINE

+DEF KRIS EQU __trainer_class__
 NUM_TRAINER_CLASSES EQU __trainer_class__ - 1

CHRIS corresponds to the unusable TRAINER_NONE, which is fine. But KRIS was actually just an alias for FALKNER—they use the exact same sprite color. Here we've moved KRIS to be after the very last trainer class. Just like CHRIS, she won't need any trainer data except a color palette, which we'll do next.

8. Create trainer sprite palettes

Create gfx/player/chris.pal:

+	RGB 30, 21, 14
+	RGB 22, 15, 00

And create gfx/player/kris.pal:

+	RGB 30, 22, 17
+	RGB 00, 18, 15

Then edit data/trainers/palettes.asm:

 TrainerPalettes:
 ; entries correspond to trainer classes

 ; Each .gbcpal is generated from the corresponding .png, and
 ; only the middle two colors are included, not black or white.

	table_width PAL_COLOR_SIZE * 2, TrainerPalettes

-PlayerPalette: ; Chris uses the same colors as Cal
-INCBIN "gfx/trainers/cal.gbcpal", middle_colors
+PlayerPalette:
+INCLUDE "gfx/player/chris.pal"
+
-KrisPalette: ; Kris shares Falkner's palette
 INCBIN "gfx/trainers/falkner.gbcpal", middle_colors
 ...
+DudePalette:
 INCBIN "gfx/trainers/cal.gbcpal", middle_colors
 ...
 INCBIN "gfx/trainers/mysticalman.gbcpal", middle_colors
+
+KrisPalette:
+INCLUDE "gfx/player/kris.pal"

-	assert_table_length NUM_TRAINER_CLASSES + 1
+	assert_table_length NUM_TRAINER_CLASSES + 2

Previously, Chris was using the same colors as Cal (the boy player lookalike whom you battle in Viridian City's Trainer House); and Kris was literally using Falkner's palette (which makes sense, given that KRIS and FALKNER were equivalent). Now they both have their own independent palettes.

Notice how they INCLUDE .pal files, not INCBIN .gbcpal files. That's because the .gbcpal files are automatically generated from trainer sprites' .png files, whereas we manually created the players' palettes.

Remember, Chris (the boy player) has to be the first entry, and Kris (the girl player) has to be the last. The ChrisPalette and KrisPalette labels are sometimes used to directly access their palettes, but sometimes the game relies on their index order. So if you add any new trainer classes, make sure they stay in order.

Also note the new DudePalette label. Before editing the trainer card, let's see how that gets used.

9. Give the catch tutorial Dude his own palette

When the Dude on Route 29 shows you how to catch Pokémon, his back sprite shares the same color as the player (red for a boy, blue for a girl), even though his overworld sprite is always red. Now that we're changing the player colors, it would be nice if the Dude has a consistent color of his own.

Edit engine/gfx/color.asm:

 GetPlayerOrMonPalettePointer:
 	and a
 	jp nz, GetMonNormalOrShinyPalettePointer
 	ld a, [wPlayerSpriteSetupFlags]
 	bit PLAYERSPRITESETUP_FEMALE_TO_MALE_F, a
 	jr nz, .male
+	ld a, [wBattleType]
+	cp BATTLETYPE_TUTORIAL
+	jr z, .dude
 	ld a, [wPlayerGender]
 	and a
 	jr z, .male
 	ld hl, KrisPalette
 	ret

 .male
 	ld hl, PlayerPalette
 	ret
+
+.dude
+	ld hl, DudePalette
+	ret

I placed the DudePalette label so that he shares Cal's palette, since that's what he used originally, but you can put that label anywhere. It doesn't even have to be among the trainer colors. If your catch tutorial NPC needs a custom color, just define it with two RGB macros, the same as chris.pal and kris.pal, and INCLUDE it with the DudePalette label after the GetPlayerOrMonPalettePointer routine.

Anyway, it's time for the last colors to change: the trainer card.

10. Edit the trainer card colors

The trainer card "cheats" a little bit. The Game Boy Color hardware only allows eight palettes at a time for BG tiles, as we can see with BGB's VRAM viewer:

Screenshot

But the trainer card wants ten: one for the player's sprite, one for the border, and eight unique Gym Leader faces. So what the game designers did is pick two pairs of those to share palettes. We already saw how Kris and Falkner used the exact same palette, and in fact, the trainer card used that palette for Clair's face too, even though her trainer sprite is a different shade of blue.

Screenshot

But now that Kris is her own shade of green, we'll have to rethink this design. In the example below, I chose to make Falkner and Clair share a palette, and Chuck and Pryce share a palette. Depending on how your Gym Leaders look, you may have a better choice available.

Edit engine/gfx/cgb_layouts.asm:

 _CGB_TrainerCard:
 	ld de, wBGPals1
 	xor a ; CHRIS
 	call GetTrainerPalettePointer
 	call LoadPalette_White_Col1_Col2_Black
-	ld a, FALKNER ; KRIS
+	ld a, KRIS
 	call GetTrainerPalettePointer
 	call LoadPalette_White_Col1_Col2_Black
 	ld a, BUGSY
 	call GetTrainerPalettePointer
 	call LoadPalette_White_Col1_Col2_Black
 	ld a, WHITNEY
 	call GetTrainerPalettePointer
 	call LoadPalette_White_Col1_Col2_Black
 	ld a, MORTY
 	call GetTrainerPalettePointer
 	call LoadPalette_White_Col1_Col2_Black
-	ld a, CHUCK
+	ld a, FALKNER ; CLAIR
 	call GetTrainerPalettePointer
 	call LoadPalette_White_Col1_Col2_Black
 	ld a, JASMINE
 	call GetTrainerPalettePointer
 	call LoadPalette_White_Col1_Col2_Black
-	ld a, PRYCE
+	ld a, PRYCE ; CHUCK
 	call GetTrainerPalettePointer
 	call LoadPalette_White_Col1_Col2_Black
 	ld a, PREDEFPAL_CGB_BADGE
 	call GetPredefPal
 	call LoadHLPaletteIntoDE

	...

 .got_gender2
 	call FillBoxCGB
-	; top-right corner still uses the border's palette
-	hlcoord 18, 1, wAttrmap
-	ld [hl], $1
 	hlcoord 2, 11, wAttrmap
 	lb bc, 2, 4
-	ld a, $1 ; falkner
+	ld a, $5 ; falkner
 	call FillBoxCGB
 	hlcoord 6, 11, wAttrmap
 	lb bc, 2, 4
 	ld a, $2 ; bugsy
 	call FillBoxCGB
 	hlcoord 10, 11, wAttrmap
 	lb bc, 2, 4
 	ld a, $3 ; whitney
 	call FillBoxCGB
 	hlcoord 14, 11, wAttrmap
 	lb bc, 2, 4
 	ld a, $4 ; morty
 	call FillBoxCGB
 	hlcoord 2, 14, wAttrmap
 	lb bc, 2, 4
-	ld a, $5 ; chuck
+	ld a, $7 ; chuck
 	call FillBoxCGB
 	hlcoord 6, 14, wAttrmap
 	lb bc, 2, 4
 	ld a, $6 ; jasmine
 	call FillBoxCGB
 	hlcoord 10, 14, wAttrmap
 	lb bc, 2, 4
 	ld a, $7 ; pryce
 	call FillBoxCGB
-	; clair uses kris's palette
-	ld a, [wPlayerGender]
-	and a
-	push af
-	jr z, .got_gender3
 	hlcoord 14, 14, wAttrmap
 	lb bc, 2, 4
-	ld a, $1
+	ld a, $5 ; clair
 	call FillBoxCGB
-.got_gender3
-	pop af
-	ld c, $0
-	jr nz, .got_gender4
-	inc c
-.got_gender4
-	ld a, c
+	; top-right corner still uses the border's palette
 	hlcoord 18, 1, wAttrmap
+	ld a, [wPlayerGender]
+	and a
+	ld a, $1 ; kris
+	jr z, .got_gender3
+	ld a, $0 ; chris
+.got_gender3
 	ld [hl], a
 	call ApplyAttrmap
 	call ApplyPals
 	ld a, $1
 	ldh [hCGBPalUpdate], a
 	ret

Basically this routine has two stages: it loads eight trainers' palettes with GetTrainerPalettePointer and LoadPalette_White_Col1_Col2_Black, and then it applies those palettes to rectangular areas of the 20x18 screen with FillBoxCGB. It happens to always load both the boy and girl palettes, using one for the player's sprite and the other for the card border, but you can always change that logic if you want a different border color. Just remember that only eight palettes in total can be used.

Anyway, with that, we're all done. Every player-related aspect of the game is recolored for both the boy and girl options.

Screenshot