Skip to content
Game 2 Unit 8 of 16 1 hr learning time

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.

50% of Shadowkeep

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

The Gallery — a room split across the middle by a wall with a single gap; the red thief stands just inside its west doorway, with another doorway gap at the top leading further in.
The Gallery — bisected by a wall with one gap, a west door the thief stepped through, and a north door at the top leading on. Same engine as the Hall, an entirely different room to play — built only from where the walls are.

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.

Step 1: a third room and the links that join the keep
+56-40
11 ; Shadowkeep — Unit 8: Three Rooms
22 ; 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.
44
55 org 32768
66
77 WALL_ATTR equ %01001000
88 FLOOR_ATTR equ %00001000
9-MARK_ATTR equ %00001111 ; dim, PAPER 1 (blue), INK 7 (white) — chalk, walkable
9+MARK_ATTR equ %00001111
1010 THIEF equ %01001010
1111 WALL_BIT equ 6
1212
...
1919 KEYS_A equ $FDFE
2020 KEYS_SPACE equ $7FFE
2121
22-; ----------------------------------------------------------------------------
23-; SETUP — copy the room templates into their RAM state buffers, then run.
24-; ----------------------------------------------------------------------------
2522 start:
2623 ld a, 0
2724 out ($FE), a
...
3229 ldir
3330 ld hl, room1_template
3431 ld de, room1_state
32+ ld bc, 768
33+ ldir
34+ ld hl, room2_template
35+ ld de, room2_state
3536 ld bc, 768
3637 ldir
3738
...
5455 call mark_step
5556 jr .loop
5657
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-; ----------------------------------------------------------------------------
6258 mark_step:
6359 ld bc, KEYS_SPACE
6460 in a, (c)
6561 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
6964 ld (hl), '+'
70-
71- ld hl, mark_tile ; remember the mark as what's under him
65+ ld hl, mark_tile
7266 ld de, under_thief
7367 ld bc, 8
7468 ldir
...
7670 ld (under_thief + 8), a
7771 ret
7872
79-; cell_state_addr — HL = current room's state buffer + thief_row*32 + thief_col.
8073 cell_state_addr:
8174 call room_entry_addr
82- ld a, (hl) ; state buffer pointer (low)
75+ ld a, (hl)
8376 inc hl
8477 ld h, (hl)
85- ld l, a ; HL = state base
78+ ld l, a
8679 push hl
8780 ld a, (thief_row)
8881 ld l, a
...
9184 add hl, hl
9285 add hl, hl
9386 add hl, hl
94- add hl, hl ; HL = row * 32
87+ add hl, hl
9588 ld a, (thief_col)
9689 ld e, a
9790 ld d, 0
98- add hl, de ; + col
91+ add hl, de
9992 pop de
100- add hl, de ; + base
93+ add hl, de
10194 ret
10295
103-; ----------------------------------------------------------------------------
104-; room_entry_addr / draw_room — draw_room now paints from the state buffer the
105-; room table points at.
106-; ----------------------------------------------------------------------------
10796 room_entry_addr:
10897 ld a, (current_room)
10998 ld l, a
...
183172 pop bc
184173 ret
185174
186-; ----------------------------------------------------------------------------
187-; player_step / wall_at / check_exit — unchanged from Unit 6.
188-; ----------------------------------------------------------------------------
189175 player_step:
190176 ld a, (thief_col)
191177 ld (tcol), a
...
395381 add hl, de
396382 ret
397383
398-; ----------------------------------------------------------------------------
399-; Palette — now with a chalk mark. The mark is dim, so it stays walkable.
400-; ----------------------------------------------------------------------------
401384 palette:
402385 defb '.'
403386 defw floor_tile
...
410393 defb MARK_ATTR
411394
412395 ; ----------------------------------------------------------------------------
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
414398 ; ----------------------------------------------------------------------------
415399 rooms:
416400 defw room0_state
417- defb NO_EXIT, NO_EXIT, 1, NO_EXIT
401+ defb NO_EXIT, NO_EXIT, 1, NO_EXIT ; Hall: east -> Gallery
418402 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
420406
407+; The Great Hall — four pillars, east door at row 11.
421408 room0_template:
422409 defb "################################"
423410 defb "#..............................#"
...
444431 defb "#..............................#"
445432 defb "################################"
446433
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.
447436 room1_template:
448- defb "################################"
437+ defb "###############.################"
449438 defb "#..............................#"
450439 defb "#..............................#"
451440 defb "#..............................#"
452- defb "#..............##..............#"
453- defb "#..............##..............#"
441+ defb "#..............................#"
454442 defb "#..............................#"
455443 defb "#..............................#"
456444 defb "#..............................#"
445+ defb "###############.################"
457446 defb "#..............................#"
458447 defb "#..............................#"
459448 defb "...............................#"
...
468457 defb "#..............................#"
469458 defb "#..............................#"
470459 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:
471465 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 "###############.################"
472489
473490 floor_tile:
474491 defb %10101010
...
510527 defb %00111100
511528 defb %00100100
512529
513-; ----------------------------------------------------------------------------
514-; Variables, then the room state buffers (RAM copies of the templates).
515-; ----------------------------------------------------------------------------
516530 current_room:
517531 defb 0
518532 thief_col:
...
535549 room0_state:
536550 defs 768
537551 room1_state:
552+ defs 768
553+room2_state:
538554 defs 768
539555
540556 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:

East out of the pillared Hall into the bisected Gallery, up through its one gap into the Vault, and around the altar that can't be crossed. Three rooms, three characters — all of it laid out from where the walls are, on the engine the sub-arc built. Hold SPACE as you go and the keep would chalk your route, room by room.

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 LDIR copies 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.