Option to show shiny colors in Pokédex - nickjwilde/pokecrystal GitHub Wiki

Screenshot

(The code for this feature was adapted from Crystal Clear.)

This implementation requires at least one WRAM byte. We'll also be using a single bit in order to handle the current status, leaving 7 more free for potential future features.

Fortunately, there is space for this in the existing Pokédex struct. For posterity's sake, we'll also clean up the related 1.1 differences. You can quickly find these two spots in wram.asm by searching for _CRYSTAL11 in your editor of choice.

Edit wram.asm:


wBackupDexListingCursor:: db
wBackupDexListingPage:: db
wDexCurLocation:: db
-if DEF(_CRYSTAL11)
wPokedexStatus:: db
+wPokedexShinyToggle::
+; bit 0: set if displaying shiny palettes
+	db
wPokedexDataEnd::
-else
-wPokedexDataEnd:: ds 1
-endc
-	ds 2
+	ds 1

NEXTU ; c6d0

Since we removed the 1.1 differences in the first section, we'll want to do the same here.

Edit wram.asm:


NEXTU ; cf64
; pokedex
wPrevDexEntryJumptableIndex:: db
-if DEF(_CRYSTAL11)
wPrevDexEntryBackup:: db
-else
-wPrevDexEntryBackup::
-wPokedexStatus:: db
-endc

NEXTU ; cf64

Now that we've got a byte, we need to locate where the engine loads the selected Pokémon palette. Inspecting engine/pokedex/pokedex.asm reveals the SCGB_POKEDEX constant, so we'll need to modify the cooresponding CGB layout.

Within _CGB_Pokedex, we can see that is uses the function GetMonPalettePointer, in engine/gfx/color.asm. Simply put, this function returns the pointer of a corresponding Pokémon palette in register hl, based on the species value in register a.

The way that palette data is aligned within Pokémon Crystal is convenient. Each Pokémon's default palette is 4 bytes, and the next 4 bytes after are the shiny palette.

With this in mind, the following implementation should be easier to understand. Whenever _CGB_Pokedex is called, the default palette is always loaded. Then, we check bit 0 of wPokedexShinyToggle, and if it's not set (jr z), we jump ahead to loading the palette at hl. But if it is set, we'll increment the value at hl 4 times, which will put us at the shiny palette!

Edit _CGB_Pokedex in engine/gfx/cgb_layouts.asm:


_CGB_Pokedex:
	ld de, wBGPals1
	ld a, PREDEFPAL_POKEDEX
	call GetPredefPal
	call LoadHLPaletteIntoDE ; dex interface palette
	ld a, [wCurPartySpecies]
	cp $ff
	jr nz, .is_pokemon
	ld hl, .PokedexQuestionMarkPalette
	call LoadHLPaletteIntoDE ; green question mark palette
	jr .got_palette

.is_pokemon
	call GetMonPalettePointer
+	ld a, [wPokedexShinyToggle]
+	bit 0, a
+	jr z, .not_shiny
+	inc hl
+	inc hl
+	inc hl
+	inc hl
+.not_shiny
	call LoadPalette_White_Col1_Col2_Black ; mon palette
.got_palette

Now we have a WRAM byte, and code that uses it. All that's left is a way to set and clear the bit we're using.

The quickest method is to take advantage of the fact that within the Pokédex, the SELECT button is unused when viewing a Pokédex entry. We can see that the joypad handling for this screen is in Pokedex_UpdateDexEntryScreen. We'll want to add the SELECT button handling first.

Edit Pokedex_UpdateDexEntryScreen in engine/pokedex/pokedex.asm:


Pokedex_UpdateDexEntryScreen:
	ld de, DexEntryScreen_ArrowCursorData
	call Pokedex_MoveArrowCursor
	ld hl, hJoyPressed
	ld a, [hl]
	and B_BUTTON
	jr nz, .return_to_prev_screen
	ld a, [hl]
	and A_BUTTON
	jr nz, .do_menu_action
+	ld a, [hl]
+	and SELECT
+	jr nz, .toggle_shininess
	call Pokedex_NextOrPreviousDexEntry
	ret nc
	call Pokedex_IncrementDexPointer
	ret

Then, at the end of the function, we'll add some code. First, we want to toggle the setting. So we check the wPokedexShinyToggle bit, and if it's not set, we'll set it and update the palettes. Otherwise if it's set, we'll clear the bit before updating the palettes.

Edit Pokedex_UpdateDexEntryScreen in engine/pokedex/pokedex.asm:


.max_volume
	call MaxVolume
	ld a, [wPrevDexEntryJumptableIndex]
	ld [wJumptableIndex], a
	ret

+.toggle_shininess
+; toggle the current shininess setting
+	ld hl, wPokedexShinyToggle
+	bit 0, [hl]
+	jr z, .set
+	; already set, so clear it
+	res 0, [hl]
+	jr .update_palettes
+.set ; bit is not set, so set it
+	set 0, [hl]
+.update_palettes
+; refresh palettes
+	ld a, SCGB_POKEDEX
+	call Pokedex_GetSGBLayout	
+; play sound based on setting
+	ld de, SFX_BUMP
+	ld a, [wPokedexShinyToggle]
+	bit 0, a
+	jr z, .got_sound
+	ld de, SFX_SHINE
+.got_sound
+	call PlaySFX
+	jp WaitSFX

While not strictly a requirement, there's some additional code here for some audio feedback when the SELECT button is pressed, based on the setting. You're free to use any SFX that you like, or if you want none at all, you can do this:


.update_palettes
; refresh palettes
	ld a, SCGB_POKEDEX
-	call Pokedex_GetSGBLayout
-; play sound based on setting
-	ld de, SFX_BUMP
-	ld a, [wPokedexShinyToggle]
-	bit 0, a
-	jr z, .got_sound
-	ld de, SFX_SHINE
-.got_sound
-	call PlaySFX
-	jp WaitSFX
+	jp Pokedex_GetSGBLayout