-
Notifications
You must be signed in to change notification settings - Fork 993
Palette Tutorial: Restore cut Celadon SGB Rainbow Palette
The code in this tutorial comes from Vortyne, thanks! It teaches:
- How palettes and palette blocks work
- How to change palettes on a town-wide basis
- How to assign a certain palette to a certain part of the screen
- How to make a special palette block work for only one specific town in the game
This tutorial works for Pokeyellow as well as Pokered. If you find any errors or mistakes in this tutorial, feel free to correct them.
Pokemon Red, Blue, Green and Yellow are not GBC-exclusive games. This means that unlike Pokemon Gold, Silver and Crystal, they only have the capacity to load up to 4 palettes per screen, and can't assign one palette per tile (unless you make extensive changes to the code, such as in pokered-gbc).
The original source code of Pokemon Blue/Red by Gamefreak, contains a "rainbow" palette which splits the screen vertically into 4 different palettes (yellow, orange, green, blue). This was originally planned for Celadon City (which still has a motto, and badge, relating to rainbows in the final version of the game). It explains why Celadon is the only city to not have its own unique palette in the final game, instead sharing one with Viridian's. You can read more about it as well as watch a video here on TCRF: https://tcrf.net/Development:Pok%C3%A9mon_Red_and_Blue/Unused_Code#Overworld_Palette
This tutorial restores a working version of that palette in Super Gameboy mode, which ends up looking like the screenshot above.
The SGB can automatically refresh palette color dynamically as you walk, which is how this works. The GBC however, loads all the palettes used in a map at once as you enter the map, so the same code won't work for both - SGB mode will have a rainbow, while GBC mode has one single palette for the whole town (as in pokemon yellow).
This tutorial currently only has the SGB version working (feel free to edit it if you get something similar working on the GBC!). This tutorial also works on Pokeyellow.
FINDING COLORS
- Go to: pokered/data/sgb/sgb_packets.asm
The file is split into 2 parts, the first is SuperPalettes which is for use with the Super Gameboy. You can emulate these palettes by using the emulator BGB, right-clicking for Options, changing the console type to SGB, and then reloading the ROM.
The second section is GBCBasePalettes for Game Boy Color, which show up on any GBC emulator.
If we edit these, we edit the town colors in the game. Which one actually shows up depends on if you’re on a GBC or SGB.
1 palette is a group of 4 colors, each color is split into 2-digits each, where 0 means pure black and 31 means pure white (you can't go over 31 or under 0). Opposite to in other styles of coding which would be Green Blue Red, in Pokered these are in Red Green Blue (RGB) order, organized from left to right. Example:
(pure white - 31,31,31), (light color), (dark color), (pure black - 00,00,00)
Where “0, 0, 31” (R,G,B) would be “pure blue”.
Modern color picker tools use either Hexadecimal (0-15, where 0-9 is 0-9, but 10-15 is A-F) to show colors, or use RGB where colors are out of 0-255 instead of how the SGB/GBC has them, out of 0-31. This means even if you know what color you want to change a town to, you can't simply copy-paste it from your image editing program. There are plenty of online translators for Hex to RGB-225 but practically none for Hex or RGB-225 to RGB-31.
To translate RGB-225 to RGB-31 (SGB/GBC values): multiply by 31, then divide that by 225. If you want to, you can feed chatgpt instructions and a list of numbers instead of doing the math by hand.
All pokemon town maps start with white (31,31,30) and end with black (06,06,06), which leaves us with just the two colors in the middle to grab and change.
For example, using a color picker on the screenshot above, then doing the math from RGB-255 to RGB-31 for each quadrant, we get:
1:
Yellow: (hex) FCE17A, rgb(252, 225, 122), gbc(31, 27, 15)
Brown: C1A451, rgb(193, 164, 81), gbc(23, 20, 10)
2:
Orange: EDA85D, rgb(237,168,93), gbc(29, 20, 11)
Red: CF5835, rgb(207,88,53), gbc(25, 11, 6)
3:
Green: 96CB22, rgb(150,203,34), gbc(18, 25, 4)
Blue: A8D5EE, rgb(168,213,238), gbc(20, 26, 29)
4:
Light Blue: 9CA4D9, rgb(156,164,217), gbc(19, 20, 26)
Dark Blue: 637ABB, rgb(99,122,187), gbc(12, 15, 23)
However if you can, it is better to just relate them to the closest palettes that already exist in-game, which would mean that instead of the above, we could use:
Quadrant 1: Yellowmon: (white) (31,31,19), (28,23,09) (black)
2: Redmon: (31,24,11), (26,09,06)
3: Viridian City: (26,31,21), (23,27,31)
4: Bluemon: (21,22,31), (09,10,20)
INPUTTING COLORS
If we want to change the colors of a normal (non-rainbow) town it’s quite easy.
Just copy-paste into sgb_palettes.asm, where that town's palette is. For example:
- RGB 31,31,30, 28,27,31, 23,27,31, 06,06,06 ; PAL_PALLET
+ RGB 31,31,30, (new color 1 - replaces light grey), (new color 2 - replaces dark grey), 06,06,06 ; PAL_PALLET
You can preview palettes through Polished Map. Maps are loaded as blk files located in: pokeyellow (main folder) > maps. Click the lightbulb icon to open up a palette editor, so you can preview a palette to see if it looks correct.
PALETTE PACKETS
If you want multiple palettes per town or screen, you need to create and use a "palette packet" and "block" of palettes. This assigns x-y coordinates to the start and end of each palette, and assigns a series of palettes to be read in order. For example, palette packets are used in the battle screen to give a different color to the HP bar than to the pokemon and menu, and are also used for the slot machine screens.
A palette packet only lists which palettes are going to be used, and does not say WHERE on the screen they will be used. The format is this:
PalPacket_(Name): PAL_SET PAL_(name of palette loaded 1st), PAL_(2nd), PAL_(3rd), PAL_(4th)
If there is, for example, only 1 palette to be loaded, such as in normal town maps, the remaining 3 spots contain "0" instead of a palette name:
PalPacket_(Name): PAL_SET PAL_(name of palette loaded 1st), 0, 0, 0
Here is an example of one way for setting up the Celadon Rainbow code:
+PalPacket_Celadon: PAL_SET PAL_YELLOWMON, PAL_REDMON, PAL_ROUTE, PAL_BLUEMON
PACKET BLOCKS
Now we need to assign where on the screen each of the 4 palettes assigned to PalPacket_Celadon appear, by telling the computer x-y coordinates to create rectangles.
Go to: pokered (main folder) /data/sgb/sgb_packets.asm:
We already have an example of one area using multiple palettes, which is the Rainbow Badge:
ATTR_BLK_DATA %010, 2,2,0, 16,11, 17,12 ; Rainbow Badge color 1
ATTR_BLK_DATA %010, 1,1,0, 14,13, 15,13 ; Rainbow Badge color 2
ATTR_BLK_DATA %010, 3,3,0, 16,13, 17,13 ; Rainbow Badge color 3
According to this info: https://gbdev.gg8.se/wiki/articles/SGB_Functions#SGB_Command_04h_-_ATTR_BLK
ATTR_BLK = assigns colors to either the inside or outside of an area of the SGB screen.
ATTR_BLK 4 = 4 is the number of palettes the screen will be split into
ATTR_BLK_DATA = assigns a palette to a rectangular section of the SGB screen.
The number with % is the "control code" controlling which aspects get painted a color, written in binary digits. I didn't understand this part from the GB documentation files above, but the following is what I was told in the pokered discord. Feel free to edit this if you understand it:
```diff
Bit 0 > %001 - Change colors inside of surrounded area (1=yes, 0=no)
Bit 1 > %010 - Change colors of surrounding character line (1=yes, 0=no)
Bit 2 > %011 - Change colors outside of surrounding area (1=yes, 0=no)
0,0,0 = assign this info to the first palette listed in PalPacket_(NAME)
1,1,0 = assign to the second palette... etc
0,0,0 00,00 04,17 = assign the 1st palette, starting at x-y coordinates (00,00), ending at coordinates (04,17). In other words, roughly a 4th of the screen vertically, as there are 20 tiles per the screen.
Here is the Celadon code to input anywhere in the file, that splits the screen into 4 equal vertical sections:
+BlkPacket_Celadon:
+ ATTR_BLK 4
+ ATTR_BLK_DATA %011, 0,0,0, 00,00, 04,17 ; left: pal 1
+ ATTR_BLK_DATA %011, 1,1,0, 05,00, 09,17 ; center left: pal 2
+ ATTR_BLK_DATA %011, 2,2,0, 10,00, 14,17 ; center right: pal 3
+ ATTR_BLK_DATA %011, 3,3,0, 15,00, 19,17 ; right: pal 4
+ ds 2, 0
ds 2, 0 = "pads out" or sets unused bytes at the end of the last packet to zero, as per SGB recommendations.
Now we need to tell the game which town to apply these palette changes to.
Go to: engine/gfx/palettes.asm
The following code may only work properly in pokered. Separate code proven to work in pokeyellow is below.
SetPal_Overworld:
+ ld a, [wLastMap]
+ cp CELADON_CITY ; check if the map ID s CELADON_CITY
+ jr z, .celadon ; if yes, jump to .celadon routine
ld hl, PalPacket_Empty
ld de, wPalPacket
ld bc, $10
call CopyData
ld a, [wCurMapTileset]
cp CEMETERY
...
.trade_center_colosseum
ld a, PAL_GREYMON - 1
jr .town
; celadon test
+.celadon
+ ld hl, PalPacket_Celadon
+ ld de, wPalPacket
+ ld bc, $10
+ call CopyData
+ ld hl, PalPacket_Celadon
+ ld de, BlkPacket_Celadon
+ ld a, SET_PAL_OVERWORLD
+ ld [wDefaultPaletteCommand], a
+ ret
+; celadon test
In yellow, the above code may make ALL towns and routes have the rainbow layout instead of just Celadon. The following code is proven to work in Pokeyellow, and sets only the main town (not the buildings inside it, which will use the usual palette assigned to that town) to a rainbow color:
SetPal_Overworld:
- ld hl, PalPacket_Empty
- ld de, wPalPacket
- ld bc, $10
- call CopyData
- ld a, [wCurMapTileset]
+; celadon rainbow code by Vortiene
+ ld a, [wCurMap]
+ cp CELADON_CITY ; Map ID name. change this to whatever map you want rainbow to be
+ jr nz, .notCeladon ; jump to "skip Celadon effect if not in Celadon" code
+ ld hl, PalPacket_Celadon
+ ld de, wPalPacket
+ ld bc, $10
+ call CopyData
+ ld hl, PalPacket_Celadon
+ ld de, BlkPacket_Celadon
+ ld a, SET_PAL_OVERWORLD
+ ld [wDefaultPaletteCommand], a
+ ret
+; Celadon rainbow effect ends here
+.notCeladon
cp CEMETERY
jr z, .PokemonTowerOrAgatha
Enjoy your rainbows! Remember, this tutorial only works if you're in SGB mode, it will not work for GBC mode.