Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

World Maps

Resource set and naming

DD1.DAT contains 21 MAP members: one for every combination of level letter A through G and difficulty code E, N, or D. The codes mean Easy, Normal, and Difficult, matching the three modes described by the manual. Archive directory entries 215 through 235 contain this complete cross product, and every member expands to exactly 768 bytes.

Scene opcode 0x78 supplies a level letter. Its load_map_resource helper at load offset 0x034F first compares that letter with variable 16. A match returns without touching the mutable grid or exploration state. For a different level, it reads script variable zero as a difficulty index, selects one byte from the literal END, appends .MAP, and loads the resulting archive member into the live grid at DS:5B16. It stores the new letter in variable 16 and clears the sixteen exploration words at DS:72C4. Uses across the scene corpus supply all seven letters. For example, level C on Easy mode loads CE.MAP.

When the global no-mature flag is set, the same loader scans the new grid. A connected cell with low kind 1 through A is replaced by an empty connected hall if either parameter is at least E0: the connection nibble survives, while the low kind and both parameters become zero. Rooms and kinds B through F are not rewritten. The resource corpus contains 67 such cells across twelve Normal and Difficult maps and none in the Easy maps.

This establishes that the policy filters access to mature encounters at the map layer. It does not compact the runtime Bible descriptor table: no-mature DOS saves still contain all 46 bank-C records, including selectors E1 through E4.

Grid layout

Each resource is a headerless, row-major 16×16 grid with three bytes per cell:

cell_offset = 3 * (16 * y + x)

+0  packed connection/location byte
+1  parameter A
+2  parameter B

Coordinates range from zero through 15. Disassembly repeatedly computes 48*y + 3*x, including accesses to the four neighboring cells by adding or subtracting 3 for X and 48 for Y. The current X and Y coordinates are words at DS:7290 and DS:7292.

The first byte has two independently used nibbles:

  • The high nibble is a four-direction connection mask: 0x10 is up, 0x20 is down, 0x40 is left, and 0x80 is right. It selects one of 16 connection frames when the map screen is drawn.
  • The low nibble selects a location kind. Shipped scripts replace it while preserving the high nibble. More precisely, opcode 0x7B preserves the old high nibble and ORs it with the low byte of a script variable; the handler does not mask that variable to four bits. All shipped callers use only 0x00, 0x05, 0x0A, 0x0B, and 0x0C, so they have the intended low-nibble effect. Several numeric kinds can be correlated with map glyphs and gameplay branches, but a complete symbolic enumeration is not yet justified.

Parameters A and B are also manipulated separately by scene commands. The map screen uses them as text-record selectors for at least location kinds 0x6 and 0xA, consistent with the manual’s statement that stations and communication locations show verse references. Other meanings remain open.

Room encoding and dispatch

A zero connection mask changes the meaning of low kinds 0x1 through 0xF. They form five room classes of three entrance orientations each:

room_class     = (location_kind - 1) / 3
entrance_code  = (location_kind - 1) % 3

Both operations use integer quotient and remainder. The executable writes them to script variables 13 and 14. A static table at load offset 0xED7A contains the same orientation sequence for neighbor detection.

Low kindsClassSceneMap letter
0x10x3VictimLevel-specific JELO, FEAR, CULT, LAW, RICH, DENY, or NAGEV
0x40x6TrapROOM1T
0x70x9PrayerROOM2P
0xA0xCCommunicationsROOM3C
0xD0xFJump TunnelROOM4J

The scene-resource identities provide independent confirmation. ROOM1 loads TRAP, TRAP2, and TRAP3; ROOM2 loads PRAY; ROOM3 loads COMM, COMM2, and FACE1; and ROOM4 loads TUNNEL, TUNNEL2, and MONST1. The hall programs dispatch victim class zero to the seven named victim scenes and patch the final digit of room1 for the other four classes. These five classes are also exactly the T, P, C, J, and V rooms described by the manual and rendered by the map screen.

Kind within each classRoom position from hallEntrance side
First (1, 4, 7, A, D)Right / eastWest
Second (2, 5, 8, B, E)Left / westEast
Third (3, 6, 9, C, F)Above / northSouth

There is no encoding for a room below a hall cell. The executable supports all 15 class/orientation codes, but the 21 shipped maps use only 14: no map contains a south-entry Jump Tunnel (0xF) with a zero connection mask.

Room codes and connected-hall codes are separate contexts. For example, a zero-mask 0xA is a Communications room, while kind 0xA on a connected hall cell participates in station and post-encounter behavior. Treating the low nibble as one global enum would incorrectly merge these states.

The parameters are likewise class-specific. process_current_map_cell copies the current room’s parameter A and B to variables 17 and 18. Trap scripts use parameter A as a study-prompt selector and clear it after the interaction. When a Trap room is adjacent to a hall, its parameter B becomes one of three contextual prompt values in variables 23 through 25; a correct study result clears that byte in the adjacent cell. This identifies both bytes as mutable encounter state in that class without assuming that they have the same meaning in Prayer, Communications, or Jump Tunnel rooms.

Connected hallway features

When the connection mask is nonzero, the low nibble describes the hallway cell rather than a room. The following meanings have independent script, resource, or transition evidence:

KindHall featureEvidence
0x0Empty connected hallwayNo feature branch or parameters.
0x1Macho CyberSelects COMBAT1, whose enemy resources are BIG*.
0x2Armored CyberSelects COMBAT2, whose enemy resource is HELMET.
0x3Mantis CyberSelects COMBAT3 and MANTIS*.
0x4Snake CyberSelects COMBAT4 and SNAKE*.
0x5Spider CyberSelects COMBAT5 and its internally named CRAB art.
0x6Leech-covered Scripture stationSelects COMBAT6 and GUARD*; victory restores a station.
0x7Zapper CyberSelects COMBAT7 and ZAP*; passing beneath it damages faith.
0x9Hidden Spider triggerA successful trigger replaces it with kind 0x5.
0xAScripture stationEnables Get Verse using parameter A.
0xBCleared encounterWritten by ordinary combat victories.
0xELevel exitEvery hall program branches from this kind to its exit sequence.

The seven hall programs dynamically patch SML1 to load SML1 through SML7, and their Confront Cyber actions enter POWER. That scene patches combat1 from the current kind and returns to COMBAT1 through COMBAT7. This joins the map kinds, hallway sprites, and combat resources without depending only on visual resemblance. The manual supplies the player-facing Cyber names. CRAB is the Spider: the separate kind-0x9 ambush state conditionally turns into kind 0x5, matching the documented Spider that can drop behind Captain Bible.

Kind 0x6 has the strongest transition evidence. Its special combat victory writes kind 0xA, copies parameter B to parameter A, and clears parameter B. The result is a normal Scripture station whose verse selector is now in the field used by Get Verse. This matches the manual’s Leech Cyber, which sits on top of Scripture stations. Kind 0x7 can be walked under, but its hall branch applies base faith loss 400; defeating it restores full faith in COMBAT7. Both behaviors match the Zapper description.

Cyber parameter A selects the lie used by the confrontation. The covered station’s parameter B is preserved as the verse selector revealed after the Leech is defeated. A normal station uses parameter A, sets the corresponding text-record state, and displays Verse loaded: & when Get Verse succeeds. The dialogue formatter expands & to the selected record’s complete citation - verse text.

Connected kinds 0xC, 0xD, and 0xF control visual or environmental hall states, but their exact player-facing meanings remain unproven. Kind 0x8 does not occur on a connected cell in any shipped map. The inspector leaves all four unnamed rather than folding them into the entity table.

The hall action selectors now also have direct labels:

SelectorAction
.u, .d, .l, .rMove Up, Down, Left, or Right
.cConfront Cyber
.xUnlock
.vGet Verse

The three Unlock targets operate on a locked Trap-room door to the right, left, or above the hall. They use the adjacent room’s parameter B as the study prompt and clear it after the correct verse is applied. This separates the door lock from parameter A, which controls the encounter inside the Trap room.

Runtime state and scene commands

The loaded resource becomes mutable gameplay state. The following commands have direct support in their handlers:

OpcodeOperandsEffect
0x77noneProcess the current cell, consulting adjacent cells and current state.
0x78BIf the level changed, load its difficulty map, apply no-mature rewriting, and clear exploration rows.
0x7BHPreserve the current high nibble and OR in a script variable’s low byte. Shipped values are all valid low-nibble kinds.
0x7CHSet parameter A from a script variable.
0x7FHSet parameter B from a script variable.
0x87noneNormalize location cells after loading or state changes.
0x89noneMark the current coordinate explored.

Opcode 0x89 updates a separate 16-word row bitmap at DS:72C4:

explored_rows[y] |= 1 << x

The F2 map screen tests this bitmap while rendering its 16×16 display. This agrees with the manual: explored areas appear gold, unexplored areas gray, stations and communication points display verse references, and room types are marked with the letters P, J, T, C, and V.

The normalization routine at 0x0457 proves that resource bytes are not immutable identifiers. In one pass it changes low kind 0x6 to 0xA, moves parameter B to parameter A, and clears parameter B. In another relevant pass, location kinds 0x1 through 0x9 become 0xB. Further control-flow work is needed to name the conditions and gameplay states behind those transitions.

Combat programs perform closely matching transitions at their victory epilogues. COMBAT1 through COMBAT5 and COMBAT7 replace the current low kind with 0xB. The guard encounter in COMBAT6 instead writes kind 0xA and copies parameter B to parameter A. Every RETREAT action jumps around these writes into the shared scene-exit path, so retreat preserves the encounter cell. These scripts prove that 0xB is a completed/cleared form for ordinary combat locations and connect the special 0xA transition to the guard encounter, although the broader uses of both kinds remain more general than those labels.

Save-state correlation

Both the live grid and its checkpoint copy are serialized in every 2,752-byte state file. They occupy file offsets 0x4C0 and 0x7C0, respectively. The supplied DDGAMES.SV3 and DDGAMES.SV4 grids match CE.MAP except for four field changes:

CoordinateFieldResourceSaved
(2,0)parameter B0x380x00
(0,1)parameter A0x370x00
(1,1)packed byte0xA20xAB
(2,1)packed byte0xE50xEB

The two packed-byte mutations preserve their high nibbles and replace their low kinds with 0xB, exactly as the normalizer does. This byte-level match independently identifies the saved 16×16×3 tables as world-map state. The other supplied saves have zeroed grids and cannot be assigned to an archive map from this field alone.

Inspection tool

Inspect a resource directly from the archive:

tools/inspect_map.py CB/DD1.DAT --map CE
tools/inspect_map.py CB/DD1.DAT --map CE --cells
tools/inspect_map.py CB/DD1.DAT --map CE --rooms
tools/inspect_map.py CB/DD1.DAT --map CE --hall-features

The compact display prints the low location-kind nibble at every coordinate. --cells adds the packed byte, named connection directions, kind, and both parameters for every nonzero cell. --rooms lists decoded room class, entrance side, and parameters. --hall-features lists only connected cells with proven nonempty features; unresolved environmental kinds remain visible in --cells. Compare a resource with the live grid in a state save with:

tools/inspect_map.py \
  CB/DD1.DAT --map CE --compare-save CB/DDGAMES.SV3

The parser requires an exact 768-byte grid and a valid level/difficulty name. Tests cover all 21 archive members, row-major addressing, connection directions, the complete encoded room domain, the 14 combinations present in the corpus, room/victim scene resources, all seven script level selectors, hall features and combat resources, the hidden-Spider transition, invalid inputs, and the four saved mutations above.

Relevant executable functions and data

Load offset / DS offsetCurrent name or role
0x034Fload_map_resource
0x0457normalize_map_cells
0x075Fshow_map_screen
0x0C6Cprocess_current_map_cell
0xED7A16-byte kind-to-entrance-code lookup table
DS:5B16Live 768-byte grid
DS:7290 / DS:7292Current X / Y coordinates
DS:72C4Sixteen explored-row bitmaps
DS:76ECCheckpoint 768-byte grid

Offsets use the unpacked load-module convention documented elsewhere in this book.