You are not logged in.
Throughout Pokémon Crystal, there's a rule: sprites go on top of tiles. But there are exceptions: grass tiles overlap you as you walk, and the popup signs with location names appear above all the NPCs. But the grass tiles depend on using collision IDs $18 or $14, and the popup signs have their own special code. Is it possible to have a simple system for making any tile appear above sprites?
It is. They key is in gbhw.asm:
; OAM attribute flags OAM_PALETTE EQU %111 OAM_TILE_BANK EQU 3 OAM_OBP_NUM EQU 4 ; Non CGB Mode Only OAM_X_FLIP EQU 5 OAM_Y_FLIP EQU 6 OAM_PRIORITY EQU 7 ; 0: OBJ above BG, 1: OBJ behind BG (colors 1-3)
Every tile on the screen has an attribute byte. The lowest three bits define the color, which is why there's only room for eight colors (from PAL_BG_GRAY, 0, to PAL_BG_TEXT, 7). The other bits control other properties. In particular, the high bit controls tile priority. So if the tilesets/*_palette_map.asm files could define tile priority as well as color, you could make any tile have priority over sprites.
There's one problem: the tilepal macro packs two tile color definitions into each byte, using four bits per tile: three for the color (OAM_PALETTE), one for the bank (OAM_TILE_BANK). So we need to add space for more data.
Step 1: Add these definitions to constants/tilemap_constants.asm:
const_value SET $80 const PAL_BG_PRIORITY_GRAY ; 80 const PAL_BG_PRIORITY_RED ; 81 const PAL_BG_PRIORITY_GREEN ; 82 const PAL_BG_PRIORITY_WATER ; 83 const PAL_BG_PRIORITY_YELLOW ; 84 const PAL_BG_PRIORITY_BROWN ; 85 const PAL_BG_PRIORITY_ROOF ; 86 const PAL_BG_PRIORITY_TEXT ; 87
Step 2: Edit macros/pals.asm.
tilepal: MACRO ; vram bank, pals x = \1 << 3 rept (_NARG +- 1) / 2 dn (x | PAL_BG_\3), (x | PAL_BG_\2) shift shift endr endm
tilepal: MACRO ; vram bank, pals x = \1 << 3 rept _NARG +- 1 db (x | PAL_BG_\2) shift endr endm
Now the tileset palette data will take up twice as much space—one byte per tile instead of half a byte—but you'll be able to use, for example, PRIORITY_GRAY instead of GRAY to define an overhead tile.
Step 3: Edit main.asm's SwapTextboxPalettes and ScrollBGMapPalettes.
These two routines read the tileset palette data, so they have to be updated to understand the new one-byte-per-tile format. While we're at it, we can also factor out a large chunk of identical code from both of them into its own subroutine.
(Here's a side-by-side comparison of the two routines. They both initialize hl to point to some tile IDs and de to point to some tile attributes, then they read tile IDs from hl, get the corresponding palette attributes from TilesetPalettes, and store them in de. The main difference is that SwapTextboxPalettes also initializes bc such that it updates the whole screen's palette data, whereas ScrollBGMapPalettes assumes that its caller already set c to the number of tiles that need updating.)
Anyway, replace all of this:
SwapTextboxPalettes:: ; 4c000 hlcoord 0, 0 decoord 0, 0, AttrMap ... ret ScrollBGMapPalettes:: ; 4c03f ld hl, BGMapBuffer ld de, BGMapPalBuffer ... ret
SwapTextboxPalettes:: ; 4c000 hlcoord 0, 0 decoord 0, 0, AttrMap ld b, SCREEN_HEIGHT .loop push bc ld c, SCREEN_WIDTH call GetBGMapTilePalettes pop bc dec b jr nz, .loop ret ScrollBGMapPalettes:: ; 4c03f ld hl, BGMapBuffer ld de, BGMapPalBuffer ; don't call GetBGMapTilePalettes and ret, just fallthrough GetBGMapTilePalettes: .loop ld a, [hl] push hl ld hl, TilesetPalettes add [hl] ld l, a ld a, [TilesetPalettes + 1] adc $0 ld h, a ld a, [hl] pop hl ld [de], a res 7, [hl] inc hl inc de dec c jr nz, .loop ret
(Notice how the common code has been moved into GetBGMapTilePalettes, and then the whole decision of which nybble to read is no longer necessary because the whole byte is needed.)
Anyway—at this point you are done! Now when you edit a palette_map.asm file, you can use the names PRIORITY_GRAY, PRIORITY_BROWN, etc instead of just GRAY, BROWN, etc, and the tile will appear above any NPC. However, the lightest hue (that is, white when you're editing the monochrome tileset graphic) will be transparent. That's how tall grass works: you see only the parts of the player sprite that overlap "white" pixels (actually light green, using the standard outdoor color palette.) You'll notice in the Magnet Train example above, I designed the overhead tracks to use only the three darker hues.
(Note that the Polished Map editor understands the PRIORITY_* color names as of version 2.1.3.)
There's one property of bridges I haven't discussed here: how is it possible to walk on top of them as well as below them? I'm not writing up a complete guide to that right now, but the short answer is "have pairs of identical bridge tiles with normal colors and PRIORITY colors, use them to assemble identical-looking metatiles, give the overhead metatiles collision data based on whatever's supposed to be underneath them, and use xy_triggers with changeblock commands followed by a refreshscreen command to swap in the appropriate blocks (this will lag a bit when you step on them)." Here's a video of the end result (apologies for the low quality).
Have fun! :)
Last edited by Rangi (2017-11-11 23:30:45)
Hi Rangi! This whole overhead and priority stuff has been very useful to make some really cool graphical artifacts.
However, the lightest hue (that is, white when you're editing the monochrome tile-set graphic) will be transparent.
I would actually like to know a way to change this behavior based on a condition (like the current loaded map). For example make it so every color except black is transparent.
I've looked through a ton of files and I think the key code that handles this may be in the engine\overworld\map_objects.asm file. However I'm not able to pinpoint the specific lines that take care of this, any help?
Huge thanks in advance!
That's not controlled by a file; it's fundamental to the Game Boy hardware.