# 09 — NPCs, Collectable Economy & New-Player Onboarding

Reverse-engineered from `extracted/client-5-8` (client GML) and `extracted/server-5-4`
(server GML). All file:line refs are to those trees. Evidence-based; guesses are flagged
**[INFERENCE]**.

The player avatar is the object **`0000-server`** (referred to everywhere as `server`).
"Press the action key" = `KeyPress-35` (the **End** key; see `0004-CmdRec.gml:130` game-help).
`smobilecheck()` = "player is free to act" gate. Inventory lives in the global 2-D array
`global.iv_itemdb[category, index]`; `[cat,0]` is the count, `[cat,1..n]` are item strings.

Inventory categories (from `client-5-8/scripts/0302-iv_datagather.gml` + `0034-title/events/Other-2.gml:207`):

| Cat | Name | Contents |
|-----|------|----------|
| 0 | (system/unused) | reserved; `iv_totalcats = 6` |
| 1 | Key data | Plugn navi-expansions (id 1-6) + moderator tools (900-909) |
| 2 | Friend data | `uid|Name` entries; whisper/join/summon actions |
| 3 | Link data | **zone links** (`main=1`) and **area links** (`main=0`) |
| 4 | Message data | saved message-board posts |
| 5 | Spectrum data | Navi color-customization parts (`set|color`) |

There is **no currency**. The entire economy is *keyed unlocks* (key items) plus
*collected data* (link data, spectrum parts). Nothing is bought or sold.

---

## NPCs

NPCs are pure **dialogue / flavor** objects. None give items, none give quests, none act
as shopkeepers. They are "Mr. Prog" programs (a BattleNetwork motif).

### `0376-npc_greenprog` — "Mr. Prog" (parent of all talking NPCs)
- `Create.gml:9-24`: defaults `name="Mr. Prog"`, `say="Hello, I'm Mr. Prog. I'm a simple
  program with routine purposes."`, `sayings=1`, `cantalk=1`. Sprites `GProgram*`.
- `KeyPress-35.gml:2-18`: when the player is within 30px and facing it (directional
  `collision_rectangle` check), prints `<name:> SAY` in whisper color
  (`global.c_whisper`). Multi-line NPCs cycle (`saytype==0`) or random (`saytype==1`)
  through `say[saynum]`. 90-step talk cooldown (`alarm[0]=90`, reset in `Alarm.gml:2`).
- `Alarm-1.gml` / `Alarm-2.gml`: each `execute_string(alarm1/alarm2)` — generic
  per-instance scripted-behavior hooks (configured in room instance creation code).
- BNCentral alone places **14** `npc_greenprog` instances (room `0058-BNCentral`
  instances.json) — they are scattered ambient townsfolk.

### Color variants (all `parentId=376`, i.e. inherit Mr. Prog)
- `0377-npc_dbprog` — Database-blue prog (`Create.gml:4-7`, `DBProgram*` sprites).
- `0493-npc_redprog` — red prog (`RProgram*` sprites).
- `0459-npc_fhqwhgads` — "Fhqwhgads" static talking easter-egg NPC (default `say="..."`).

### `0413-npc_reaprog` — "REAPROG" (standalone, Digital Abyss)
- `Create.gml:11-32`: roams via `path_start(reapath_1/reapath_2)`, `pushnavi=1`,
  `cantalk=1`, default `say="NHEH NHEH NHEH NHEH NNNHEH ..."`.
- `Step.gml:9-16` + `Other-7/8.gml`: when the player approaches within 80px **while
  moving**, it `path_end()`s and **fades away** (`DAProgramFade` → invisible →
  re-spawns elsewhere after a random delay). A ghostly, evasive Digital-Abyss creature.
- `Step.gml:4-7`: per-instance `execute_string(stepcode)` hook.

**Verdict:** NPCs are atmosphere. All progression items come from world drop objects and
portals, never from NPCs. **[INFERENCE]** "REAPROG" reads as a reaper/jump-scare flavor
entity tied to the Digital-Abyss "fall" hazard.

---

## Collectable Economy

All collectables share the same pickup gesture: stand within ~30px, face the object,
press **End** (`KeyPress-35`). Optional `passkey` (typed via `keyboard_string`) locks some
drops (`d_key`/`d_link`/`d_spectrum` KeyPress: "This data is locked, and requires a
passkey." e.g. `0425-d_link/events/KeyPress-35.gml:7,19`).

### How a pickup is placed and picked up
- Placement: object instances are dropped in a room (room `instances.json`); their
  per-instance `creationCode` sets `name`/`tname`/`keyid`/`set`/`color`/`passkey`.
- Pickup writes to the inventory via `iv_additem(cat, datastring)`
  (`client-5-8/scripts/0323-iv_additem.gml`), which appends the string and immediately
  calls `iv_updateserver()`.
- `iv_updateserver()` (`scripts/0324-iv_updateserver.gml`) **sends the entire inventory
  to the server** as packet **8** (`writebyte(8)` + every category + every item string).

### "Already collected" tracking — PERMANENT, server-side
- Inventory is **per-account, server-authoritative, permanent**. The server stores it in
  `UserData\Inv\Inventory_<uid>.bnu` (`server-5-4/scripts/0377-uinv_restore.gml:1`,
  written by `0376-uinv_backup.gml`). On login (packet 5 → server) the server replies
  with the saved inventory (`server-5-4/scripts/0359-server_receive.gml:209-216`).
- A drop checks at spawn whether it is already owned and self-destructs if so:
  - `d_link Create.gml:19-23`: `if(!got && iv_itemsearch(3,datastring)>0){got=1;
    instance_destroy();}`.
  - `d_key Alarm.gml:4-8`: same against category 1.
  - `linkpoint Alarm-1.gml:5-8`: same against category 3.
- So pickups are **one-time per account, permanent (do NOT respawn)**. There is no
  per-session respawn; "already collected" is the presence of the item string in the
  account's saved inventory (`iv_itemsearch`, `scripts/0320-iv_itemsearch.gml`).
- **Exception — `d_spectrum` does not destroy**: on pickup it greys out
  (`sprite_index = DataBW; nocollide = 1`) but the instance stays
  (`0418-d_spectrum/events/KeyPress-35.gml:22-25`), and re-greys at spawn if already owned
  (`Create.gml:19-24`). Visual "you already have this color" marker.

### The four drop objects

**`0425-d_link` — link data drop (zone or area).**
- Datastring built in `Create.gml:16`:
  `main|1]name|2]tname|3]zone|4]zx|5]zy`. `main` defaults 0 (area link); `name` defaults
  `"[Z] BNCentral"`.
- Pickup `KeyPress-35.gml:9-15`: prints "Link data found! … ~ Link data for a zone/an
  area." then `iv_additem(3, datastring)`, destroys.
- **No `d_link` instances exist in any extracted room.** Links in the live world are
  served by `linkpoint` and `hometele` (below); `d_link` is the generic placeable variant
  (also used by the mod "Location" tool, `scripts/0347-iv_keyactions.gml:250`).

**`0427-linkpoint` — in-world AREA-link beacon (the visible link pillars).**
- `Alarm-1.gml:2-3`: datastring = `"0|1]"+name+...` → always `main=0` (**area** link),
  zone = current `global.zone`.
- Pickup `KeyPress-35.gml:8-15`: "Area link data for <tname> acquired" → `iv_additem(3,…)`.
- **Gated by the zone link**: `Alarm-1.gml:10-13` + `Draw`/`Step` — if
  `iv_zonesearch(global.zone) <= 0` the linkpoint shows `LinkPointInDead` and is inert.
  You must own the zone's main link before its area links can be collected.
- Counts (extracted rooms): BNCentral 5, Online_Lobby 3, Prairie_Flats 3, Database 4,
  Bahoo 3, Floes 2, Traverse_Core 1, Schweisstar 0.

**`0442-d_key` — key-item drop (Plugn navi-expansions + mod tools).**
- `Create.gml:10` arms `alarm[0]=2`; `Alarm.gml:2-8` sets `datastring = string(keyid)` and
  destroys if already owned.
- Pickup `KeyPress-35.gml:9-24`: "Key item <icon> found! <name> - <desc>" →
  `iv_additem(1, string(keyid))`. **Special case keyid 5 (DuoPlugn)** also sets
  `global.canduo=1` and notifies the server via packet 22 (`KeyPress-35.gml:11-21`).
- keyid → item (defined `0034-title/events/Other-2.gml:99-127`):
  1 HxpPlugn (hexporting), 2 **RmtPlugn (remote teleport)**, 3 MskPlugn (spectrum
  compatibility), 4 TakABrak.exe (easter-egg), 5 DuoPlugn (two-navi duo), 6 WrpPlugn
  (local warps). 900-909 are moderator tools.
- **Where the Plugns live:** all four core expansions are dropped in **Emptiness_Hull**
  (room `0048`, creation codes: HxpPlugn keyid 1, RmtPlugn keyid 2, MskPlugn keyid 3,
  DuoPlugn keyid 5). **Traverse_Core** (room `0052`) holds the 10 moderator-tool keys, each
  behind a `passkey` (e.g. `keyid=900; passkey="tammygoose"`).

**`0418-d_spectrum` — Navi-color-customization part.**
- Datastring = `set|color` (`KeyPress-35.gml:21`); `iv_additem(5, …)`. set 1=body/helmet,
  2=glove/boots, 3=trim, 4=stripe, 5=core circle.
- **Requires MskPlugn**: pickup only fires when `global.iv_itemdb[5,0] > 0`
  (`KeyPress-35.gml:8`) — **[INFERENCE]** category-5 nonzero is bootstrapped by owning
  MskPlugn / starting colors; see Avatar/Spectrum recon doc 08. Cosmetic only.

### The Central / Home Portal — `0058-hometele`
This is the object the mission calls the "Central Portal." It is the **zone link
dispenser** and **remote-teleport enabler**, one per zone.
- `Alarm-1.gml:2`: datastring = `"1|1]"+name+"|2]"+tname+"|3]"+global.zone+"|4]0|5]0"` →
  `main=1` ⇒ a **ZONE link**.
- Pickup `KeyPress-35.gml:7-20`: "**Zone link data for <tname> acquired**" →
  `iv_additem(3, datastring)`, AND **revives every dead linkpoint in the zone**
  (`with(linkpoint){ if(sprite_index==LinkPointInDead) sprite_index=LinkPointInner }`).
  So touching the zone portal unlocks that zone's whole area-link economy.
- BNCentral's hometele instance is `name="[z]BNCentrl"`, `tname="BNCentral"`
  (room `0058` instances.json).
- Acts as a **portal anchor**: when the player object collides with `hometele` (id 58),
  `0000-server/events/Collision-58.gml:2` sets `gportal=1` (reset 2 steps after leaving,
  `Alarm-5.gml`). `linkpoint` collisions do the same (`Collision-427.gml:2`). `gportal`
  lets you use the teleport menu even without RmtPlugn (see Onboarding/teleport gate).

### Remote teleport (the "use a link" action) — `scripts/0347-iv_keyactions.gml:105-156`
Selecting a Link-data item → "Teleport" runs a gate that requires ALL of:
1. you own the **destination zone link** (`iv_zonesearch(destZone) > 0`), AND
2. you own the **source zone link** OR are at a portal (`iv_zonesearch(curZone)>0 ||
   zone==3 || server.gportal`), AND
3. you own **RmtPlugn** (`iv_itemsearch(1,'2')`) OR are at a portal (`server.gportal`),
4. not zone 4 (Digital Abyss), not in a duo.
Failure messages spell out the design: *"Without a navi expansion, you are unable to
remotely teleport. You must be at a central area portal or a link point to use link
data."* (line 142). So: **link points / the zone portal are the no-expansion teleport
kiosks; RmtPlugn lets you teleport from anywhere.**

Other movement objects (not collectables):
- `to*` objects (`teleparent`, e.g. `0319-todesertedlobby`, `0366-toemptinesshull`,
  `0531-tobncentral`, `0532-toocs`): **physical in-world portals** between adjacent zone
  rooms. Walk on, press End → `online_room(area)` (`teleparent/KeyPress-35.gml`,
  `scripts/0095-online_room.gml`). **No link data required** to walk through them.
- `ac_*` objects: invisible **area-name triggers** — `Collision-0` with the player calls
  `change_area("…")` (`scripts/0117-change_area.gml`); pure label/chat-scope changes.
- `0516-loctele`: intra-zone local warp pad (paired by `ltid`); gated by WrpPlugn (key 6).
- `*_mb` objects (`mbparent`, `bncentral_mb`, …): **message boards**, not portals. End key
  requests a board via packet 13. Lore/community boards (see recon doc 07).

---

## Onboarding / Starting State

### New account creation
- Accounts are **created out-of-band**, not in-game. The server authenticates packet-5
  logins against `localList.txt` (`server-5-4/scripts/0392-users_load.gml`); there is **no
  in-game registration path** and **no default-item grant**. (CLAUDE.md Hard Rule 2: the
  REBNO rebuild replaces plaintext `localList.txt` with argon2id.)
- A brand-new account has **no `Inventory_<uid>.bnu` file**, so the server returns **empty
  inventory** for every category (`server_receive.gml:209-216` via `uinv_get`, which yields
  0/empty for an unseen UID). ⇒ **new players start with: no Plugns, no link data, no
  spectrum parts, no friends.**

### Starting connection state (server) — `server-5-4/scripts/0360-init_user.gml`
- `p_name="UNDEFINED"`, `p_spr=NaviStandD`, `p_room=Online_Command_Screen` (line 23-25).
- Sends client its player id (pkt 6), UDP port (pkt 7), online-player list (pkt 9).

### Starting client state — `client-5-8/objects/0034-title/events/Other-2.gml`
- Default name `"Client"`, ip `127.0.0.1` (lines 29-30). `homedata` starts `-1`.
- Default Navi colors set if none saved: DB / B / Y / LB / R
  (`Other-2.gml:39-46`; re-asserted on login at `0094-begin_client_receive.gml:87-94`
  whenever the account lacks key item "3"/MskPlugn — the new-account default look).

### The spawn room is the OCS, NOT the Deserted Lobby
- After login the player is in **`Online_Command_Screen`** (OCS, room `0007`, zone -1) — a
  text console, not a walkable map.
- `0045-commandob/events/Create.gml:25-39`: **if `homedata != -1`** the player is
  auto-transferred on login to their saved **Home Portal** zone/area. A new account has
  `homedata=-1`, so it **stays in the OCS** showing
  *"::Online Command Screen. Type [command]…"* (`commandob/Step.gml:2`).
- Home Portal is set by the inventory "Set As Home" action on any owned link
  (`scripts/0347-iv_keyactions.gml:157-181`, persisted by `save_settings()`); "Reset Home"
  returns it to the OCS.

### Escaping the OCS — the free entry point is BNCentral, not the Deserted Lobby
- OCS `goto` commands (`scripts/0004-CmdRec.gml:3-13`): every `goto <zone>` is gated by
  `iv_zonesearch(N) > 0` (own that zone's link) **except**:
  - **`goto bncentral` is UNGATED** (line 13) — any new player can enter **BNCentral**.
- So the **Deserted Lobby (`Online_Lobby`, zone 0) is NOT the spawn/tutorial** zone; it is
  just another zone gated behind its own zone link (`goto deserted lobby` needs
  `iv_zonesearch(0)`). Its room caption is literally "…Deserted Lobby"
  (`rooms/0004-Online_Lobby/meta.json`). **[INFERENCE]** the name "Deserted" + the gate
  suggests it was an early/low-traffic area, not the newbie funnel.

### Zone map (`scripts/0326-zone_room.gml`)
0 Online_Lobby(Deserted Lobby) · 1 Prairie_Flats · 2 Database · 3 Emptiness_Hull ·
4 Digital_Abyss · 5 Traverse_Core · 6 Bahoo · 7 Noiya · 8 Schweisstar ·
9 Floes_of_Ghennam · 10 BNCentral · -1 Online_Command_Screen.

### New-player flow (assembled)
1. Log in → **OCS** (text console), empty inventory, default colors.
2. Type **`goto bncentral`** (the one ungated command) → arrive in **BNCentral** (the hub).
3. Walk to BNCentral's **`hometele`** ("[z]BNCentrl"), press **End** → receive the
   **BNCentral zone link**; this revives BNCentral's 5 linkpoints.
4. Collect **area links** at linkpoints and walk through **`to*` portals** to neighbours.
5. Reach **Emptiness_Hull** to grab the four **Plugns** (esp. **RmtPlugn**, key 2) →
   unlock remote teleporting from the inventory.
6. Each new zone's `hometele` grants that zone's link; that link is what lets `goto <zone>`
   and remote-teleport work and what re-activates that zone's linkpoints.
7. Optionally "Set As Home" on a link to make future logins auto-warp there.

---

## Quest / Objective Structure

There is **no explicit quest system** — no quest log, no objective tracker, no NPC quest
givers. Progression is **emergent exploration gated by collected data**:

- The only "progress state" is the inventory (`global.iv_itemdb`, persisted server-side).
- The gating chain is purely mechanical:
  **zone link (from a zone's `hometele`) → that zone's linkpoints come alive → collect
  area links / find key items → `goto`/remote-teleport unlock → reach the next zone's
  portal.** (`linkpoint Alarm-1.gml:10-13`, `iv_keyactions.gml:105-156`, `CmdRec.gml:3-13`.)
- Key items (`d_key`) are capability unlocks, not quest rewards: RmtPlugn (teleport
  anywhere), HxpPlugn (hexporters), MskPlugn (spectrum parts), WrpPlugn (local warps),
  DuoPlugn (co-op duo system, `iv_setactions.gml:68-104`).
- Passkeys (`passkey` on locked drops, e.g. all Traverse_Core mod tools) are the only
  "puzzle/secret" element — knowledge gates, discovered socially, not tracked in-game.

**[INFERENCE]** The intended loop is a Metroidvania of *connectivity*: you unlock the
ability to move around the network (links + Plugns) rather than combat power. "Completion"
= owning every zone link + area link + all Plugns + a full spectrum palette, with no
in-game checklist surfacing it.
