This tutorial is for how to add a fourth page to Pokémon stats. As an example, we'll make an orange page after the pink, green, and blue ones. (The code for this feature was adapted from [i-am-the-pokeman](https://github.com/i-am-the-pokeman/pokecrystal-4th-stat-screen/).) ## Contents 1. [Define the fourth page's colors](#1-define-the-fourth-pages-colors) 2. [Load and apply the fourth page's palette](#2-load-and-apply-the-fourth-pages-palette) 3. [Display the fourth page's content](#3-display-the-fourth-pages-content) 4. [Account for the shifted page indexes](#4-account-for-the-shifted-page-indexes) 5. [Show Caught Data on the fourth page](#5-show-caught-data-on-the-fourth-page) ## 1. Define the fourth page's colors Edit [gfx/stats/pages.pal](../blob/master/gfx/stats/pages.pal): ```diff ; pink RGB 31, 31, 31 RGB 31, 19, 31 RGB 31, 15, 31 RGB 00, 00, 00 ; green RGB 31, 31, 31 RGB 21, 31, 14 RGB 17, 31, 00 RGB 00, 00, 00 ; blue RGB 31, 31, 31 RGB 17, 31, 31 RGB 17, 31, 31 RGB 00, 00, 00 +; orange + RGB 31, 31, 31 + RGB 30, 24, 16 + RGB 30, 22, 12 + RGB 00, 00, 00 ``` And edit [gfx/stats/stats.pal](../blob/master/gfx/stats/stats.pal): ```diff ; pink RGB 31, 19, 31 ; green RGB 21, 31, 14 ; blue RGB 17, 31, 31 +; orange + RGB 30, 24, 16 ``` `RGB` colors define 5-bit red, green, and blue channels, each from 0 to 31. (On a PC, typically 8-bit color channels go from 0 to 255, or in hex 00 to FF. Just multiply or divide by 8 to convert between them; e.g. `RGB 30, 24, 16` looks like [#F0C080](https://www.color-hex.com/color/F0C080).) Both of these files sort the pages in order from left to right. ## 2. Load and apply the fourth page's palette Edit [engine/gfx/cgb_layouts.asm](../blob/master/engine/gfx/cgb_layouts.asm): ```diff _CGB_StatsScreenHPPals: ld de, wBGPals1 ld a, [wCurHPPal] ld l, a ld h, $0 add hl, hl add hl, hl ld bc, HPBarPals add hl, bc call LoadPalette_White_Col1_Col2_Black ; hp palette ld a, [wCurPartySpecies] ld bc, wTempMonDVs call GetPlayerOrMonPalettePointer call LoadPalette_White_Col1_Col2_Black ; mon palette ld hl, ExpBarPalette call LoadPalette_White_Col1_Col2_Black ; exp palette ld hl, StatsScreenPagePals ld de, wBGPals1 palette 3 - ld bc, 3 palettes ; pink, green, and blue page palettes + ld bc, 4 palettes ; pink, green, blue, and orange page palettes ld a, BANK(wBGPals1) call FarCopyWRAM call WipeAttrmap hlcoord 0, 0, wAttrmap lb bc, 8, SCREEN_WIDTH ld a, $1 ; mon palette call FillBoxCGB hlcoord 10, 16, wAttrmap ld bc, 10 ld a, $2 ; exp palette call ByteFill - hlcoord 13, 5, wAttrmap + hlcoord 11, 5, wAttrmap lb bc, 2, 2 ld a, $3 ; pink page palette call FillBoxCGB - hlcoord 15, 5, wAttrmap + hlcoord 13, 5, wAttrmap lb bc, 2, 2 ld a, $4 ; green page palette call FillBoxCGB - hlcoord 17, 5, wAttrmap + hlcoord 15, 5, wAttrmap lb bc, 2, 2 ld a, $5 ; blue page palette call FillBoxCGB + + hlcoord 17, 5, wAttrmap + lb bc, 2, 2 + ld a, $6 ; orange page palette + call FillBoxCGB call ApplyAttrmap call ApplyPals ld a, $1 ldh [hCGBPalUpdate], a ret StatsScreenPagePals: INCLUDE "gfx/stats/pages.pal" StatsScreenPals: INCLUDE "gfx/stats/stats.pal" ``` Here we load the new fourth palette, and change where the palettes are applied to the screen. The pink, green, and blue page icons are going to shift left to make room for a fourth orange icon. ## 3. Display the fourth page's content Edit [engine/pokemon/stats_screen.asm](../blob/master/engine/pokemon/stats_screen.asm): ```diff - const_def 1 - const PINK_PAGE ; 1 - const GREEN_PAGE ; 2 - const BLUE_PAGE ; 3 -NUM_STAT_PAGES EQU const_value + -1 + const_def + const PINK_PAGE ; 0 + const GREEN_PAGE ; 1 + const BLUE_PAGE ; 2 + const ORANGE_PAGE ; 3 +NUM_STAT_PAGES EQU const_value ``` Instead of three constants from 1 to 3, we'll use four constants from 0 to 3. We're not using 1 to 4, because 4 in binary is %100, and these values need to fit in two bits. ```diff StatsScreenMain: xor a ld [wJumptableIndex], a -; ??? - ld [wStatsScreenFlags], a - ld a, [wStatsScreenFlags] - and ~STAT_PAGE_MASK - or PINK_PAGE ; first_page - ld [wStatsScreenFlags], a + ld [wStatsScreenFlags], a ; PINK_PAGE .loop ld a, [wJumptableIndex] and ~(1 << 7) ld hl, StatsScreenPointerTable rst JumpTable call StatsScreen_WaitAnim ; check for keys? ld a, [wJumptableIndex] bit 7, a jr z, .loop ret StatsScreenMobile: xor a ld [wJumptableIndex], a - ??? - ld [wStatsScreenFlags], a - ld a, [wStatsScreenFlags] - and ~STAT_PAGE_MASK - or PINK_PAGE ; first_page - ld [wStatsScreenFlags], a + ld [wStatsScreenFlags], a ; PINK_PAGE .loop farcall Mobile_SetOverworldDelay ld a, [wJumptableIndex] and ~(1 << 7) ld hl, StatsScreenPointerTable rst JumpTable call StatsScreen_WaitAnim farcall MobileComms_CheckInactivityTimer jr c, .exit ld a, [wJumptableIndex] bit 7, a jr z, .loop .exit ret ``` This code was a bit messy and redundant, but the intended effect is to start at the pink page. We changed its index from 1 to 0, so that needed updating. ```diff StatsScreen_JoypadAction: ... .a_button ld a, c - cp BLUE_PAGE ; last page + cp ORANGE_PAGE ; last page jr z, .b_button .d_right inc c - ld a, BLUE_PAGE ; last page + ld a, ORANGE_PAGE ; last page cp c jr nc, .set_page ld c, PINK_PAGE ; first page jr .set_page .d_left + ld a, c dec c + and a ; cp PINK_PAGE ; first page jr nz, .set_page - ld c, BLUE_PAGE ; last page + ld c, ORANGE_PAGE ; last page jr .set_page .done ret .set_page ld a, [wcf64] and %11111100 or c ld [wcf64], a ld h, 4 call StatsScreen_SetJumptableIndex ret ``` Pressing left and right navigates between the pages, wrapping around the ends if necessary. The orange page is last, not the blue page any more, and some logic also needed updating since a page index of 0 is now possible. ```diff StatsScreen_PlacePageSwitchArrows: - hlcoord 12, 6 + hlcoord 10, 6 ld [hl], "◀" hlcoord 19, 6 ld [hl], "▶" ret ``` The arrow left of the page icons needs shifting further left to make room for a fourth icon. ```diff StatsScreen_LoadGFX: ... .PageTilemap: ld a, [wStatsScreenFlags] maskbits NUM_STAT_PAGES - dec a ld hl, .Jumptable rst JumpTable ret .Jumptable: ; entries correspond to *_PAGE constants dw LoadPinkPage dw LoadGreenPage dw LoadBluePage + dw LoadOrangePage ... +LoadOrangePage: + ld de, HelloWorldString + hlcoord 1, 9 + call PlaceString + ret + +HelloWorldString: + db "Hello world!@" + IDNoString: db "№.@" OTString: db "OT/@" ``` This is where we display the actual content of the orange page (as well as another adjustment to account for the pink page's new index 0). This example will just say ["Hello world!"](https://en.wikipedia.org/wiki/%22Hello,_World!%22_program) ```diff StatsScreen_LoadPageIndicators: + hlcoord 11, 5 + ld a, $36 ; " " " " + call .load_square hlcoord 13, 5 ld a, $36 ; first of 4 small square tiles call .load_square hlcoord 15, 5 ld a, $36 ; " " " " call .load_square hlcoord 17, 5 ld a, $36 ; " " " " call .load_square ld a, c - cp GREEN_PAGE - ld a, $3a ; first of 4 large square tiles - hlcoord 13, 5 ; PINK_PAGE (< GREEN_PAGE) - jr c, .load_square - hlcoord 15, 5 ; GREEN_PAGE (= GREEN_PAGE) - jr z, .load_square - hlcoord 17, 5 ; BLUE_PAGE (> GREEN_PAGE) + cp PINK_PAGE + hlcoord 11, 5 + jr z, .load_highlighted_square + cp GREEN_PAGE + hlcoord 13, 5 + jr z, .load_highlighted_square + cp BLUE_PAGE + hlcoord 15, 5 + jr z, .load_highlighted_square + ; must be ORANGE_PAGE + hlcoord 17, 5 +.load_highlighted_square + ld a, $3a ; first of 4 large square tiles .load_square push bc ld [hli], a inc a ld [hld], a ld bc, SCREEN_WIDTH add hl, bc inc a ld [hli], a inc a ld [hl], a pop bc ret ``` The current page's icon is shown larger than the rest. Apart from changing their coordinates, we also need to update the logic. With only three pages, a single less/equal/greater comparison to the middle page's index was enough to tell which one to highlight, but four pages need more thorough checking. ## 4. Account for the shifted page indexes Edit [engine/gfx/color.asm](../blob/master/engine/gfx/color.asm): ```diff LoadStatsScreenPals: call CheckCGB ret z ld hl, StatsScreenPals ld b, 0 - dec c add hl, bc add hl, bc ldh a, [rSVBK] push af ld a, BANK(wBGPals1) ldh [rSVBK], a ld a, [hli] ld [wBGPals1 palette 0], a ld [wBGPals1 palette 2], a ld a, [hl] ld [wBGPals1 palette 0 + 1], a ld [wBGPals1 palette 2 + 1], a pop af ldh [rSVBK], a call ApplyPals ld a, $1 ret ``` This function used to adjust the page index in `c` from 1–3 to 0–2. Now that indexes are 0–3, they don't need adjustment. Now we have a fourth stats page in-game! ![Screenshot](screenshots/fourth-stats-page.png) ## 5. Show Caught Data on the fourth page This is an alternative `loadOrangePage`, as opposed to the "Hello world!" example earlier. It displays the Caught Location (or "UNKNOWN"), the caught Time (or a blank row if the Caught Location was unknown) as well as the level the Pokémon was met at (or "???"). ```diff +LoadOrangePage: + call .placeCaughtLocation + ld de, MetAtMapString + hlcoord 1, 9 + call PlaceString + call .placeCaughtLevel + ret + +.placeCaughtLocation + ld a, [wTempMonCaughtLocation] + and CAUGHT_LOCATION_MASK + jr z, .unknown_location + cp LANDMARK_EVENT + jr z, .unknown_location + cp LANDMARK_GIFT + jr z, .unknown_location + ld e, a + farcall GetLandmarkName + ld de, wStringBuffer1 + hlcoord 2, 10 + call PlaceString + ld a, [wTempMonCaughtTime] + and CAUGHT_TIME_MASK + ret z ; no time + rlca + rlca + dec a + ld hl, .times + call GetNthString + ld d, h + ld e, l + call CopyName1 + ld de, wStringBuffer2 + hlcoord 2, 11 + call PlaceString + ret + +.unknown_location: + ld de, MetUnknownMapString + hlcoord 2, 10 + call PlaceString + ret + +.times + db "MORN@" + db "DAY@" + db "NITE@" + +.placeCaughtLevel + ; caught level + ; Limited to between 1 and 63 since it's a 6-bit quantity. + ld a, [wTempMonCaughtLevel] + and CAUGHT_LEVEL_MASK + jr z, .unknown_level + cp CAUGHT_EGG_LEVEL ; egg marker value + jr nz, .print + ld a, EGG_LEVEL ; egg hatch level + +.print + ld [wTextDecimalByte], a + hlcoord 3, 13 + ld de, wTextDecimalByte + lb bc, PRINTNUM_LEFTALIGN | 1, 3 + call PrintNum + ld de, MetAtLevelString + hlcoord 1, 12 + call PlaceString + hlcoord 2, 13 + ld [hl], "" + ret + +.unknown_level + ld de, MetUnknownLevelString + hlcoord 2, 12 + call PlaceString + ret + +MetAtMapString: + db "MET AT:@" + +MetUnknownMapString: + db "UNKNOWN@" + +MetAtLevelString: + db "MET LEVEL:@" +MetUnknownLevelString: + db "???@" ```