# 10 — NPC Dialogue Integrity Check

**Date:** 2026-06-03
**Scope:** Determine whether NPC hint dialogue survived extraction of `BN Online Client 5-8.gmd` into `extracted/client-5-8/`.
**Method:** Read NPC object events + room `instances.json` per-instance creation code + extraction tool source. No hex inspection (per Hard Rule 4/5).

---

## VERDICT: ✅ PRESENT — dialogue fully survived extraction

NPC dialogue is **present in the extracted data** (option **a**). It lives in the
**per-instance creation code** field, exactly as the project owner hypothesised — and
the extraction tool **does** capture that field. There is **no data loss** and **no
re-extraction is required**.

- The room extraction format **includes** a per-instance `creationCode` field.
- For NPC instances it is **populated** with the actual dialogue (`name`, `say`/`say[]`
  arrays, `sayings`, `saytype`) plus movement behavior (`pcode_*` calls).
- **64 of 66** NPC instances across 11 rooms carry populated creation code. The 2
  "empty" ones are legitimate source defaults (they intentionally fall back to the
  object's built-in `nullcheck` default text), **not** dropped data.

---

## Evidence

### 1. Dialogue mechanism — the object reads per-instance variables

NPC object `npc_greenprog` (id 376) is the base class; `npc_dbprog` (377),
`npc_redprog` (493), `npc_fhqwhgads` (459) inherit from it via `parentId`.
`npc_reaprog` (413) is standalone.

`objects/0376-npc_greenprog/events/Create.gml`:

```gml
if(nullcheck(name))
  name = "Mr. Prog";
if(nullcheck(say))
  say = "Hello, I'm Mr. Prog. I'm a simple program with routine purposes.";
if(sayings == 0)
  sayings = 1;
```

The defaults are guarded by `nullcheck(...)` — they apply **only if instance creation
code did not already set them**. This is the smoking gun the owner predicted: the real
per-NPC text is injected by creation code that runs *before* Create's fallbacks take
effect.

`objects/0376-npc_greenprog/events/KeyPress-35.gml` (talk handler) emits the text:

```gml
caddline("<" + name + ":> " + string_upper(say[saynum]), global.c_whisper);
if(sayings > 1 && saytype == 0) { saynum += 1; if(saynum >= sayings) saynum = 0; }   // cycle
else if(sayings > 1 && saytype == 1) saynum = abs(floor(random(sayings-0.1)));        // random
```

So dialogue is an **array** `say[]` of length `sayings`, advanced by `saytype`
(0 = sequential, 1 = random). `Step.gml` and `Alarm-1/2.gml` additionally
`execute_string(stepcode/alarm1/alarm2)` — arbitrary per-instance behavior, also
sourced from creation code. None of this text is in the object events; it MUST come
from instance creation code — and it does.

### 2. The room format HAS per-instance creation code, and it is POPULATED

`rooms/0058-BNCentral/instances.json` is a JSON array (6141 instances). Each element:

```json
{ "creationCode": "...", "instanceId": 128992, "objectId": 51, "x": 88, "y": 6160 }
```

Sample NPC instances from BNCentral (objectId 376 = greenprog), creation code intact:

- **instanceId 138154 "Pallikos"** @ (3817,2430): a 13-line `say[0..12]` array with
  `sayings = 13; saytype = 0;` — includes the genuine onboarding hint
  *"...ctrl+r toggles running. Try it to move faster!"*
- **instanceId 138390 "B. U. Gaito"** @ (4026,5560): *"...southwest you will find a path
  to Elder Wood; southeast of here lies Propeller Square... northwest leads back to
  Centripetal Square!"* — a directional/navigation hint NPC.
- **instanceId 138391 "Nelelle"** @ (2244,3390): *"...there's some cool rainbow thing
  down below, and to the west is the Oldpeople Wood... Above Is the Abyssal Approach..."*
- **instanceId 138393 "Coptre"** @ (5885,4070): compass-style hint string
  `"<Tropical Suitcase message board | >Thermal Pocket | \/Bug Station | ^Centripetal Square"`.

These are exactly the spatial-hint hint-givers described in the mission.

### 3. REAPROG abyss wanderer — present, path-driven

`objects/0413-npc_reaprog/events/Create.gml` sets its own defaults
(`name = "REAPROG"`, `say = "NHEH NHEH NHEH NHEH NNNHEH ..."`) and starts a wander
path (`path_start(reapath_1/reapath_2, ...)`, waypoints `pathx/pathy[1..2]`).
`Other-7.gml` / `Other-8.gml` implement its fade-out / teleport-respawn wandering
through the Digital Abyss. The single reaprog instance lives in room
`0049-Digital_Abyss` (populated creation code = 1/1). Behavior fully intact.

### 4. Coverage across all rooms

| Room | NPC instances | populated cc | empty cc |
|---|---|---|---|
| 0004-Online_Lobby | 13 | 13 | 0 |
| 0046-Prairie_Flats | 9 | 9 | 0 |
| 0047-Database | 13 | 11 | 2 |
| 0049-Digital_Abyss | 1 | 1 | 0 |
| 0052-Traverse_Core | 1 | 1 | 0 |
| 0053-Bahoo | 8 | 8 | 0 |
| 0054-Noiya | 1 | 1 | 0 |
| 0055-Schweisstar | 5 | 5 | 0 |
| 0056-Floes_of_Ghennam | 1 | 1 | 0 |
| 0058-BNCentral | 14 | 14 | 0 |
| **TOTAL** | **66** | **64** | **2** |

The 2 empty-cc instances (Database: objectId 377 dbprog @ (638,4900) and objectId 459
fhqwhgads @ (4004,5440)) are **not** data loss — they carry no per-instance code in the
source and intentionally render the object's default text via the `nullcheck` fallback.
This is confirmed normal: in BNCentral, only 30 of 6141 instances have non-empty
creation code, so `""` is the expected value for the vast majority of instances.

### 5. Extraction tool confirms instance creation code is in scope

`tools/extract-gmd/src/reader/rooms.ts` parses each instance from the `.gmd` binary
and explicitly reads the creation-code string (room block version 520 layout):

```
rooms.ts:148-152
  const x = r.readInt32LE();
  const y = r.readInt32LE();
  const objectId = r.readInt32LE();
  const instanceId = r.readInt32LE();
  const instCreationCode = r.readStr();   // <-- per-instance creation code
  ...
  instances.push({ x, y, objectId, instanceId, creationCode: instCreationCode });
```

`tools/extract-gmd/src/types.ts:222` documents the field
(`creationCode: string; // GML plaintext, "" if none`), and
`tools/reader/rooms.test.ts` (RM-04) regression-tests round-tripping of instance
creation-code GML. Per-instance creation code is a first-class, tested part of the
extraction pipeline — it was never out of scope.

---

## NPC function catalog

| Function | Examples (instance / room) | Notes |
|---|---|---|
| Navigation / directional hint-givers | B. U. Gaito, Nelelle, Bwude, Coptre (BNCentral) | `say` strings name adjacent zones and compass directions to hidden/linked areas |
| Onboarding / control hints | Pallikos (BNCentral) | teaches ctrl+r run toggle; multi-line `say[]` Easter-egg cycle |
| Flavor / lore talkers | Guardian Egis, Sir Vish, Brenua Utnis G. (BNCentral) | atmosphere + message-board prompts |
| Abyss wanderer / guide | REAPROG (Digital_Abyss, obj 413) | path-driven (`reapath_1/2`), fade/respawn wander; own default name+say |
| Default Mr. Prog (no custom text) | dbprog @ (638,4900), fhqwhgads @ (4004,5440) in Database | intentionally use object default dialogue |
| Themed prog variants | npc_dbprog (Database), npc_redprog, npc_greenprog, npc_fhqwhgads | sprite/behavior variants of the same talk system |

Hidden-tile / obscure-spot positioning is preserved via each instance's `x,y` in
`instances.json` (e.g. Pallikos @ 3817,2430), so the spatial-hint placement is also
fully recoverable.

---

## Remediation

**None required.** No re-extraction needed. Dialogue, speaker names, multi-line
`say[]` arrays, cycle/random mode (`saytype`), NPC positions, and wander behavior are
all present in `extracted/client-5-8/rooms/*/instances.json` (`creationCode` field) and
the corresponding object events under `extracted/client-5-8/objects/`.

Implementation note for the rebuild: an NPC's full hint text is **not** in the object
definition — it is in the room instance's `creationCode` GML. Any REBNO NPC/dialogue
importer MUST parse `instances.json[].creationCode` (assign `say`/`say[]`, `name`,
`sayings`, `saytype`) and treat the object's Create-event `nullcheck` defaults only as
the fallback when an instance supplies no text.
