The Room Graph
One room is a picture; a keep is many pictures and a sense of which lies through each door. Build the room table — each room's map plus its four edge-links — and let the thief walk through a doorway into a second room.
A keep is more than one room, and the rooms know each other: north of this hall lies that one, east lies another. This unit gives the keep that structure — a graph of rooms — and lets the thief travel it. It's the map of the map: the data that says how the pictures from Unit 4 join up.
What you'll see by the end
Walk the thief east, out through the doorway in the pillared hall's right-hand wall, and the screen flicks to a new room — a plainer chamber with a block of its own and a doorway back the way you came. Walk west and you return. Two rooms, joined, and a thief who travels between them. The keep has become a place with elsewhere in it.
A room, and its neighbours
In Unit 4 a room was a map pointer. Now each room is a small table entry: that map pointer, followed by four links — the room that lies North, South, East and West — or $FF for "solid wall, no way through":
rooms:
defw room0_data
defb NO_EXIT, NO_EXIT, 1, NO_EXIT ; east -> room 1
defw room1_data
defb NO_EXIT, NO_EXIT, NO_EXIT, 0 ; west -> room 0
Two rooms, joined east-west. Room 0's east link is 1; room 1's west link is 0. That pair of numbers is the connection — the whole graph in eight bytes. A current_room byte says which one we're in, and draw_room now draws whichever that is, fetching its map from the table:
room_entry_addr: ; HL = rooms + current_room * 6
ld a, (current_room)
; ... * 6 (each entry is a 2-byte pointer + 4 link bytes) ...
ld de, rooms
add hl, de
ret
A doorway is a gap
How does the thief leave? Through a doorway — and a doorway is nothing more than a . where the wall would be a #. Room 0's east wall has one floor cell, at row 11; room 1's west wall has one too. Because a doorway is dim floor, not bright wall, the thief can walk onto it — collision already lets him.
The moment he stands on an edge cell, we look up that edge's link and travel:
check_exit:
ld a, (thief_col)
or a
jr z, .west ; on column 0?
cp 31
jr z, .east ; on column 31?
; ... row 0 -> north, row 23 -> south ...
ret ; not on an edge
Each branch reads its link from the room table and, if it isn't $FF, travels there:
.travel:
cp NO_EXIT
ret z ; a doorway to nowhere — stay
ld (current_room), a
call draw_room ; repaint the whole screen as the new room
ld a, START_COL ; drop him in the middle (for now)
ld (thief_col), a
; ...
The flick, and an honest seam
Travelling repaints the entire screen — all 768 cells — as the new room. That repaint takes a handful of frames, so you see it happen: a brief flick as the keep redraws. That's not a flaw to hide; it's the sound of the genre. Atic Atac, Sabre Wulf, Knight Lore — every flick-screen game paused to redraw as you crossed a threshold. Ours is in good company.
There's one rough edge, though, and it's deliberate. When the thief travels, we drop him in the centre of the new room — a small teleport. Walk east through a door and you ought to arrive at the west edge of the next room, mid-stride, as if you'd stepped through. Right now you appear in the middle instead. It works — you can roam the whole keep — but it doesn't yet feel like a doorway. This unit is the map; making the step feel like a step is the next one.
Milestone — a graph of rooms
We turn the single map into a room table — each entry a map pointer plus four links (N/S/E/W, or NO_EXIT) — add a current_room, and have check_exit read the link of whatever edge the thief stands on and travel there, repainting the screen as the new room.
| 1 | 1 | ; Shadowkeep — Unit 5: The Room Graph | |
| 2 | 2 | ; Cumulative build; every step runs on its own. Narrative: the unit page. | |
| 3 | - | ; step-00 = Unit 4's end: the thief in a single data-driven pillared hall. | |
| 3 | + | ; step-01 gives the keep a graph of rooms — walk a doorway, flick to the next. | |
| 4 | 4 | | |
| 5 | 5 | org 32768 | |
| 6 | 6 | | |
| 7 | - | WALL_ATTR equ %01001000 ; BRIGHT — lit stone (solid) | |
| 8 | - | FLOOR_ATTR equ %00001000 ; dim — dark slate (walkable) | |
| 9 | - | THIEF equ %01001010 ; BRIGHT, PAPER 1 (blue), INK 2 (red) | |
| 7 | + | WALL_ATTR equ %01001000 | |
| 8 | + | FLOOR_ATTR equ %00001000 | |
| 9 | + | THIEF equ %01001010 | |
| 10 | 10 | WALL_BIT equ 6 | |
| 11 | 11 | | |
| 12 | 12 | START_COL equ 15 | |
| 13 | 13 | START_ROW equ 11 | |
| 14 | + | NO_EXIT equ $FF | |
| 14 | 15 | | |
| 15 | 16 | KEYS_OP equ $DFFE | |
| 16 | 17 | KEYS_Q equ $FBFE | |
| ... | |||
| 23 | 24 | ld a, 0 | |
| 24 | 25 | out ($FE), a | |
| 25 | 26 | | |
| 27 | + | xor a | |
| 28 | + | ld (current_room), a | |
| 26 | 29 | ld a, START_COL | |
| 27 | 30 | ld (thief_col), a | |
| 28 | 31 | ld a, START_ROW | |
| ... | |||
| 40 | 43 | jr .loop | |
| 41 | 44 | | |
| 42 | 45 | ; ---------------------------------------------------------------------------- | |
| 43 | - | ; draw_room — walk the map cell by cell, look each glyph up in the palette, | |
| 44 | - | ; draw the tile it names. The map pointer is kept in a variable because | |
| 45 | - | ; draw_tile uses HL for itself. | |
| 46 | + | ; room_entry_addr — HL = rooms + current_room * 6 (entry = map ptr + 4 links). | |
| 47 | + | ; ---------------------------------------------------------------------------- | |
| 48 | + | room_entry_addr: | |
| 49 | + | ld a, (current_room) | |
| 50 | + | ld l, a | |
| 51 | + | ld h, 0 | |
| 52 | + | ld d, h | |
| 53 | + | ld e, l ; DE = room number | |
| 54 | + | add hl, hl ; *2 | |
| 55 | + | add hl, de ; *3 | |
| 56 | + | add hl, hl ; *6 | |
| 57 | + | ld de, rooms | |
| 58 | + | add hl, de | |
| 59 | + | ret | |
| 60 | + | | |
| 61 | + | ; ---------------------------------------------------------------------------- | |
| 62 | + | ; draw_room — draw the current room's map. Same walk as Unit 4, but the map | |
| 63 | + | ; pointer comes from the room table now, not a fixed label. | |
| 46 | 64 | ; ---------------------------------------------------------------------------- | |
| 47 | 65 | draw_room: | |
| 48 | - | ld hl, room_data | |
| 66 | + | call room_entry_addr | |
| 67 | + | ld a, (hl) ; map pointer, low | |
| 68 | + | inc hl | |
| 69 | + | ld h, (hl) ; map pointer, high | |
| 70 | + | ld l, a | |
| 49 | 71 | ld (map_ptr), hl | |
| 50 | 72 | ld b, 0 | |
| 51 | 73 | .room_row: | |
| 52 | 74 | ld c, 0 | |
| 53 | 75 | .room_col: | |
| 54 | 76 | ld hl, (map_ptr) | |
| 55 | - | ld a, (hl) ; the glyph for this cell | |
| 56 | - | call lookup_tile ; -> tile_ptr, tile_attr | |
| 57 | - | call draw_tile ; draws at (B, C); preserves BC | |
| 77 | + | ld a, (hl) | |
| 78 | + | call lookup_tile | |
| 79 | + | call draw_tile | |
| 58 | 80 | ld hl, (map_ptr) | |
| 59 | 81 | inc hl | |
| 60 | 82 | ld (map_ptr), hl | |
| ... | |||
| 68 | 90 | jr nz, .room_row | |
| 69 | 91 | ret | |
| 70 | 92 | | |
| 71 | - | ; lookup_tile — glyph in A. Scan the palette for a matching glyph and copy its | |
| 72 | - | ; tile pointer and attribute into tile_ptr / tile_attr. Every glyph the map uses | |
| 73 | - | ; must exist in the palette. | |
| 74 | 93 | lookup_tile: | |
| 75 | 94 | ld hl, palette | |
| 76 | 95 | .scan: | |
| 77 | - | cp (hl) ; glyph match? | |
| 96 | + | cp (hl) | |
| 78 | 97 | jr z, .found | |
| 79 | - | inc hl ; step over glyph + pointer + attribute | |
| 98 | + | inc hl | |
| 80 | 99 | inc hl | |
| 81 | 100 | inc hl | |
| 82 | 101 | inc hl | |
| 83 | 102 | jr .scan | |
| 84 | 103 | .found: | |
| 85 | - | inc hl ; -> pointer low byte | |
| 104 | + | inc hl | |
| 86 | 105 | ld e, (hl) | |
| 87 | 106 | inc hl | |
| 88 | 107 | ld d, (hl) | |
| ... | |||
| 92 | 111 | ld (tile_attr), a | |
| 93 | 112 | ret | |
| 94 | 113 | | |
| 95 | - | ; ---------------------------------------------------------------------------- | |
| 96 | - | ; draw_tile — colour cell (B,C), then lay its eight bitmap rows. | |
| 97 | - | ; ---------------------------------------------------------------------------- | |
| 98 | 114 | draw_tile: | |
| 99 | 115 | push bc | |
| 100 | 116 | call attr_addr_cr | |
| ... | |||
| 113 | 129 | ret | |
| 114 | 130 | | |
| 115 | 131 | ; ---------------------------------------------------------------------------- | |
| 116 | - | ; player_step / wall_at — unchanged from Unit 3. Collision is still "is the | |
| 117 | - | ; target cell BRIGHT?", which now stops the thief at pillars as well as edges. | |
| 132 | + | ; player_step — move as before, then check whether he stepped onto a doorway. | |
| 118 | 133 | ; ---------------------------------------------------------------------------- | |
| 119 | 134 | player_step: | |
| 120 | 135 | ld a, (thief_col) | |
| ... | |||
| 167 | 182 | ld (thief_row), a | |
| 168 | 183 | call save_under | |
| 169 | 184 | call draw_thief | |
| 185 | + | call check_exit | |
| 170 | 186 | ret | |
| 171 | 187 | | |
| 172 | 188 | wall_at: | |
| ... | |||
| 175 | 191 | ret | |
| 176 | 192 | | |
| 177 | 193 | ; ---------------------------------------------------------------------------- | |
| 178 | - | ; Save / restore / draw the thief — unchanged from Unit 3. | |
| 194 | + | ; check_exit — if the thief is standing on an edge, follow that edge's link in | |
| 195 | + | ; the room table and travel there. | |
| 196 | + | ; ---------------------------------------------------------------------------- | |
| 197 | + | check_exit: | |
| 198 | + | ld a, (thief_col) | |
| 199 | + | or a | |
| 200 | + | jr z, .west ; column 0 | |
| 201 | + | cp 31 | |
| 202 | + | jr z, .east ; column 31 | |
| 203 | + | ld a, (thief_row) | |
| 204 | + | or a | |
| 205 | + | jr z, .north ; row 0 | |
| 206 | + | cp 23 | |
| 207 | + | jr z, .south ; row 23 | |
| 208 | + | ret ; not on an edge | |
| 209 | + | .north: | |
| 210 | + | call room_entry_addr | |
| 211 | + | inc hl | |
| 212 | + | inc hl ; +2 = North link | |
| 213 | + | ld a, (hl) | |
| 214 | + | jr .travel | |
| 215 | + | .south: | |
| 216 | + | call room_entry_addr | |
| 217 | + | inc hl | |
| 218 | + | inc hl | |
| 219 | + | inc hl ; +3 = South link | |
| 220 | + | ld a, (hl) | |
| 221 | + | jr .travel | |
| 222 | + | .east: | |
| 223 | + | call room_entry_addr | |
| 224 | + | ld de, 4 ; +4 = East link | |
| 225 | + | add hl, de | |
| 226 | + | ld a, (hl) | |
| 227 | + | jr .travel | |
| 228 | + | .west: | |
| 229 | + | call room_entry_addr | |
| 230 | + | ld de, 5 ; +5 = West link | |
| 231 | + | add hl, de | |
| 232 | + | ld a, (hl) | |
| 233 | + | .travel: | |
| 234 | + | cp NO_EXIT | |
| 235 | + | ret z ; a doorway to nowhere — stay | |
| 236 | + | ld (current_room), a | |
| 237 | + | call draw_room | |
| 238 | + | ld a, START_COL ; crude: drop him in the centre (Unit 6 fixes this) | |
| 239 | + | ld (thief_col), a | |
| 240 | + | ld a, START_ROW | |
| 241 | + | ld (thief_row), a | |
| 242 | + | call save_under | |
| 243 | + | call draw_thief | |
| 244 | + | ret | |
| 245 | + | | |
| 246 | + | ; ---------------------------------------------------------------------------- | |
| 247 | + | ; Save / restore / draw the thief — unchanged. | |
| 179 | 248 | ; ---------------------------------------------------------------------------- | |
| 180 | 249 | pos_bc: | |
| 181 | 250 | ld a, (thief_row) | |
| ... | |||
| 234 | 303 | djnz .thief_row | |
| 235 | 304 | ret | |
| 236 | 305 | | |
| 237 | - | ; ---------------------------------------------------------------------------- | |
| 238 | - | ; scr_addr_cr / attr_addr_cr — carried from Gloaming. | |
| 239 | - | ; ---------------------------------------------------------------------------- | |
| 240 | 306 | scr_addr_cr: | |
| 241 | 307 | ld a, b | |
| 242 | 308 | and %00011000 | |
| ... | |||
| 269 | 335 | ret | |
| 270 | 336 | | |
| 271 | 337 | ; ---------------------------------------------------------------------------- | |
| 272 | - | ; The palette — one entry per kind of tile: glyph, eight-byte pattern, colour. | |
| 273 | - | ; To give the keep a new sort of cell, add a line here and use its glyph below. | |
| 274 | - | ; entry = glyph (1) + tile pointer (2) + attribute (1) | |
| 338 | + | ; Palette. | |
| 275 | 339 | ; ---------------------------------------------------------------------------- | |
| 276 | 340 | palette: | |
| 277 | 341 | defb '.' | |
| ... | |||
| 282 | 346 | defb WALL_ATTR | |
| 283 | 347 | | |
| 284 | 348 | ; ---------------------------------------------------------------------------- | |
| 285 | - | ; The room — read it like a picture. '#' is lit wall, '.' is dark floor. The | |
| 286 | - | ; outer ring walls the hall; the four blocks are pillars the thief walks around. | |
| 349 | + | ; The room graph. Each entry: map pointer, then North, South, East, West links | |
| 350 | + | ; ($FF = no door). Two rooms, joined east-west: room 0's east door leads to | |
| 351 | + | ; room 1, room 1's west door leads back. | |
| 287 | 352 | ; ---------------------------------------------------------------------------- | |
| 288 | - | room_data: | |
| 353 | + | rooms: | |
| 354 | + | defw room0_data | |
| 355 | + | defb NO_EXIT, NO_EXIT, 1, NO_EXIT | |
| 356 | + | defw room1_data | |
| 357 | + | defb NO_EXIT, NO_EXIT, NO_EXIT, 0 | |
| 358 | + | | |
| 359 | + | ; Room 0 — the pillared hall, with a doorway in the east wall (row 11). | |
| 360 | + | room0_data: | |
| 289 | 361 | defb "################################" | |
| 290 | 362 | defb "#..............................#" | |
| 291 | 363 | defb "#..............................#" | |
| ... | |||
| 296 | 368 | defb "#..............................#" | |
| 297 | 369 | defb "#........##..........##........#" | |
| 298 | 370 | defb "#........##..........##........#" | |
| 299 | - | defb "#..............................#" | |
| 300 | 371 | defb "#..............................#" | |
| 372 | + | defb "#..............................." | |
| 301 | 373 | defb "#..............................#" | |
| 302 | 374 | defb "#..............................#" | |
| 303 | 375 | defb "#........##..........##........#" | |
| 304 | 376 | defb "#........##..........##........#" | |
| 377 | + | defb "#..............................#" | |
| 378 | + | defb "#..............................#" | |
| 379 | + | defb "#..............................#" | |
| 380 | + | defb "#..............................#" | |
| 381 | + | defb "#..............................#" | |
| 382 | + | defb "#..............................#" | |
| 383 | + | defb "#..............................#" | |
| 384 | + | defb "################################" | |
| 385 | + | | |
| 386 | + | ; Room 1 — a plainer chamber with a block near the top, doorway in the west | |
| 387 | + | ; wall (row 11) leading back to room 0. | |
| 388 | + | room1_data: | |
| 389 | + | defb "################################" | |
| 390 | + | defb "#..............................#" | |
| 391 | + | defb "#..............................#" | |
| 392 | + | defb "#..............................#" | |
| 393 | + | defb "#..............##..............#" | |
| 394 | + | defb "#..............##..............#" | |
| 395 | + | defb "#..............................#" | |
| 396 | + | defb "#..............................#" | |
| 397 | + | defb "#..............................#" | |
| 398 | + | defb "#..............................#" | |
| 399 | + | defb "#..............................#" | |
| 400 | + | defb "...............................#" | |
| 401 | + | defb "#..............................#" | |
| 402 | + | defb "#..............................#" | |
| 403 | + | defb "#..............................#" | |
| 404 | + | defb "#..............................#" | |
| 305 | 405 | defb "#..............................#" | |
| 306 | 406 | defb "#..............................#" | |
| 307 | 407 | defb "#..............................#" | |
| ... | |||
| 347 | 447 | ; ---------------------------------------------------------------------------- | |
| 348 | 448 | ; Variables. | |
| 349 | 449 | ; ---------------------------------------------------------------------------- | |
| 450 | + | current_room: | |
| 451 | + | defb 0 | |
| 350 | 452 | thief_col: | |
| 351 | 453 | defb START_COL | |
| 352 | 454 | thief_row: |
The complete program
; Shadowkeep — Unit 5: The Room Graph
; Cumulative build; every step runs on its own. Narrative: the unit page.
; step-01 gives the keep a graph of rooms — walk a doorway, flick to the next.
org 32768
WALL_ATTR equ %01001000
FLOOR_ATTR equ %00001000
THIEF equ %01001010
WALL_BIT equ 6
START_COL equ 15
START_ROW equ 11
NO_EXIT equ $FF
KEYS_OP equ $DFFE
KEYS_Q equ $FBFE
KEYS_A equ $FDFE
; ----------------------------------------------------------------------------
; SETUP.
; ----------------------------------------------------------------------------
start:
ld a, 0
out ($FE), a
xor a
ld (current_room), a
ld a, START_COL
ld (thief_col), a
ld a, START_ROW
ld (thief_row), a
call draw_room
call save_under
call draw_thief
im 1
ei
.loop:
halt
call player_step
jr .loop
; ----------------------------------------------------------------------------
; room_entry_addr — HL = rooms + current_room * 6 (entry = map ptr + 4 links).
; ----------------------------------------------------------------------------
room_entry_addr:
ld a, (current_room)
ld l, a
ld h, 0
ld d, h
ld e, l ; DE = room number
add hl, hl ; *2
add hl, de ; *3
add hl, hl ; *6
ld de, rooms
add hl, de
ret
; ----------------------------------------------------------------------------
; draw_room — draw the current room's map. Same walk as Unit 4, but the map
; pointer comes from the room table now, not a fixed label.
; ----------------------------------------------------------------------------
draw_room:
call room_entry_addr
ld a, (hl) ; map pointer, low
inc hl
ld h, (hl) ; map pointer, high
ld l, a
ld (map_ptr), hl
ld b, 0
.room_row:
ld c, 0
.room_col:
ld hl, (map_ptr)
ld a, (hl)
call lookup_tile
call draw_tile
ld hl, (map_ptr)
inc hl
ld (map_ptr), hl
inc c
ld a, c
cp 32
jr nz, .room_col
inc b
ld a, b
cp 24
jr nz, .room_row
ret
lookup_tile:
ld hl, palette
.scan:
cp (hl)
jr z, .found
inc hl
inc hl
inc hl
inc hl
jr .scan
.found:
inc hl
ld e, (hl)
inc hl
ld d, (hl)
ld (tile_ptr), de
inc hl
ld a, (hl)
ld (tile_attr), a
ret
draw_tile:
push bc
call attr_addr_cr
ld a, (tile_attr)
ld (hl), a
call scr_addr_cr
ld de, (tile_ptr)
ld b, 8
.tile_row:
ld a, (de)
ld (hl), a
inc de
inc h
djnz .tile_row
pop bc
ret
; ----------------------------------------------------------------------------
; player_step — move as before, then check whether he stepped onto a doorway.
; ----------------------------------------------------------------------------
player_step:
ld a, (thief_col)
ld (tcol), a
ld a, (thief_row)
ld (trow), a
ld bc, KEYS_OP
in a, (c)
bit 1, a
jr z, .left
bit 0, a
jr z, .right
ld bc, KEYS_Q
in a, (c)
bit 0, a
jr z, .up
ld bc, KEYS_A
in a, (c)
bit 0, a
jr z, .down
ret
.left:
ld hl, tcol
dec (hl)
jr .move
.right:
ld hl, tcol
inc (hl)
jr .move
.up:
ld hl, trow
dec (hl)
jr .move
.down:
ld hl, trow
inc (hl)
.move:
ld a, (trow)
ld b, a
ld a, (tcol)
ld c, a
call wall_at
ret nz
call restore_under
ld a, (tcol)
ld (thief_col), a
ld a, (trow)
ld (thief_row), a
call save_under
call draw_thief
call check_exit
ret
wall_at:
call attr_addr_cr
bit WALL_BIT, (hl)
ret
; ----------------------------------------------------------------------------
; check_exit — if the thief is standing on an edge, follow that edge's link in
; the room table and travel there.
; ----------------------------------------------------------------------------
check_exit:
ld a, (thief_col)
or a
jr z, .west ; column 0
cp 31
jr z, .east ; column 31
ld a, (thief_row)
or a
jr z, .north ; row 0
cp 23
jr z, .south ; row 23
ret ; not on an edge
.north:
call room_entry_addr
inc hl
inc hl ; +2 = North link
ld a, (hl)
jr .travel
.south:
call room_entry_addr
inc hl
inc hl
inc hl ; +3 = South link
ld a, (hl)
jr .travel
.east:
call room_entry_addr
ld de, 4 ; +4 = East link
add hl, de
ld a, (hl)
jr .travel
.west:
call room_entry_addr
ld de, 5 ; +5 = West link
add hl, de
ld a, (hl)
.travel:
cp NO_EXIT
ret z ; a doorway to nowhere — stay
ld (current_room), a
call draw_room
ld a, START_COL ; crude: drop him in the centre (Unit 6 fixes this)
ld (thief_col), a
ld a, START_ROW
ld (thief_row), a
call save_under
call draw_thief
ret
; ----------------------------------------------------------------------------
; Save / restore / draw the thief — unchanged.
; ----------------------------------------------------------------------------
pos_bc:
ld a, (thief_row)
ld b, a
ld a, (thief_col)
ld c, a
ret
save_under:
call pos_bc
call scr_addr_cr
ld de, under_thief
ld b, 8
.save_row:
ld a, (hl)
ld (de), a
inc de
inc h
djnz .save_row
call pos_bc
call attr_addr_cr
ld a, (hl)
ld (under_thief + 8), a
ret
restore_under:
call pos_bc
call scr_addr_cr
ld de, under_thief
ld b, 8
.restore_row:
ld a, (de)
ld (hl), a
inc de
inc h
djnz .restore_row
call pos_bc
call attr_addr_cr
ld a, (under_thief + 8)
ld (hl), a
ret
draw_thief:
call pos_bc
call attr_addr_cr
ld (hl), THIEF
call pos_bc
call scr_addr_cr
ld de, thief
ld b, 8
.thief_row:
ld a, (de)
ld (hl), a
inc de
inc h
djnz .thief_row
ret
scr_addr_cr:
ld a, b
and %00011000
or %01000000
ld h, a
ld a, b
and %00000111
rrca
rrca
rrca
or c
ld l, a
ret
attr_addr_cr:
ld a, b
ld l, a
ld h, 0
add hl, hl
add hl, hl
add hl, hl
add hl, hl
add hl, hl
ld de, $5800
add hl, de
ld a, c
ld e, a
ld d, 0
add hl, de
ret
; ----------------------------------------------------------------------------
; Palette.
; ----------------------------------------------------------------------------
palette:
defb '.'
defw floor_tile
defb FLOOR_ATTR
defb '#'
defw wall_tile
defb WALL_ATTR
; ----------------------------------------------------------------------------
; The room graph. Each entry: map pointer, then North, South, East, West links
; ($FF = no door). Two rooms, joined east-west: room 0's east door leads to
; room 1, room 1's west door leads back.
; ----------------------------------------------------------------------------
rooms:
defw room0_data
defb NO_EXIT, NO_EXIT, 1, NO_EXIT
defw room1_data
defb NO_EXIT, NO_EXIT, NO_EXIT, 0
; Room 0 — the pillared hall, with a doorway in the east wall (row 11).
room0_data:
defb "################################"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#........##..........##........#"
defb "#........##..........##........#"
defb "#..............................#"
defb "#..............................."
defb "#..............................#"
defb "#..............................#"
defb "#........##..........##........#"
defb "#........##..........##........#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "################################"
; Room 1 — a plainer chamber with a block near the top, doorway in the west
; wall (row 11) leading back to room 0.
room1_data:
defb "################################"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............##..............#"
defb "#..............##..............#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "...............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "################################"
; ----------------------------------------------------------------------------
; Tiles and the thief.
; ----------------------------------------------------------------------------
floor_tile:
defb %10101010
defb %01010101
defb %10101010
defb %01010101
defb %10101010
defb %01010101
defb %10101010
defb %01010101
wall_tile:
defb %00010001
defb %00000000
defb %01000100
defb %00000000
defb %00010001
defb %00000000
defb %01000100
defb %00000000
thief:
defb %00011000
defb %00111100
defb %01111110
defb %01111110
defb %01111110
defb %01111110
defb %00111100
defb %00100100
; ----------------------------------------------------------------------------
; Variables.
; ----------------------------------------------------------------------------
current_room:
defb 0
thief_col:
defb START_COL
thief_row:
defb START_ROW
tcol:
defb 0
trow:
defb 0
map_ptr:
defw 0
tile_ptr:
defw 0
tile_attr:
defb 0
under_thief:
defb 0, 0, 0, 0, 0, 0, 0, 0, 0
end start
Walk east with P to the gap in the right-hand wall and the screen flicks to the second room; walk west with O to its doorway and you flick back. You're navigating a graph of rooms with your feet:
Try this: a room to the north
Add a room2_data map with a doorway in its south wall (a . on row 23), give it a south link back to room 0, and set room 0's north link to 2 with a . in room 0's north wall (row 0). Now the keep branches: east to one room, north to another. The graph is yours to grow — each new room is a map plus four numbers.
Try this: a one-way door
Give room 0 an east link to room 1, but leave room 1's west link as NO_EXIT and wall up its west side. Now you can go east but not come back — a trapdoor, a portcullis that falls behind you. One number, and the keep has a one-way passage. (Make sure there's another way out, or the thief is stuck!)
Try this: lengthen the flick
The repaint is plain — the new room appears all at once. Before the draw_room in .travel, try blacking the screen (an LDIR wash over the bitmap) for a frame or two, so the old room clears before the new one paints. A heavier, more deliberate transition. Feel how much the timing of the flick changes the keep's mood.
When it's wrong, see why
- He walks to the edge and nothing happens. The doorway cell must be floor (
.), and that edge's link must not be$FF. A#in the wall blocks him before he reaches the edge; a$FFlink is a door to nowhere. - He vanishes / the screen is half-painted. You caught the repaint mid-flick — it takes a few frames. In real play it settles immediately; it's only visible as the brief genre flick.
- He travels to the wrong room. Check the link order in each entry: North, South, East, West. East is the third link byte, west the fourth.
- The machine hangs on travel. A link points at a room number with no table entry, so
room_entry_addrcomputes off into nowhere. Every link must be$FFor a real room index. - He arrives stuck in a wall. The centre cell (15, 11) must be floor in every room. If a room has a block there, he'll spawn inside it — Unit 6's edge re-entry will fix the spawn, but for now keep the centre clear.
Before and after
You started with a single room you could fill but never leave, and finished with a keep you can walk across — two rooms joined by doorways, the thief travelling between them by foot. The world turned out to be a graph: each room a map, each wall an edge that either holds or leads somewhere. A doorway is the absence of a wall; travel is a table lookup; the flick is the genre showing its hand. The keep has elsewhere in it now — and one honest seam left, the centre-landing, for the next unit to close.
What you've learnt
- A world is a graph. Each room is a node — its map — and the edges are four links saying what lies through each wall. The whole structure is a small table.
- A doorway is the absence of a wall. A floor cell in the wall ring; the thief walks onto it, and collision needs no special case.
- Travel is a table lookup. Stand on an edge, read that edge's link, switch the current room, repaint. The graph does the routing.
- The flick is the genre. Repainting the screen on entry is what a flick-screen adventure is — not a cost to hide, a texture to embrace.
What's next
You can cross the keep, but the crossing is a teleport — walk through a door and you appear in the middle of the next room, not at the threshold. In Unit 6, "Through the Doorway," we make it a real step: leave by the east edge and arrive at the west edge of the next room, at the same height, mid-stride. We'll carry his position across the seam so the flick reads as walking through a doorway, not blinking across the keep. The map becomes a walk.