Skeetendo

’Cause all games were better on the GBC

You are not logged in.

#1 2017-11-08 00:04:04

Rangi
Member
Registered: 2016-05-09
Post 825/870

How to make bridges and other overhead tiles in pokecrystal

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?

q1htP8F.png

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.

Replace this:

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

with this:

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

with this:

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).

vjJGmWn.png

Have fun! :)

Last edited by Rangi (2017-11-11 23:30:45)


Pokémon Polished Crystal (GitHub) — version 2.2.0 released
Pokémon Red★ and Blue★: Space World Edition (GitHub) — updated August 19!
Polished Map: pokered+pokecrystal map, tileset, and palette editor — version 3.5.1 released!

Offline

Board footer

Powered by FluxBB