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.
| Opcode | Operands | Current name | Corpus evidence |
|---|---|---|---|
0x14 | p | show_adversary_dialogue | Ten uses, all in FACE.BIN, spoken by the Tower of Deception. |
0x48 | p | show_character_dialogue | 306 uses in 12 resources for the boss, victims, and allies. |
0x4E | p | show_captain_bible_dialogue | 281 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 / offset | Size | Meaning |
|---|---|---|
DS:B428 | 2 | Number of active choices. |
DS:B116 + 6*n + 0 | 2 | Absolute target offset in the current BIN resource. |
DS:B116 + 6*n + 2 | 2 | Text pointer offset. |
DS:B116 + 6*n + 4 | 2 | Text 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:
clear_dialogue_choices(0x45, handler0x51B7) sets the count and dialogue state to zero.add_dialogue_choice(0x44, handler0x51C6) stores the absolute target and far string pointer, then increments the count.present_dialogue_choices(0x46, handler0x5257) enters state 1 and suspends the current scene thread.select_from_text_menuat0x2556draws the entries and returns the target word belonging to the chosen row.poll_input_eventwrites that target toDS: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:
| Choice | Source | Target | Text |
|---|---|---|---|
| 0 | 0x045C | 0x0644 | So what do I do when I get inside? |
| 1 | 0x0482 | 0x07E8 | Can I expect any resistance? |
| 2 | 0x04A2 | 0x0751 | What about the people inside? |
| 3 | 0x04C3 | 0x0519 | Should I expect any problems with my computer bible? |
| 4 | 0x04FB | 0x095C | OK! 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 value | Companion component shown |
|---|---|
0x00, 0x09 | No prompt. |
0x2A (*) | Victim-conversation text. |
0x64 | P paraphrase text. |
| other nonzero | L 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 offset | Current name |
|---|---|
0x1C88 | show_study_bible |
0x2556 | select_from_text_menu |
0x2933 | show_dialogue_message |
0x446F | render_study_prompt |
0x4672 | Configure-study-prompt handler. |
0x51B7 | Clear dialogue-choice handler. |
0x51C6 | Add dialogue-choice handler. |
0x51FF | Remove dialogue-choice handler. |
0x5257 | Present dialogue-choice handler. |
0x52A3 | Shared dialogue-message handler. |
0x5351 | Study-Bible request handler. |
0x5357 | Suspend-scene-thread handler. |
0x7BED | poll_input_event |
0x834E | handle_study_bible_request |
0xAE42 | Load the STUFF.ART font and build glyph offsets. |
0xAF36 | Draw proportional seven-row glyphs. |
0xBE7E | Draw a text object with a three-index style mapping. |
Offsets use the unpacked load-module convention documented elsewhere in this book.