Three Rooms
The flick-screen engine is complete — graph, doorways, memory. Now build a keep with it: three connected rooms, each with its own character, doors lined up so the whole thing reads as one place to explore. No new code, all design.
Everything the keep needs is built. A room is data; a palette names its tiles; rooms join in a graph; doorways carry the thief across the seam; the keep remembers what he does to it. This unit adds no new engine at all. It does the thing all that machinery was for: it builds a keep.
Three rooms, each with its own character, laid out to be explored — and because the engine's done, the whole unit is design. This is what the sub-arc was for.
What you'll see by the end
Step east out of the pillared Hall and you arrive in the Gallery — a room split by a wall with one gap you must find to go on. Thread that gap and head north into the Vault, where a great altar of stone stands in the middle, to be circled, not crossed. Three rooms, three characters, joined into one keep you can walk end to end. Hold SPACE as you go and you chalk your route, so you can find your way back.
The keep is three entries
The whole keep is three rows of the room table — each a map and its four exits:
rooms:
defw room0_state
defb NO_EXIT, NO_EXIT, 1, NO_EXIT ; Hall: east -> Gallery
defw room1_state
defb 2, NO_EXIT, NO_EXIT, 0 ; Gallery: north -> Vault, west -> Hall
defw room2_state
defb NO_EXIT, 1, NO_EXIT, NO_EXIT ; Vault: south -> Gallery
Read it like a little map: the Hall's east goes to the Gallery; the Gallery's west comes back, its north goes up to the Vault; the Vault's south returns to the Gallery. A keep is just rooms and the numbers between them.
Doors that line up
The crossings feel like steps only because the doors agree, the rule from Unit 6. The Hall's east door is at row 11, so the Gallery's west door is at row 11 — walk east, arrive level. The Gallery's north door is at column 15, so the Vault's south door is at column 15 — walk up through the gap, arrive in line. You can see the discipline in the templates: a . punched in a wall on one side has a matching . on the other.
; The Gallery — a dividing wall (row 8) with one gap at column 15,
; a west door (row 11), a north door (column 15):
defb "###############.################" ; row 0 — north door
; ...
defb "###############.################" ; row 8 — the divider's gap
; ...
defb "...............................#" ; row 11 — west door
Character is just layout
None of the three rooms needed new code to feel different — only different #s and .s. The Hall is open with four pillars; the Gallery is bisected, so it plays as a puzzle of finding the gap; the Vault is dominated by a solid altar you must walk around. Same draw_room, same collision, same everything — three distinct places, built entirely out of where the walls are. That's the whole promise of "a room is data" paying off: design is now a thing you do in a text editor, not in the assembler.
Milestone — build the keep
No new engine: the step adds a third room. The room table gains a Vault entry, a room2_template is drawn as a picture (a solid altar in the middle), and the Gallery's links are widened to join Hall, Gallery and Vault into one place. Everything that lays it out — draw_room, the palette, collision, travel, the state buffers — is exactly what the last four units built.
| 1 | 1 | ; Shadowkeep — Unit 8: Three Rooms | |
| 2 | 2 | ; Cumulative build; every step runs on its own. Narrative: the unit page. | |
| 3 | - | ; step-00 = Unit 7's end: two rooms in RAM, chalk that persists. | |
| 3 | + | ; step-01 adds a third room — Hall, Gallery, Vault — joined into one keep. | |
| 4 | 4 | | |
| 5 | 5 | org 32768 | |
| 6 | 6 | | |
| 7 | 7 | WALL_ATTR equ %01001000 | |
| 8 | 8 | FLOOR_ATTR equ %00001000 | |
| 9 | - | MARK_ATTR equ %00001111 ; dim, PAPER 1 (blue), INK 7 (white) — chalk, walkable | |
| 9 | + | MARK_ATTR equ %00001111 | |
| 10 | 10 | THIEF equ %01001010 | |
| 11 | 11 | WALL_BIT equ 6 | |
| 12 | 12 | | |
| ... | |||
| 19 | 19 | KEYS_A equ $FDFE | |
| 20 | 20 | KEYS_SPACE equ $7FFE | |
| 21 | 21 | | |
| 22 | - | ; ---------------------------------------------------------------------------- | |
| 23 | - | ; SETUP — copy the room templates into their RAM state buffers, then run. | |
| 24 | - | ; ---------------------------------------------------------------------------- | |
| 25 | 22 | start: | |
| 26 | 23 | ld a, 0 | |
| 27 | 24 | out ($FE), a | |
| ... | |||
| 32 | 29 | ldir | |
| 33 | 30 | ld hl, room1_template | |
| 34 | 31 | ld de, room1_state | |
| 32 | + | ld bc, 768 | |
| 33 | + | ldir | |
| 34 | + | ld hl, room2_template | |
| 35 | + | ld de, room2_state | |
| 35 | 36 | ld bc, 768 | |
| 36 | 37 | ldir | |
| 37 | 38 | | |
| ... | |||
| 54 | 55 | call mark_step | |
| 55 | 56 | jr .loop | |
| 56 | 57 | | |
| 57 | - | ; ---------------------------------------------------------------------------- | |
| 58 | - | ; mark_step — while SPACE is held, chalk the cell the thief is on: write the | |
| 59 | - | ; mark glyph into the room's state buffer (so it persists and redraws), and | |
| 60 | - | ; set what's "under" him to the mark, so it shows the moment he steps off. | |
| 61 | - | ; ---------------------------------------------------------------------------- | |
| 62 | 58 | mark_step: | |
| 63 | 59 | ld bc, KEYS_SPACE | |
| 64 | 60 | in a, (c) | |
| 65 | 61 | bit 0, a | |
| 66 | - | ret nz ; SPACE not held | |
| 67 | - | | |
| 68 | - | call cell_state_addr ; HL -> this cell in the state buffer | |
| 62 | + | ret nz | |
| 63 | + | call cell_state_addr | |
| 69 | 64 | ld (hl), '+' | |
| 70 | - | | |
| 71 | - | ld hl, mark_tile ; remember the mark as what's under him | |
| 65 | + | ld hl, mark_tile | |
| 72 | 66 | ld de, under_thief | |
| 73 | 67 | ld bc, 8 | |
| 74 | 68 | ldir | |
| ... | |||
| 76 | 70 | ld (under_thief + 8), a | |
| 77 | 71 | ret | |
| 78 | 72 | | |
| 79 | - | ; cell_state_addr — HL = current room's state buffer + thief_row*32 + thief_col. | |
| 80 | 73 | cell_state_addr: | |
| 81 | 74 | call room_entry_addr | |
| 82 | - | ld a, (hl) ; state buffer pointer (low) | |
| 75 | + | ld a, (hl) | |
| 83 | 76 | inc hl | |
| 84 | 77 | ld h, (hl) | |
| 85 | - | ld l, a ; HL = state base | |
| 78 | + | ld l, a | |
| 86 | 79 | push hl | |
| 87 | 80 | ld a, (thief_row) | |
| 88 | 81 | ld l, a | |
| ... | |||
| 91 | 84 | add hl, hl | |
| 92 | 85 | add hl, hl | |
| 93 | 86 | add hl, hl | |
| 94 | - | add hl, hl ; HL = row * 32 | |
| 87 | + | add hl, hl | |
| 95 | 88 | ld a, (thief_col) | |
| 96 | 89 | ld e, a | |
| 97 | 90 | ld d, 0 | |
| 98 | - | add hl, de ; + col | |
| 91 | + | add hl, de | |
| 99 | 92 | pop de | |
| 100 | - | add hl, de ; + base | |
| 93 | + | add hl, de | |
| 101 | 94 | ret | |
| 102 | 95 | | |
| 103 | - | ; ---------------------------------------------------------------------------- | |
| 104 | - | ; room_entry_addr / draw_room — draw_room now paints from the state buffer the | |
| 105 | - | ; room table points at. | |
| 106 | - | ; ---------------------------------------------------------------------------- | |
| 107 | 96 | room_entry_addr: | |
| 108 | 97 | ld a, (current_room) | |
| 109 | 98 | ld l, a | |
| ... | |||
| 183 | 172 | pop bc | |
| 184 | 173 | ret | |
| 185 | 174 | | |
| 186 | - | ; ---------------------------------------------------------------------------- | |
| 187 | - | ; player_step / wall_at / check_exit — unchanged from Unit 6. | |
| 188 | - | ; ---------------------------------------------------------------------------- | |
| 189 | 175 | player_step: | |
| 190 | 176 | ld a, (thief_col) | |
| 191 | 177 | ld (tcol), a | |
| ... | |||
| 395 | 381 | add hl, de | |
| 396 | 382 | ret | |
| 397 | 383 | | |
| 398 | - | ; ---------------------------------------------------------------------------- | |
| 399 | - | ; Palette — now with a chalk mark. The mark is dim, so it stays walkable. | |
| 400 | - | ; ---------------------------------------------------------------------------- | |
| 401 | 384 | palette: | |
| 402 | 385 | defb '.' | |
| 403 | 386 | defw floor_tile | |
| ... | |||
| 410 | 393 | defb MARK_ATTR | |
| 411 | 394 | | |
| 412 | 395 | ; ---------------------------------------------------------------------------- | |
| 413 | - | ; The room graph — pointers now name the RAM STATE buffers, not the templates. | |
| 396 | + | ; The keep: three rooms. Hall -east-> Gallery -north-> Vault, and back. | |
| 397 | + | ; entry: map ptr, North, South, East, West | |
| 414 | 398 | ; ---------------------------------------------------------------------------- | |
| 415 | 399 | rooms: | |
| 416 | 400 | defw room0_state | |
| 417 | - | defb NO_EXIT, NO_EXIT, 1, NO_EXIT | |
| 401 | + | defb NO_EXIT, NO_EXIT, 1, NO_EXIT ; Hall: east -> Gallery | |
| 418 | 402 | defw room1_state | |
| 419 | - | defb NO_EXIT, NO_EXIT, NO_EXIT, 0 | |
| 403 | + | defb 2, NO_EXIT, NO_EXIT, 0 ; Gallery: north -> Vault, west -> Hall | |
| 404 | + | defw room2_state | |
| 405 | + | defb NO_EXIT, 1, NO_EXIT, NO_EXIT ; Vault: south -> Gallery | |
| 420 | 406 | | |
| 407 | + | ; The Great Hall — four pillars, east door at row 11. | |
| 421 | 408 | room0_template: | |
| 422 | 409 | defb "################################" | |
| 423 | 410 | defb "#..............................#" | |
| ... | |||
| 444 | 431 | defb "#..............................#" | |
| 445 | 432 | defb "################################" | |
| 446 | 433 | | |
| 434 | + | ; The Gallery — a dividing wall with one gap (column 15). West door (row 11) | |
| 435 | + | ; back to the Hall; north door (column 15) up to the Vault. | |
| 447 | 436 | room1_template: | |
| 448 | - | defb "################################" | |
| 437 | + | defb "###############.################" | |
| 449 | 438 | defb "#..............................#" | |
| 450 | 439 | defb "#..............................#" | |
| 451 | 440 | defb "#..............................#" | |
| 452 | - | defb "#..............##..............#" | |
| 453 | - | defb "#..............##..............#" | |
| 441 | + | defb "#..............................#" | |
| 454 | 442 | defb "#..............................#" | |
| 455 | 443 | defb "#..............................#" | |
| 456 | 444 | defb "#..............................#" | |
| 445 | + | defb "###############.################" | |
| 457 | 446 | defb "#..............................#" | |
| 458 | 447 | defb "#..............................#" | |
| 459 | 448 | defb "...............................#" | |
| ... | |||
| 468 | 457 | defb "#..............................#" | |
| 469 | 458 | defb "#..............................#" | |
| 470 | 459 | defb "#..............................#" | |
| 460 | + | defb "################################" | |
| 461 | + | | |
| 462 | + | ; The Vault — a great altar of stone in the middle, south door (column 15) | |
| 463 | + | ; down to the Gallery. | |
| 464 | + | room2_template: | |
| 471 | 465 | defb "################################" | |
| 466 | + | defb "#..............................#" | |
| 467 | + | defb "#..............................#" | |
| 468 | + | defb "#..............................#" | |
| 469 | + | defb "#..............................#" | |
| 470 | + | defb "#..............................#" | |
| 471 | + | defb "#..............................#" | |
| 472 | + | defb "#..............................#" | |
| 473 | + | defb "#..............................#" | |
| 474 | + | defb "#..............................#" | |
| 475 | + | defb "#.............####.............#" | |
| 476 | + | defb "#.............####.............#" | |
| 477 | + | defb "#.............####.............#" | |
| 478 | + | defb "#.............####.............#" | |
| 479 | + | defb "#..............................#" | |
| 480 | + | defb "#..............................#" | |
| 481 | + | defb "#..............................#" | |
| 482 | + | defb "#..............................#" | |
| 483 | + | defb "#..............................#" | |
| 484 | + | defb "#..............................#" | |
| 485 | + | defb "#..............................#" | |
| 486 | + | defb "#..............................#" | |
| 487 | + | defb "#..............................#" | |
| 488 | + | defb "###############.################" | |
| 472 | 489 | | |
| 473 | 490 | floor_tile: | |
| 474 | 491 | defb %10101010 | |
| ... | |||
| 510 | 527 | defb %00111100 | |
| 511 | 528 | defb %00100100 | |
| 512 | 529 | | |
| 513 | - | ; ---------------------------------------------------------------------------- | |
| 514 | - | ; Variables, then the room state buffers (RAM copies of the templates). | |
| 515 | - | ; ---------------------------------------------------------------------------- | |
| 516 | 530 | current_room: | |
| 517 | 531 | defb 0 | |
| 518 | 532 | thief_col: | |
| ... | |||
| 535 | 549 | room0_state: | |
| 536 | 550 | defs 768 | |
| 537 | 551 | room1_state: | |
| 552 | + | defs 768 | |
| 553 | + | room2_state: | |
| 538 | 554 | defs 768 | |
| 539 | 555 | | |
| 540 | 556 | end start |
The complete program
; Shadowkeep — Unit 8: Three Rooms
; Cumulative build; every step runs on its own. Narrative: the unit page.
; step-01 adds a third room — Hall, Gallery, Vault — joined into one keep.
org 32768
WALL_ATTR equ %01001000
FLOOR_ATTR equ %00001000
MARK_ATTR equ %00001111
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
KEYS_SPACE equ $7FFE
start:
ld a, 0
out ($FE), a
ld hl, room0_template
ld de, room0_state
ld bc, 768
ldir
ld hl, room1_template
ld de, room1_state
ld bc, 768
ldir
ld hl, room2_template
ld de, room2_state
ld bc, 768
ldir
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
call mark_step
jr .loop
mark_step:
ld bc, KEYS_SPACE
in a, (c)
bit 0, a
ret nz
call cell_state_addr
ld (hl), '+'
ld hl, mark_tile
ld de, under_thief
ld bc, 8
ldir
ld a, MARK_ATTR
ld (under_thief + 8), a
ret
cell_state_addr:
call room_entry_addr
ld a, (hl)
inc hl
ld h, (hl)
ld l, a
push hl
ld a, (thief_row)
ld l, a
ld h, 0
add hl, hl
add hl, hl
add hl, hl
add hl, hl
add hl, hl
ld a, (thief_col)
ld e, a
ld d, 0
add hl, de
pop de
add hl, de
ret
room_entry_addr:
ld a, (current_room)
ld l, a
ld h, 0
ld d, h
ld e, l
add hl, hl
add hl, de
add hl, hl
ld de, rooms
add hl, de
ret
draw_room:
call room_entry_addr
ld a, (hl)
inc hl
ld h, (hl)
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:
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:
ld a, (thief_col)
or a
jr z, .west
cp 31
jr z, .east
ld a, (thief_row)
or a
jr z, .north
cp 23
jr z, .south
ret
.east:
call room_entry_addr
ld de, 4
add hl, de
ld a, (hl)
cp NO_EXIT
ret z
ld (current_room), a
ld a, 1
ld (thief_col), a
jr .enter
.west:
call room_entry_addr
ld de, 5
add hl, de
ld a, (hl)
cp NO_EXIT
ret z
ld (current_room), a
ld a, 30
ld (thief_col), a
jr .enter
.north:
call room_entry_addr
inc hl
inc hl
ld a, (hl)
cp NO_EXIT
ret z
ld (current_room), a
ld a, 22
ld (thief_row), a
jr .enter
.south:
call room_entry_addr
inc hl
inc hl
inc hl
ld a, (hl)
cp NO_EXIT
ret z
ld (current_room), a
ld a, 1
ld (thief_row), a
.enter:
call draw_room
call save_under
call draw_thief
ret
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:
defb '.'
defw floor_tile
defb FLOOR_ATTR
defb '#'
defw wall_tile
defb WALL_ATTR
defb '+'
defw mark_tile
defb MARK_ATTR
; ----------------------------------------------------------------------------
; The keep: three rooms. Hall -east-> Gallery -north-> Vault, and back.
; entry: map ptr, North, South, East, West
; ----------------------------------------------------------------------------
rooms:
defw room0_state
defb NO_EXIT, NO_EXIT, 1, NO_EXIT ; Hall: east -> Gallery
defw room1_state
defb 2, NO_EXIT, NO_EXIT, 0 ; Gallery: north -> Vault, west -> Hall
defw room2_state
defb NO_EXIT, 1, NO_EXIT, NO_EXIT ; Vault: south -> Gallery
; The Great Hall — four pillars, east door at row 11.
room0_template:
defb "################################"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#........##..........##........#"
defb "#........##..........##........#"
defb "#..............................#"
defb "#..............................."
defb "#..............................#"
defb "#..............................#"
defb "#........##..........##........#"
defb "#........##..........##........#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "################################"
; The Gallery — a dividing wall with one gap (column 15). West door (row 11)
; back to the Hall; north door (column 15) up to the Vault.
room1_template:
defb "###############.################"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "###############.################"
defb "#..............................#"
defb "#..............................#"
defb "...............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "################################"
; The Vault — a great altar of stone in the middle, south door (column 15)
; down to the Gallery.
room2_template:
defb "################################"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#.............####.............#"
defb "#.............####.............#"
defb "#.............####.............#"
defb "#.............####.............#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "#..............................#"
defb "###############.################"
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
mark_tile:
defb %00000000
defb %00011000
defb %00011000
defb %01111110
defb %01111110
defb %00011000
defb %00011000
defb %00000000
thief:
defb %00011000
defb %00111100
defb %01111110
defb %01111110
defb %01111110
defb %01111110
defb %00111100
defb %00100100
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
room0_state:
defs 768
room1_state:
defs 768
room2_state:
defs 768
end start
From the Hall, walk east into the Gallery, thread the gap in its dividing wall, head north into the Vault, and round the altar — three rooms walked end to end as one keep:
Try this: a fourth room
Add a room3 east of the Vault: give it a map, a west door lined up with a new east door in the Vault, and the two links to join them. The keep grows by exactly one table row and one aligned pair of doors. Sketch the map on paper first — a keep is more fun to design than to code.
Try this: a loop
Right now the keep is a dead-end branch — you always backtrack. Make it a loop: give the Vault an east door to a new room whose south door returns to the Hall. Now the thief can walk a circuit and come back where he started without retracing. Loops are what turn a set of rooms into a place you can get lost in — the heart of Atic Atac.
Try this: a room that lies
Design a room with two doors that look symmetric but lead to different places — one east door home, one north door deeper in, with no hint which is which. A keep that withholds its map is a keep worth mapping (which is what your chalk is for). Atmosphere is partly architecture.
When it's wrong, see why
- A door leads into a wall. The connected doors don't share a row (east/west) or column (north/south). Line the
.s up across the two templates. - The thief spawns in a wall on arrival. The entry cell (one inside the opposite edge) is blocked. Keep the cell just inside each doorway clear of pillars and altars.
- A room is the wrong shape / shifted. A template row isn't 32 characters, or there aren't 24 rows. Count them.
- Travelling hangs the machine. A link points to a room index with no table entry. Three rooms means valid indices 0, 1, 2 — everything else is
NO_EXIT. - The start-up corrupts. Three rooms now means three
LDIRcopies and three 768-byte state buffers; make sure each template is copied into its own buffer and they all fit in RAM.
Before and after
You started this sub-arc with one hand-placed hall and finished it with a keep: three rooms, each playing differently, joined by doorways that line up, the thief walking the whole of it and chalking his route as he goes. This last unit added no engine at all — a third table entry and a room drawn as a picture, on machinery that was already done. That's the dividend the groundwork paid: when a room is data and the rules read the data, a new place to explore costs a map and a couple of numbers, written in a text editor. The keep is built; what it lacks now is not structure but atmosphere.
What you've learnt
- A world is rooms plus numbers. The whole keep's structure is a handful of table entries — maps and the links between them.
- Character comes from layout. Open, bisected, blocked-by-an-altar — three rooms that play differently, built only from where the walls are, with no new code.
- Alignment makes it a place. Doors that share a row or column turn separate screens into one continuous space.
- Good engines make content cheap. Because the machinery was done, a whole keep was a unit of design. That's the dividend of the last seven units.
What's next
The keep is built — but it's evenly, flatly lit, every room the same cold blue. A real keep has atmosphere: dark corners and lit pools, mood that changes room to room, a sense of being somewhere after nightfall. The next sub-arc, Mood and Light, gives the keep that. We begin in Unit 9, "Light and Shadow," where the dithering you met in Unit 2 becomes a tool for lighting — varying the shade of the stone by how near it is to a torch, so the dark of the keep finally feels dark.