Use GS SGB palettes for maps - pret/pokecrystal GitHub Wiki

The Super Game Boy (SGB) was an adapter that let you play Game Boy games on a SNES, taking advantage of the console's hardware to add more colors than a Game Boy (DMG), but fewer than a Game Boy Color (CGB). Pokémon Gold and Silver supported all three systems, but Crystal was GBC-only. However, it has leftover unused code from G/S for SGB colors. This tutorial will apply the SGB colors to overworld maps, giving them a "retro" look.

Contents

  1. Comparison of DMG, SGB, and CGB colors
  2. Load SGB palettes for overworld maps
  3. Make water, flower, and cave entrance tiles look like SGB

1. Comparison of DMG, SGB, and CGB colors

A palette consists of four shades: white, light, dark, and black.

The DMG (from the Game Boy's internal codename "Dot Matrix Game") was monochrome; its games had no concept of color, and the display hardware itself was a greenish monochrome. However, it supported three shade assignments at once: BGP for background tiles, and OBP0 and OBP1 for objects (aka sprites). G/S were designed to look good even with these limitations.

Screenshot

The SGB supported four different palettes at once, and was backwards-compatible with DMG shade assignments. G/S, like Yellow, took advantage of this to give each city its own colors, as well as unique colors for different interfaces like the Pokédex, slot machines, and so on.

Screenshot

As you can see, the SGB also supported decorative borders, since a TV screen had enough space to spare for one.

Screenshot

The CGB supported sixteen palettes: eight usable by background tiles, and eight by objects.

Screenshot

It also doubled the amount of video RAM, which Crystal needed for its extra graphics (like improved maps and in-battle sprite animations). That's why Crystal couldn't have supported the DMG or SGB:

Screenshot

Despite only supporting the CGB, Crystal still has leftover SGB palette data from G/S. The gfx/sgb/predef.pal table defines most of the individual palettes, which were selectively loaded with the GetPredefPal routine in engine/gfx/color.asm and applied to regions of the screen by LoadSGBLayout in engine/gfx/sgb_layouts.asm. (Since the SGB used an SNES and TV screen to display the game, that data was sent in "packets" from the Game Boy to the SNES for processing.)

With that background knowledge, we can apply some SGB palettes to Crystal. It won't literally be playable on a Super Game Boy, but it will look like one. (At least the overworld maps will.)

2. Load SGB palettes for overworld maps

Edit engine/gfx/cgb_layouts.asm:

 _CGB_MapPals:
-	call LoadMapPals
+; Get SGB palette
+	call SGBLayoutJumptable.GetMapPalsIndex
+	call GetPredefPal
+	ld de, wBGPals1
+; Copy 7 BG palettes
+	ld b, 7
+.bg_loop
+	call .LoadHLBGPaletteIntoDE
+	dec b
+	jr nz, .bg_loop
+; Copy PAL_BG_TEXT and 6 OB palettes
+	ld b, 7
+.ob_loop
+	call .LoadHLOBPaletteIntoDE
+	dec b
+	jr nz, .ob_loop
+; Copy PAL_OW_TREE and PAL_OW_ROCK
+	call .LoadHLBGPaletteIntoDE
+	call .LoadHLBGPaletteIntoDE
 	ld a, SCGB_MAPPALS
 	ld [wDefaultSGBLayout], a
 	ret
+
+.LoadHLBGPaletteIntoDE:
+; morn/day: shades 0, 1, 2, 3 -> 0, 1, 2, 3
+; nite: shades 0, 1, 2, 3 -> 1, 2, 2, 3
+	push hl
+	ld a, [wTimeOfDayPal]
+	cp NITE_F
+	jr c, .bg_morn_day
+	inc hl
+	inc hl
+	call .LoadHLColorIntoDE
+	call .LoadHLColorIntoDE
+	dec hl
+	dec hl
+	call .LoadHLColorIntoDE
+	call .LoadHLColorIntoDE
+.bg_done
+	pop hl
+	ret
+
+.bg_morn_day
+	call LoadHLPaletteIntoDE
+	jr .bg_done
+
+.LoadHLOBPaletteIntoDE:
+; shades 0, 1, 2, 3 -> 0, 0, 1, 3
+	push hl
+	call .LoadHLColorIntoDE
+	dec hl
+	dec hl
+	call .LoadHLColorIntoDE
+	call .LoadHLColorIntoDE
+	inc hl
+	inc hl
+	call .LoadHLColorIntoDE
+	pop hl
+	ret
+
+.LoadHLColorIntoDE:
+	ldh a, [rSVBK]
+	push af
+	ld a, BANK(wBGPals1)
+	ldh [rSVBK], a
+rept PAL_COLOR_SIZE
+	ld a, [hli]
+	ld [de], a
+	inc de
+endr
+	pop af
+	ldh [rSVBK], a
+	ret

The LoadMapPals routine in engine/gfx/color.asm was responsible for loading the palettes from gfx/tilesets/bg_tiles.pal and gfx/overworld/npc_sprites.pal based on the environment and time of day. (Crystal also made it call LoadSpecialMapPalette in engine/tilesets/tileset_palettes.asm to load new unique palettes like gfx/tilesets/ice_path.pal.)

Anyway, we've replaced all of that with a call to SGBLayoutJumptable.GetMapPalsIndex in engine/gfx/sgb_layouts.asm, which gets a single palette from gfx/sgb/predef.pal based on the environment and time of day. The rest of the code is applying the colors for that one palette to all sixteen CGB palettes, giving different shade assignments to background tiles and to objects.

3. Make water, flower, and cave entrance tiles look like SGB

Edit engine/tilesets/tileset_anims.asm:

 AnimateFlowerTile:
 ; Save the stack pointer in bc for WriteTile to restore

 ; Save sp in bc (see WriteTile).
 	ld hl, sp+0
 	ld b, h
 	ld c, l

 ; A cycle of 2 frames, updating every other tick
 	ld a, [wTileAnimationTimer]
 	and %10

-; CGB has different tile graphics for flowers
-	ld e, a
-	ldh a, [hCGB]
-	and 1
-	add e
-
 ; hl = .FlowerTileFrames + a * 16
 	swap a
 	ld e, a
 	ld d, 0
 	ld hl, .FlowerTileFrames
 	add hl, de
 
 ; Write the tile graphic from hl (now sp) to tile $03 (now hl)
 	ld sp, hl
 	ld hl, vTiles2 tile $03
 	jp WriteTile
 
 .FlowerTileFrames:
 	INCBIN "gfx/tilesets/flower/dmg_1.2bpp"
 	INCBIN "gfx/tilesets/flower/cgb_1.2bpp"
 	INCBIN "gfx/tilesets/flower/dmg_2.2bpp"
 	INCBIN "gfx/tilesets/flower/cgb_2.2bpp"

As you can see in the screenshots in step 1, the waving flowers have different shading.

 AnimateWaterPalette:
 ; Transition between color values 0-2 for color 0 in palette 3.

 ; Don't update the palette on DMG
-	ldh a, [hCGB]
-	and a
-	ret z
-
-; Don't update a non-standard palette order
-	ldh a, [rBGP]
-	cp %11100100
-	ret nz
-
-; Only update on even ticks
-	ld a, [wTileAnimationTimer]
-	ld l, a
-	and 1 ; odd
-	ret nz
-
-; Ready for BGPD input
-	ld a, (1 << rBGPI_AUTO_INCREMENT) palette PAL_BG_WATER color 0
-	ldh [rBGPI], a
-
-	ldh a, [rSVBK]
-	push af
-	ld a, BANK(wBGPals1)
-	ldh [rSVBK], a
-
-; A cycle of 4 colors (0 1 2 1), updating every other tick
-	ld a, l
-	and %110
-	jr z, .color0
-	cp %100
-	jr z, .color2
-
-; Copy one color from hl to rBGPI via rBGPD
-
-; color1
-	ld hl, wBGPals1 palette PAL_BG_WATER color 1
-	ld a, [hli]
-	ldh [rBGPD], a
-	ld a, [hli]
-	ldh [rBGPD], a
-	jr .end
-
-.color0
-	ld hl, wBGPals1 palette PAL_BG_WATER color 0
-	ld a, [hli]
-	ldh [rBGPD], a
-	ld a, [hli]
-	ldh [rBGPD], a
-	jr .end
-
-.color2
-	ld hl, wBGPals1 palette PAL_BG_WATER color 2
-	ld a, [hli]
-	ldh [rBGPD], a
-	ld a, [hli]
-	ldh [rBGPD], a
-
-.end
-	pop af
-	ldh [rSVBK], a
 	ret

CGB animated the "white" shade of PAL_BG_WATER, cycling it between the white, light, and dark shades. This was arguably a design flaw: it prevents that palette from being used for general-purpose blue things, and when it cycles to the dark shade, water ends up looking like solid dark blue. Here we've fixed the problem.

 FlickeringCaveEntrancePalette:
 ; Don't update the palette on DMG
-	ldh a, [hCGB]
-	and a
-	ret z
-
-; Don't update a non-standard palette order
-	ldh a, [rBGP]
-	cp %11100100
-	ret nz
-
-; We only want to be here if we're in a dark cave.
-	ld a, [wTimeOfDayPalset]
-	cp DARKNESS_PALSET
-	ret nz
-
-	ldh a, [rSVBK]
-	push af
-	ld a, BANK(wBGPals1)
-	ldh [rSVBK], a
-
-; Ready for BGPD input
-	ld a, (1 << rBGPI_AUTO_INCREMENT) palette PAL_BG_YELLOW color 0
-	ldh [rBGPI], a
-
-; A cycle of 2 colors (0 2), updating every other vblank
-	ldh a, [hVBlankCounter]
-	and %10
-	jr nz, .color1
-
-; Copy one color from hl to rBGPI via rBGPD
-
-; color0
-	ld hl, wBGPals1 palette PAL_BG_YELLOW color 0
-	jr .okay
-
-.color1
-	ld hl, wBGPals1 palette PAL_BG_YELLOW color 1
-
-.okay
-	ld a, [hli]
-	ldh [rBGPD], a
-	ld a, [hli]
-	ldh [rBGPD], a
-
-	pop af
-	ldh [rSVBK], a
 	ret

CGB also animated PAL_BG_YELLOW inside of dark caves, giving their entrances a flickering look. This would not have been possible on the SGB, since every tile used the same BGP palette. For a more accurate retro look, we've disabled it.

That's all it takes to make the overworld maps look like an SGB game:

Screenshot

Adapting more SGB palettes—the Pokédex, slot machines, Pokégear, trainer card, and so on—is left as an exercise for the reader. ;)

TODO: apply other overworld SGB palettes (heal machine anim, battle transition, poision step, etc)