Option to show shiny colors in Pokédex - nickjwilde/pokecrystal GitHub Wiki
(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