summaryrefslogtreecommitdiff
path: root/Use-GS-SGB-palettes-for-maps.md
blob: 27c4d064922a62a2e45ee1dd3d0b06832094d233 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
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](#1-comparison-of-dmg-sgb-and-cgb-colors)
2. [Load SGB palettes for overworld maps](#2-load-sgb-palettes-for-overworld-maps)
3. [Make water, flower, and cave entrance tiles look like SGB](#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](screenshots/gold-dmg-overworld.png)

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](screenshots/gold-sgb-overworld.png)

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

![Screenshot](screenshots/gold-sgb-battle.png)

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

![Screenshot](screenshots/crystal-cgb-overworld.png)

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](screenshots/crystal-gbc-only.png)

Despite only supporting the CGB, Crystal still has leftover SGB palette data from G/S. The [gfx/sgb/predef.pal](../blob/master/gfx/sgb/predef.pal) table defines most of the individual palettes, which were selectively loaded with the `GetPredefPal` routine in [engine/gfx/color.asm](../blob/master/engine/gfx/color.asm) and applied to regions of the screen by `LoadSGBLayout` in [engine/gfx/sgb_layouts.asm](../blob/master/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](../blob/master/engine/gfx/cgb_layouts.asm):

```diff
 _CGB_MapPals:
-	call LoadMapPals
+; Get SGB palette
+	call LoadSGBLayout.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 [wSGBPredef], 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](../blob/master/engine/gfx/color.asm) was responsible for loading the palettes from [gfx/tilesets/bg_tiles.pal](../blob/master/gfx/tilesets/bg_tiles.pal) and [gfx/overworld/npc_sprites.pal](../blob/master/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](../blob/master/engine/tilesets/tileset_palettes.asm) to load new unique palettes like [gfx/tilesets/ice_path.pal](../blob/master/gfx/tilesets/ice_path.pal).)

Anyway, we've replaced all of that with a call to `LoadSGBLayout.GetMapPalsIndex` in [engine/gfx/sgb_layouts.asm](../blob/master/engine/gfx/sgb_layouts.asm), which gets a single palette from [gfx/sgb/predef.pal](../blob/master/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](../blob/master/engine/tilesets/tileset_anims.asm):

```diff
 AnimateFlowerTile:
 ; No parameters.

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

 ; Alternate tile graphic every other frame
 	ld a, [wTileAnimationTimer]
 	and %10
-
-; CGB has different color mappings for flowers.
-	ld e, a
-	ldh a, [hCGB]
-	and 1
-	add e
-
 	swap a
 	ld e, a
 	ld d, 0
 	ld hl, FlowerTileFrames
 	add hl, de
 	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](#1-comparison-of-dmg-sgb-and-cgb-colors), the waving flowers have different shading.

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

 ; No palette changes on DMG.
-	ldh a, [hCGB]
-	and a
-	ret z
-
-; We don't want to mess with non-standard palettes.
-	ldh a, [rBGP] ; BGP
-	cp %11100100
-	ret nz
-
-; Only update on even frames.
-	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
-	ldh [rBGPI], a
-
-	ldh a, [rSVBK]
-	push af
-	ld a, BANK(wBGPals1)
-	ldh [rSVBK], a
-
-; Update color 0 in order 0 1 2 1
-	ld a, l
-	and %110 ; frames 0 2 4 6
-	jr z, .color0
-	cp %100 ; frame 4
-	jr z, .color2
-
-.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.

```diff
 FlickeringCaveEntrancePalette:
 ; No palette changes on DMG.
-	ldh a, [hCGB]
-	and a
-	ret z
-; We don't want to mess with non-standard palettes.
-	ldh a, [rBGP]
-	cp %11100100
-	ret nz
-; We only want to be here if we're in a dark cave.
-	ld a, [wTimeOfDayPalset]
-	cp %11111111 ; 3,3,3,3
-	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
-	ldh [rBGPI], a
-	ldh a, [hVBlankCounter]
-	and %10
-	jr nz, .bit1set
-	ld hl, wBGPals1 palette PAL_BG_YELLOW
-	jr .okay
-
-.bit1set
-	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](screenshots/sgb-map-palettes.png)

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)