-
Notifications
You must be signed in to change notification settings - Fork 806
Add a third trainer card page for Kanto badges
The engine for the Trainer Card screen has unused (and incomplete) code intended to display a third page for the Kanto Badges. The following tutorial outlines the necessary steps needed for restoring the feature.
Note: This tutorial assumes you have not implemented either colored trainer card badges or show the tops of leaders heads on the trainer card. However, both of those can be implemented by following the steps listed in those tutorials, but substituting the existing Johto relevant code with the new Kanto relevant code from below. See the appendix for additional details.
- Define new SGB and CGB color palette constant
- Reuse the SGB palette routine
- Create the CGB palette routine
- Modify the trainer card engine code
- Appendix
First, we need to define a new constant in constants/scgb_constants.asm:
; CGBLayoutJumptable indexes (see engine/gfx/cgb_layouts.asm)
; SGBLayoutJumptable indexes (see engine/gfx/sgb_layouts.asm)
const_def
...
const SCGB_POKEPIC
const SCGB_MAGNET_TRAIN
const SCGB_PACKPALS
const SCGB_TRAINER_CARD
+ const SCGB_TRAINER_CARD_KANTO
const SCGB_POKEDEX_UNOWN_MODE
const SCGB_BILLS_PC
const SCGB_UNOWN_PUZZLE
...
DEF NUM_SCGB_LAYOUTS EQU const_value
...
Open engine/gfx/sgb_layouts.asm
These are the routines for loading palette data when running the game in Super Game Boy mode. We need to associate the new constant with a routine that will load the correct palette. Luckily for us, we can just point to and reuse the existing routine.
Associate a routine pointer for the new constant in the SGBLayoutJumptable
table:
SGBLayoutJumptable:
table_width 2, SGBLayoutJumptable
...
dw .SGB_Pokepic
dw .SGB_MagnetTrain
dw .SGB_PackPals
dw .SGB_TrainerCard
+ dw .SGB_TrainerCardKanto
dw .SGB_PokedexUnownMode
dw .SGB_BillsPC
dw .SGB_UnownPuzzle
...
assert_table_length NUM_SCGB_LAYOUTS
...
And then add the new label to the existing routine SGB_TrainerCard
:
.SGB_Unused0D:
.SGB_TrainerCard:
+ .SGB_TrainerCardKanto:
ld hl, PalPacket_Diploma
ld de, BlkPacket_AllPal0
ret
Open engine/gfx/cgb_layouts.asm
These are the routines for loading palette data when running the game in Color Game Boy mode. Naturally, these functions are quite a degree more complicated than their SGB counterparts. Same as before, we need to associate the new constant with a routine that will load the desired palette. Except, this time a new routine will have to be created.
Associate a routine pointer for the new constant in the CGBLayoutJumptable
table:
CGBLayoutJumptable:
table_width 2, CGBLayoutJumptable
...
dw _CGB_Pokepic
dw _CGB_MagnetTrain
dw _CGB_PackPals
dw _CGB_TrainerCard
+ dw _CGB_TrainerCardKanto
dw _CGB_PokedexUnownMode
dw _CGB_BillsPC
dw _CGB_UnownPuzzle
...
assert_table_length NUM_SCGB_LAYOUTS
...
Then create the new routine label _CGB_TrainerCardKanto
between the existing routine _CGB_TrainerCard
and the routine _CGB_MoveList
:
_CGB_TrainerCard:
...
ret
+
+ _CGB_TrainerCardKanto:
+ < C&P EXISTING CODE HERE >
+
_CGB_MoveList:
...
ret
Next, we will be copy & pasting the existing code from _CGB_TrainerCard
to our new routine _CGB_TrainerCardKanto
, but with the following changes:
In the first half of the code, change all the Johto trainer constants to the corresponding Kanto trainer constants. Note that palettes 0 and 1 are reserved for the male and female player palettes. This only leaves 6 open palettes for 8 gym leaders. In much the same way that the old code reuses the same palette for Kris, Falkner, and Claire; Chris will share a palette with Misty, and Lt.Surge with Erika.
_CGB_TrainerCard:
...
ret
_CGB_TrainerCardKanto:
ld de, wBGPals1
- xor a ; CHRIS
+ xor a ; CHRIS & MISTY
call GetTrainerPalettePointer
call LoadPalette_White_Col1_Col2_Black
ld a, FALKNER ; KRIS
call GetTrainerPalettePointer
call LoadPalette_White_Col1_Col2_Black
- ld a, BUGSY
+ ld a, BROCK
call GetTrainerPalettePointer
call LoadPalette_White_Col1_Col2_Black
- ld a, WHITNEY
+ ld a, LT_SURGE ; ERIKA
call GetTrainerPalettePointer
call LoadPalette_White_Col1_Col2_Black
- ld a, MORTY
+ ld a, JANINE
call GetTrainerPalettePointer
call LoadPalette_White_Col1_Col2_Black
- ld a, CHUCK
+ ld a, SABRINA
call GetTrainerPalettePointer
call LoadPalette_White_Col1_Col2_Black
- ld a, JASMINE
+ ld a, BLAINE
call GetTrainerPalettePointer
call LoadPalette_White_Col1_Col2_Black
- ld a, PRYCE
+ ld a, BLUE
call GetTrainerPalettePointer
call LoadPalette_White_Col1_Col2_Black
ld a, PREDEFPAL_CGB_BADGE
call GetPredefPal
call LoadHLPaletteIntoDE
...
_CGB_MoveList:
...
ret
In the second half we will keep most of the same code, save for changing the palette $id that we load into each section of the screen. We will also take this moment to refactor a leftover remnant of code from GS that recolors the upper right hand corner by moving that logic to the end. It might not be a bad idea to do the same for the existing code as well.
_CGB_TrainerCard:
...
ret
_CGB_TrainerCardKanto:
...
; fill screen with opposite-gender palette for the card border
hlcoord 0, 0, wAttrmap
ld bc, SCREEN_WIDTH * SCREEN_HEIGHT
ld a, [wPlayerGender]
and a
ld a, $1 ; kris
jr z, .got_gender
ld a, $0 ; chris
.got_gender
call ByteFill
; fill trainer sprite area with same-gender palette
hlcoord 14, 1, wAttrmap
lb bc, 7, 5
ld a, [wPlayerGender]
and a
ld a, $0 ; chris
jr z, .got_gender2
ld a, $1 ; kris
.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, $2 ; brock
call FillBoxCGB
hlcoord 6, 11, wAttrmap
lb bc, 2, 4
- ld a, $2 ; bugsy
+ ld a, $0 ; misty / chris
call FillBoxCGB
hlcoord 10, 11, wAttrmap
lb bc, 2, 4
- ld a, $3 ; whitney
+ ld a, $3 ; lt.surge / erika
call FillBoxCGB
hlcoord 14, 11, wAttrmap
lb bc, 2, 4
- ld a, $4 ; morty
+ ld a, $3 ; erika / lt.surge
call FillBoxCGB
hlcoord 2, 14, wAttrmap
lb bc, 2, 4
- ld a, $5 ; chuck
+ ld a, $4 ; janine
call FillBoxCGB
hlcoord 6, 14, wAttrmap
lb bc, 2, 4
- ld a, $6 ; jasmine
+ ld a, $5 ; sabrina
call FillBoxCGB
hlcoord 10, 14, wAttrmap
lb bc, 2, 4
- ld a, $7 ; pryce
+ ld a, $6 ; blaine
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, $7 ; blue
call FillBoxCGB
+ ; top-right corner still uses the border's palette
+ ld a, [wPlayerGender]
+ and a
+ ld a, $1 ; kris
+ jr z, .got_gender3
+ ld a, $0 ; chris
.got_gender3
- pop af
- ld c, $0
- jr nz, .got_gender4
- inc c
- .got_gender4
- ld a, c
hlcoord 18, 1, wAttrmap
ld [hl], a
call ApplyAttrmap
call ApplyPals
ld a, TRUE
ldh [hCGBPalUpdate], a
ret
_CGB_MoveList:
...
ret
Open engine/menus/trainer_card.asm
These are the routines that control the trainer card screen. Everything from drawing the graphics, handling input and screen state navigation, and displaying the correct data in each screen state. Scrolling through this code, you will find many references to a page 3 and Kanto badges. At one point in development it must have been planned for a page 3 that displays Kanto badges to be included. However, since the feature was scrapped, most of the code is in an incomplete state. To restore the missing page 3, we need to complete the following:
- Fix jump table navigation code
- Edit OAM data and include new GFX files
- Load per-page palette and OAM data
First, we can remove this unused Kanto check under TrainerCard_Page1_Joypad
:
...
.pressed_right_a
ld a, TRAINERCARDSTATE_PAGE2_LOADGFX
ld [wJumptableIndex], a
ret
- .KantoBadgeCheck: ; unreferenced
- ld a, [wKantoBadges]
- and a
- ret z
- ld a, TRAINERCARDSTATE_PAGE3_LOADGFX
- ld [wJumptableIndex], a
- ret
TrainerCard_Page2_LoadGFX:
...
Second, alter the navigation code for the Johto page under TrainerCard_Page2_Joypad
such that pressing A or Right navigates to the third page when the player has 1 or more Kanto badges:
...
TrainerCard_Page2_Joypad:
ld hl, TrainerCard_JohtoBadgesOAM
call TrainerCard_Page2_3_AnimateBadges
ld hl, hJoyLast
ld a, [hl]
+ and D_LEFT
+ jr nz, .pressed_left
+ ld a, [wKantoBadges]
+ and a
+ jr nz, .has_kanto_badges
+ ld a, [hl]
and A_BUTTON
jr nz, .Quit
- ld a, [hl]
- and D_LEFT
- jr nz, .d_left
ret
+ .has_kanto_badges
+ ld a, [hl]
+ and D_RIGHT | A_BUTTON
+ jr nz, .pressed_right_a
+ ret
- .d_left
+ .pressed_left
ld a, TRAINERCARDSTATE_PAGE1_LOADGFX
ld [wJumptableIndex], a
ret
- .KantoBadgeCheck: ; unreferenced
- ld a, [wKantoBadges]
- and a
- ret z
+ .pressed_right_a
ld a, TRAINERCARDSTATE_PAGE3_LOADGFX
ld [wJumptableIndex], a
ret
.Quit:
ld a, TRAINERCARDSTATE_QUIT
ld [wJumptableIndex], a
ret
TrainerCard_Page3_LoadGFX:
...
Third, alter the navigation code for the Kanto page under TrainerCard_Page3_Joypad
such that pressing A quits and pressing right does not loop back around to the first page:
...
TrainerCard_Page3_Joypad:
ld hl, TrainerCard_JohtoBadgesOAM
call TrainerCard_Page2_3_AnimateBadges
ld hl, hJoyLast
ld a, [hl]
and D_LEFT
- jr nz, .left
+ jr nz, .pressed_left
ld a, [hl]
- and D_RIGHT
- jr nz, .right
+ and A_BUTTON
+ jr nz, .pressed_a
ret
- .left
+ .pressed_left
ld a, TRAINERCARDSTATE_PAGE2_LOADGFX
ld [wJumptableIndex], a
ret
- .right
- ld a, TRAINERCARDSTATE_PAGE1_LOADGFX
+ .pressed_a
+ ld a, TRAINERCARDSTATE_QUIT
ld [wJumptableIndex], a
ret
We need to define the OAM data for the Kanto badge screen. This data defines which wram address has the badge count to use (Johto or Kanto) as well as the individual tile data for each badge. Copy the code under TrainerCard_JohtoBadgesOAM
into a new label TrainerCard_KantoBadgesOAM
:
Note: In the example below, the Kanto badges have their int1-int3 tiles x-flipped opposite of the Johto badges. This creates the illusion as if they were spinning the opposite way. This is purely a cosmetic change to make them unique.
TrainerCard_JohtoBadgesOAM:
- ; Template OAM data for each badge on the trainer card.
+ ; Template OAM data for Johto badges on the trainer card.
; Format:
; y, x, palette
; cycle 1: face tile, in1 tile, in2 tile, in3 tile
; cycle 2: face tile, in1 tile, in2 tile, in3 tile
dw wJohtoBadges
...
+ TrainerCard_KantoBadgesOAM:
+ ; Template OAM data for Kanto badges on the trainer card.
+ ; Format:
+ ; y, x, palette
+ ; cycle 1: face tile, in1 tile, in2 tile, in3 tile
+ ; cycle 2: face tile, in1 tile, in2 tile, in3 tile
+
+ dw wKantoBadges
+
+ ; Boulderbadge
+ db $68, $18, 0
+ db $00, $20 | (1 << 7), $24, $20
+ db $00, $20 | (1 << 7), $24, $20
+
+ ; Cascadebadge
+ db $68, $38, 0
+ db $04, $20 | (1 << 7), $24, $20
+ db $04, $20 | (1 << 7), $24, $20
+
+ ; Thunderbadge
+ db $68, $58, 0
+ db $08, $20 | (1 << 7), $24, $20
+ db $08, $20 | (1 << 7), $24, $20
+
+ ; Rainbowbadge
+ db $68, $78, 0
+ db $0c, $20 | (1 << 7), $24, $20
+ db $0c, $20 | (1 << 7), $24, $20
+
+ ; Soulbadge
+ db $80, $18, 0
+ db $10, $20 | (1 << 7), $24, $20
+ db $10, $20 | (1 << 7), $24, $20
+
+ ; Marshbadge
+ db $80, $38, 0
+ db $14, $20 | (1 << 7), $24, $20
+ db $14, $20 | (1 << 7), $24, $20
+
+ ; Volcanobadge
+ db $80, $58, 0
+ db $18, $20 | (1 << 7), $24, $20
+ db $18, $20 | (1 << 7), $24, $20
+
+ ; Earthbadge
+ ; X-flips on alternate cycles.
+ db $80, $78, 0
+ db $1c, $20 | (1 << 7), $24, $20
+ db $1c | (1 << 7), $20 | (1 << 7), $24, $20
CardStatusGFX: INCBIN "gfx/trainer_card/card_status.2bpp"
- LeaderGFX: INCBIN "gfx/trainer_card/leaders.2bpp"
- LeaderGFX2: INCBIN "gfx/trainer_card/leaders.2bpp"
- BadgeGFX: INCBIN "gfx/trainer_card/badges.2bpp"
- BadgeGFX2: INCBIN "gfx/trainer_card/badges.2bpp"
+ LeaderGFX: INCBIN "gfx/trainer_card/johto_leaders.2bpp"
+ LeaderGFX2: INCBIN "gfx/trainer_card/kanto_leaders.2bpp"
+ BadgeGFX: INCBIN "gfx/trainer_card/johto_badges.2bpp"
+ BadgeGFX2: INCBIN "gfx/trainer_card/kanto_badges.2bpp"
CardRightCornerGFX: INCBIN "gfx/trainer_card/card_right_corner.2bpp"
Add these files to the /gfx/trainer_card directory and rename the existing files accordingly.
kanto_leaders.png
kanto_badges.png
Here under TrainerCard_Page1_LoadGFX
:
TrainerCard_Page1_LoadGFX:
call ClearSprites
hlcoord 0, 8
ld d, 6
call TrainerCard_InitBorder
call WaitBGMap
+ ld b, SCGB_TRAINER_CARD
+ call GetSGBLayout
+ call SetDefaultBGPAndOBP ; this function was called 'SetPalettes' in older versions of pokecrystal
+ call WaitBGMap
ld de, CardStatusGFX
ld hl, vTiles2 tile $29
lb bc, BANK(CardStatusGFX), 86
...
Here under TrainerCard_Page2_LoadGFX
:
TrainerCard_Page2_LoadGFX:
call ClearSprites
hlcoord 0, 8
ld d, 6
call TrainerCard_InitBorder
call WaitBGMap
+ ld b, SCGB_TRAINER_CARD
+ call GetSGBLayout
+ call SetDefaultBGPAndOBP ; this function was called 'SetPalettes' in older versions of pokecrystal
+ call WaitBGMap
ld de, LeaderGFX
ld hl, vTiles2 tile $29
lb bc, BANK(LeaderGFX), 86
call Request2bpp
ld de, BadgeGFX
ld hl, vTiles0 tile $00
lb bc, BANK(BadgeGFX), 44
call Request2bpp
+ ld hl, TrainerCard_JohtoBadgesOAM
call TrainerCard_Page2_3_InitObjectsAndStrings
call TrainerCard_IncrementJumptable
ret
TrainerCard_Page2_Joypad:
ld hl, TrainerCard_JohtoBadgesOAM
call TrainerCard_Page2_3_AnimateBadges
...
And here under TrainerCard_Page3_LoadGFX
, this time with our new Kanto constant and Kanto OAM data:
TrainerCard_Page3_LoadGFX:
call ClearSprites
hlcoord 0, 8
ld d, 6
call TrainerCard_InitBorder
call WaitBGMap
+ ld b, SCGB_TRAINER_CARD_KANTO
+ call GetSGBLayout
+ call SetDefaultBGPAndOBP ; this function was called 'SetPalettes' in older versions of pokecrystal
+ call WaitBGMap
ld de, LeaderGFX2
ld hl, vTiles2 tile $29
lb bc, BANK(LeaderGFX2), 86
call Request2bpp
+ ld hl, TrainerCard_KantoBadgesOAM
call TrainerCard_Page2_3_InitObjectsAndStrings
call TrainerCard_IncrementJumptable
ret
TrainerCard_Page3_Joypad:
- ld hl, TrainerCard_JohtoBadgesOAM
+ ld hl, TrainerCard_KantoBadgesOAM
call TrainerCard_Page2_3_AnimateBadges
...
Then we must change the routine TrainerCard_Page2_3_InitObjectsAndStrings
so it works with either OAM data set:
TrainerCard_Page2_3_InitObjectsAndStrings:
+ push hl
hlcoord 2, 8
ld de, .BadgesTilemap
call TrainerCardSetup_PlaceTilemapString
hlcoord 2, 10
ld a, $29
ld c, 4
...
xor a
ld [wTrainerCardBadgeFrameCounter], a
- ld hl, TrainerCard_JohtoBadgesOAM
+ pop hl
call TrainerCard_Page2_3_OAMUpdate
ret
If you want to follow the tutorial to display colored trainer card badges, here is the palette data for the kanto badges:
Create gfx/trainer_card/kanto_badges.pal:
+ RGB 31,31,31, 23,22,22, 14,13,13, 00,00,00 ; Boulder Badge
+ RGB 31,31,31, 19,31,30, 00,23,30, 00,00,00 ; Cascade Badge
+ RGB 31,31,31, 31,26,05, 31,11,00, 00,00,00 ; Thunder Badge
+ RGB 31,31,31, 31,31,14, 00,29,07, 00,00,00 ; Rainbow Badge
+ RGB 31,31,31, 31,19,30, 31,09,30, 00,00,00 ; Marsh Badge
+ RGB 31,31,31, 31,22,04, 19,13,01, 00,00,00 ; Soul Badge
+ RGB 31,31,31, 31,17,23, 31,00,06, 00,00,00 ; Volcano Badge
+ RGB 31,31,31, 19,30,12, 00,16,06, 00,00,00 ; Earth Badge
If you want to follow the tutorial to show the tops of leaders heads, here is an updated gfx file with the blank tiles restored:
kanto_leaders.png