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

Conversation Flow

Overview

Conversations are ordinary BIN control flow layered over two reusable user interfaces: a modal dialogue box and a text-choice menu. Scene scripts supply speaker text inline, build choices as target/text pairs, and branch to the target selected by the player. The same study-Bible screen used elsewhere in the game can be opened from a conversation and returns success or failure through state flags.

This system is distinct from the scene display list. Portraits and other characters can be display objects, but conversation branches are absolute BIN offsets and their transient menu records live in a separate table.

Dialogue commands

Three commands share the handler at load offset 0x52A3. The handler chooses presentation parameters from the opcode and stores a far pointer to text in the current BIN resource. The input layer then displays the modal box and waits for Enter or a click to advance it. Escape temporarily transfers control to Game Options without accepting the message.

OpcodeOperandsCurrent nameCorpus evidence
0x14pshow_adversary_dialogueTen uses, all in FACE.BIN, spoken by the Tower of Deception.
0x48pshow_character_dialogue306 uses in 12 resources for the boss, victims, and allies.
0x4Epshow_captain_bible_dialogue281 uses in 25 resources, normally Captain Bible’s side of a conversation.

Here p is either inline NUL-terminated CP437 text or byte 0xFF followed by an explicit 16-bit offset in the same BIN resource. ROOM3.BIN uses the offset form twice, at commands 0x181C and 0x18CE, to reuse text beginning at 0x0336. Those are the only explicit string offsets in shipped code.

The handler has a deliberate retry path that reads no operand. If a modal dialogue is already active, it suspends the command thread at the original command offset. Once the modal state clears, re-execution consumes p and continues. This no-read path is synchronization behavior, not an optional text operand.

Opcode 0x4E is a presentation channel rather than a hard type system. A few scripts reuse it for system or caption text such as Verse loaded: & and the credit screen. The speaker names describe the dominant, independently labelled use rather than a restriction enforced by the interpreter.

The formatter at 0x224F scans the display string for &. When present, it switches to the complete index string of the currently selected text record. That record is stored as citation|verse; while copying it into the temporary display buffer, the formatter replaces | with -. Thus Verse loaded: & expands to the reference and full verse. A literal | in the display template is not a separate verse-only placeholder.

The modal dialogue state word is at DS:8934. State 1 denotes a choice menu; higher positive states select dialogue-box artwork and behavior. Function show_dialogue_message at 0x2933 renders the frame, wraps the far text, and runs the input loop while continuing to update scene animation.

The routine also creates one transient input record at DS:0248. Its anchor is the horizontal text midpoint and logical text Y plus five. Each update passes that record to the shared nearest-object selector at 0x6CE5, which uses the distance helper at 0x3315 and rejects values above 100. The click exit at 0x2ADD requires both a selected record and the primary-button latch. A click outside that proximity region therefore does not accept the message; the full panel and viewport are not implicit continue targets.

The Escape branch at 0x2AAA writes status event 12 to DS:0052, removes the transient dialogue artwork, and returns -1 without clearing the underlying dialogue state. poll_input_event at 0x7C3F converts event 12 back to Escape, and the main dispatcher at 0x8904 opens Game Options. Returning from options causes the still-active dialogue to be drawn again. The common choice selector takes the same event-12 route, preserving both the choice records and current selection. Game Options is therefore an overlay continuation, not a dialogue cancellation.

Both blocking text loops also recognize BIOS key values BBh through C5h. They subtract BBh, store the result as the status event, and return -1; poll_input_event reverses that translation before returning to the main dispatcher. Values BBh..C2h are F1..F8 and reach the status switch only when word 004C enables action selection. F9 (C3h) and F10 (C4h) reach the quick-load and quick-save branches independently of that gate. A blocking Computer Bible, map, or notice routine then returns to the still active text modal, except when loading replaces the current game state. Other keys rejected by the active text loop do not fall through to the scene action matcher. In particular, an Up or Down press cannot activate a directional exploration target behind an ordinary dialogue.

The three bytes written by opcodes 0x5C and 0x5D are logical text X, text Y, and wrap width. The BOSS.BIN values [4, 30, 150] and [162, 89, 150] align exactly with the upper-left and lower-right panels in the preserved capture. FACE.BIN supplies [80, 12, 180] and [80, 170, 180], establishing that the tuple is general panel geometry rather than a small set of speaker identifiers.

The visible type is not a BIOS font. Startup routine 0xAE42 loads frame 0 of STUFF.ART, a 257-by-14 atlas arranged as two seven-row glyph strips, and builds an offset table for bytes 21h through 7Fh. The executable’s width table at DS:3462 makes the font proportional; renderer 0xAF36 advances by width plus one, treats spaces as three pixels, and copies seven source rows. Each logical source pixel becomes a 2-by-2 block on the 640-by-400 presentation surface, with an eight-logical-pixel line pitch.

The atlas has three source values which renderer 0xBE7E maps through a text style. Character and Captain Bible messages use style 2 (1,37,4), while adversary messages use style 7 (15,86,90). The live BOSS framebuffer contains exactly indexes 1, 37, and 4 in the displayed line, independently confirming the normal-dialogue mapping. The CONTINUE caption is the complete sprite in STUFF.ART frame 29 rather than text rendered at runtime; its descriptor (-17,-3,35,7) centers and overlaps it above the panel.

Choice table

Opcode 0x45 clears the current choices and resets dialogue state. Each opcode 0x44 then appends one six-byte entry, and opcode 0x46 presents the result. The runtime structure is:

Location / offsetSizeMeaning
DS:B4282Number of active choices.
DS:B116 + 6*n + 02Absolute target offset in the current BIN resource.
DS:B116 + 6*n + 22Text pointer offset.
DS:B116 + 6*n + 42Text pointer segment.

The choice text normally points directly into the loaded BIN member. Opcode 0x13 removes the first entry whose target word matches its operand and shifts the later six-byte records down. No recovered BIN resource uses 0x13, but its implementation is unambiguous.

The full command lifecycle is:

  1. clear_dialogue_choices (0x45, handler 0x51B7) sets the count and dialogue state to zero.
  2. add_dialogue_choice (0x44, handler 0x51C6) stores the absolute target and far string pointer, then increments the count.
  3. present_dialogue_choices (0x46, handler 0x5257) enters state 1 and suspends the current scene thread.
  4. select_from_text_menu at 0x2556 draws the entries and returns the target word belonging to the chosen row.
  5. poll_input_event writes that target to DS:7CBA. The resumed handler replaces the interpreter cursor with the target, so execution continues at the selected branch.

The input loop clamps keyboard selection at row zero and count minus one. For every wrapped row, 0x25E8..0x26AC constructs a 16-byte selector record. Its first two words are the proximity anchor (text_x + 72,row_y + 2); offsets 8 and 10 hold the separate SELECT artwork anchor. Function 0x6CE5 tests all row anchors with distance helper 0x3315, retains the strictly closest result at distance 100 or less, and therefore resolves equal distances in row order. A pointer move outside all row regions clears the pointer selection rather than retaining the prior keyboard row.

Unselected choices use style 1 (1,7,3) and the selected choice uses style 2 (1,37,4). The adjacent SELECT caption is STUFF.ART frame 28 with descriptor (-12,-3,24,7), including its lettering and border. Thus neither choice label nor continuation label should be synthesized with a host font. The caption anchor is (text_x - 11,row_y + 2) for a text origin of 23 or greater and (text_x + text_width + 12,row_y + 2) otherwise. This selector record family is shared by Game Options, slot lists, and confirmation menus; those interfaces are not rectangular host controls.

The corpus contains 40 choice definitions in six resources, 11 clear commands in six resources, and 14 present commands in seven resources. Those different totals reflect conditional paths and reuse of a previously built menu; a linear listing must not assume that every definition belongs to one runtime menu.

BOSS.BIN example

The introductory conversation constructs one five-choice menu:

ChoiceSourceTargetText
00x045C0x0644So what do I do when I get inside?
10x04820x07E8Can I expect any resistance?
20x04A20x0751What about the people inside?
30x04C30x0519Should I expect any problems with my computer bible?
40x04FB0x095COK! I'd better go do it!

Each answer eventually jumps back to the menu-building region, except the final choice, which advances the introductory sequence. This is ordinary BIN branching; the menu itself does not retain a conversation graph.

The shared text-menu input loop at 0x2556 lowercases an otherwise unmatched keyboard character at 0x27AA, scans displayed strings from row zero at 0x28BA, and compares their first byte at 0x28D0. A match is converted into the same activation event as Enter at 0x28E7. Choice menus therefore support case-insensitive first-letter activation, with the first duplicate initial winning.

The branches at 0x2796..0x2825 additionally map Home and End to row zero and the final row. Up and Down walk over the disabled marker byte 0xFE, while the direct edge assignments do not perform that skip.

Study-Bible integration

Victim conversations repeatedly ask the player to select an appropriate verse. Opcode 0x7D reads a byte plus a script-variable operand and stores a prompt component at DS:0066 and the variable’s current selector value at DS:0068. The study screen’s prompt builder at 0x446F interprets the first value as follows:

Prompt valueCompanion component shown
0x00, 0x09No prompt.
0x2A (*)Victim-conversation text.
0x64P paraphrase text.
other nonzeroL cyber-lie text.

The builder looks up the expected selector directly rather than searching the acquired-verse list. It submits the resolved text to the general wrapped-text renderer with logical X 6, Y 22, width 309, and at most three lines on an eight-pixel pitch. A vertical bar forces a new line. L prompts use text style 7, P prompts use style 9, and * prompts use style 10. Because the open-book chassis begins at logical Y 51, these three possible rows occupy the black space immediately above it.

Opcode 0x49 sets the study request at DS:79F0 and suspends the current thread. The main game loop clears the request and calls handle_study_bible_request at 0x834E, which invokes the full study browser at 0x1C88. The browser clears state flags 0x14 and 0x15 before input. Its key branch at 0x1E2C recognizes ASCII 16h (Ctrl+V), finds the configured selector from DS:A0EA, and changes the current record only when the corresponding acquired-state byte is nonzero. The normal redraw then places that record on its 14-row page; the shortcut does not apply it. Selecting the expected descriptor sets flag 0x14; leaving without that match sets flag 0x15. Scripts branch on those flags around opcode 0x72, which applies a one-update scheduler yield rather than another modal suspension.

For example, NAGE.BIN sets variable 28 to a text-record selector, executes configure_study_prompt 0x2A, var[28], requests the study screen, and then branches to success, rejection, or departure paths according to flags 0x14 and 0x15. This ties the conversation graph directly to the text descriptors and companion * records documented in the text-format chapter.

QEMU validation

A visible, silent ./run.sh --trace-dos session was advanced from the title screen to the BOSS conversation. At the five-choice menu, the stable game data segment was 14E1. The one-megabyte physical-memory capture therefore placed the count at physical 0x20238 and the table at physical 0x1FF26.

The live count was five. All five records contained the exact static target words above and far pointers into BOSS.BIN at 4C13:045F, 4C13:0485, 4C13:04A5, 4C13:04C6, and 4C13:04FE. Dereferencing those pointers produced the five visible menu strings byte for byte.

Before selection, DS:8934 was 1 and DS:7CBA was zero. Selecting the final row changed DS:7CBA to 0x095C; the next visible dialogue was Before you go, I think that we should pray., exactly the opcode-0x48 string at target 0x095C. The resulting dialogue state was 2. QEMU was then stopped through its monitor.

The ignored evidence files are under build/qemu-trace/, including conversation-menu2.png, conversation-menu2-physical-1m.bin, conversation-selected.png, and conversation-selected-physical-1m.bin.

Inspection

The ordinary BIN listing now uses semantic names for the recovered dialogue, choice, study-request, and thread-suspension commands. Add --choices for a compact linear inventory of every opcode-0x44 definition:

tools/inspect_bin.py build/dd1/all/327_BOSS.BIN --choices

The summary prints source offset, target offset, and text. It explicitly warns that branches can change the menu seen at runtime.

Remaining boundaries

  • The exact artwork role of every positive dialogue state is not yet named.
  • Conversation choices and verse answers alter progression flags, but they do not expose a separate enemy-health, hit-test, or combat-entity structure.

Relevant executable routines

Load offsetCurrent name
0x1C88show_study_bible
0x2556select_from_text_menu
0x2933show_dialogue_message
0x446Frender_study_prompt
0x4672Configure-study-prompt handler.
0x51B7Clear dialogue-choice handler.
0x51C6Add dialogue-choice handler.
0x51FFRemove dialogue-choice handler.
0x5257Present dialogue-choice handler.
0x52A3Shared dialogue-message handler.
0x5351Study-Bible request handler.
0x5357Suspend-scene-thread handler.
0x7BEDpoll_input_event
0x834Ehandle_study_bible_request
0xAE42Load the STUFF.ART font and build glyph offsets.
0xAF36Draw proportional seven-row glyphs.
0xBE7EDraw a text object with a three-index style mapping.

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