---
mvp: partial
subsystem: ui-and-menus
---

# UI & Menus

The BNO 5-8 client is a single-window, no-DOM application; every menu, modal, button, and HUD element is an instance of a GameMaker object that owns its own draw + click events. There is no UI framework — no panel/widget/layout system. Each visual is hand-positioned with `x, y` and hand-rendered in its own Draw event.

The four canonical UI rooms are:

- **Room 0003 `Main_Menu`** — landing screen. Hosts objects: 0034-title, 0035-exit, 0036-connect, 0037-settings, 0394-help. Five buttons + a title.
- **Room 0004 `Online_Lobby`** — server lobby with chat HUD, player list, request queue. ~30 objects (chat panel, player-list rows, send buttons, message-board entry, request-bar, etc.).
- **Room 0006 `Settings_Menu`** — options screen. Hosts 0034-title, 0039-version, 0040-cusername, 0041-tomainmenu, 0118-alphaq, 0325-bkmbq, 0359-cpassword, 0384-serveraddress, 0406-navicolordisplay, 0423-notifq, 0464-tcprecq, 0480-tcpsendq.
- **Room 0007 `Online_Command_Screen`** — admin command console. Hosts 0043-operations and 0045-commandob. **THIS IS THE Ctrl+E surface — see [admin-anti-port.md](admin-anti-port.md).**

Five UI primitive subsystems exist within these rooms, each implemented as a cluster of scripts:

- **Message-board (`mb_*` scripts 252-266, 278-283, 332-335, 350-351)** — the persistent multi-channel message board. `0263 mb_topdraw` is the top panel; `0264 mb_repdraw` is the reply panel; `0267 mb_newdraw` is the compose panel; `0265 mb_up` / `0266 mb_down` scroll. The MB is a full read/write UI inside the lobby.
- **Inventory / data-table (`iv_*` scripts 298-300, 302-305, 307-308, 310, 312, 320, 323-324, 327, 329-332, 347)** — the inventory and data-display widget. `0298 iv_infodraw` draws the info card; `0303 iv_datadraw` draws the table; `0311 iv_actiondraw` draws the action menu; `0299 iv_infochange` switches viewed entry. This is the "look-up tables" UI used both for player-actions and for browsing zone metadata.
- **Request queue (`rq_*` scripts 269-272, 274-275, 278-283, 331)** — the persistent request bar. `0269 draw_rqbar` draws it; the rest handle accept / decline / ignore / dig / spot / duo / cancel actions. Requests are user-to-user invitations (Hexport, chat, group movement).
- **Chat (`chat_*` + `caddline` scripts 100, 102, 106, 268)** — the chat scroll buffer. `0100 chat_initlines` initialises; `0102 chat_drawlines` renders; `0106 caddline` appends a message.
- **Navi colour customisation (`set_navi_colors`, `set_user_colors`, `spr2ncol`, `get_ncol_cnumber`, etc. — scripts 313-315, 317, 322)** — the player avatar colourisation widget visible in Settings.

Each UI element responds to mouse + keyboard via Mouse-N events on its host object. Click-to-confirm modals chain via `instance_create` of next-screen objects.

## Key idioms

- **Each UI element = an object.** No widget tree, no parent-child layout. The 90+ UI objects in the lobby are all hand-placed in the room editor.
- **Draw event = render code.** No declarative description; the Draw event runs every frame, redoing all draw-set-font / draw-set-color / draw-text calls. Cheap because GameMaker is single-threaded and per-instance.
- **Mouse-N event for click handlers.** GM has Mouse-Press-Left, Mouse-Released-Left, Mouse-Enter, Mouse-Leave events; BNO uses Mouse-Press-Left for almost every button.
- **Modal chaining via instance_create.** "Click → spawn next-screen object → first object destroys self." There is no modal stack; if you click multiple times fast you can spawn duplicates.
- **`alphaq`, `bkmbq`, `tcprecq`, `tcpsendq`** are *queue* objects (the `q` suffix), tucked into Settings, that show the live state of internal queues. These were debug-style developer surfaces left enabled in retail. The rebuild may keep them as devtools but should not expose to end users.

## CLI-08 MVP scope

For CLI-08 (the Phase 6 hard gate: two players + chat over deployed server), only the **chat HUD** is `mvp: yes`. Everything else (menus, message board, inventory, request queue, settings, navi-color picker) is **post-MVP**.

The minimum surface is:

- A single text-input field for chat
- A scrollable buffer of recent messages
- A simple "send" action (Enter key or Send button)
- Server-pushed `messages` updates render to the buffer

Total complexity: ~150 lines of Phaser/Pixi UI. Settings, modals, and command screens come later.

## Scripts referenced in this subsystem

<!-- AUTOGEN:scripts:start -->
| Script ID | Name | Lines | Used in objects |
|-----------|------|-------|------------------|
| 0100 | chat_initlines | 16 | — |
| 0252 | mb_getname | 11 | — |
| 0253 | mb_getwriter | 10 | — |
| 0254 | mb_getbody | 10 | — |
| 0262 | mb_repwrite | 19 | — |
| 0265 | mb_up | 38 | — |
| 0266 | mb_down | 39 | — |
| 0268 | nametopid | 8 | — |
| 0270 | rq_timercount | 13 | — |
| 0271 | rq_collapse | 13 | — |
| 0272 | rq_new | 167 | — |
| 0274 | rq_dig | 54 | — |
| 0275 | rq_find | 16 | — |
| 0278 | rq_areasend | 12 | — |
| 0279 | rq_join | 26 | — |
| 0280 | rq_summon | 31 | — |
| 0281 | rq_cancel | 21 | — |
| 0282 | rq_decline | 21 | — |
| 0283 | rq_ignore | 16 | — |
| 0290 | pcode_lrloop | 31 | — |
| 0291 | pcode_udloop | 31 | — |
| 0292 | pcode_slookat | 27 | — |
| 0293 | pcode_wander | 69 | — |
| 0294 | extract_links | 59 | — |
| 0299 | iv_infochange | 47 | — |
| 0300 | iv_formattable | 24 | — |
| 0302 | iv_datagather | 84 | — |
| 0304 | iv_tabcol2db | 18 | — |
| 0305 | iv_iconextras | 61 | — |
| 0307 | nullcheck | 2 | — |
| 0308 | iv_setactions | 264 | — |
| 0310 | iv_quit | 5 | — |
| 0312 | iv_mdatainfo | 33 | — |
| 0313 | set_navi_colors | 34 | — |
| 0314 | spr2ncol | 22 | — |
| 0315 | get_navi_front | 13 | — |
| 0317 | get_navi_cletter | 20 | — |
| 0320 | iv_itemsearch | 9 | — |
| 0322 | set_user_colors | 35 | — |
| 0323 | iv_additem | 13 | — |
| 0324 | iv_updateserver | 20 | — |
| 0327 | iv_ldatainfo | 41 | — |
| 0329 | iv_linksearch | 8 | — |
| 0330 | iv_zonesearch | 8 | — |
| 0331 | rq_add | 29 | — |
| 0332 | iv_collapse | 24 | — |
| 0333 | mb_rcollapse | 15 | — |
| 0334 | mb_tcollapse | 20 | — |
| 0335 | mb_trise | 29 | — |
| 0347 | iv_keyactions | 383 | — |
| 0348 | hxb_search | 8 | — |
| 0349 | pindex_opt | 20 | — |
| 0350 | mb_bgset | 12 | — |
| 0351 | mb_colset | 29 | — |
| 0357 | connectevent | 38 | — |
| 0358 | connectstep | 15 | — |
| 0359 | rq_spot | 23 | — |
| 0360 | rq_duodig | 23 | — |
| 0361 | end_duo | 8 | — |
<!-- AUTOGEN:scripts:end -->

## Objects referenced in this subsystem

<!-- AUTOGEN:objects:start -->
| Object ID | Name | Sprite | Mask | Events |
|-----------|------|--------|------|--------|
| 0025 | dirttile | 46 | -1 | 1 |
| 0030 | mtile1l | 53 | -1 | 1 |
| 0032 | mtile1u | 55 | -1 | 1 |
| 0033 | mtile1d | 56 | -1 | 1 |
| 0034 | title | 59 | -1 | 3 |
| 0035 | exit | 59 | 59 | 2 |
| 0036 | connect | 59 | 59 | 4 |
| 0037 | settings | 59 | 59 | 2 |
| 0039 | version | 59 | 59 | 1 |
| 0040 | cusername | 59 | 59 | 3 |
| 0041 | tomainmenu | 59 | -1 | 2 |
| 0043 | operations | 355 | -1 | 9 |
| 0044 | getip | 59 | 59 | 2 |
| 0045 | commandob | -1 | -1 | 5 |
| 0047 | press enter | -1 | -1 | 2 |
| 0048 | mtile1r | 54 | -1 | 1 |
| 0051 | bkdraw | 64 | -1 | 3 |
| 0053 | mplat1_ud_u | 1013 | -1 | 11 |
| 0056 | mplat1_lr_r | 1013 | -1 | 11 |
| 0057 | mplatstop | 67 | 23 | 0 |
| 0058 | hometele | 69 | 364 | 7 |
| 0109 | ac_centralsquare | 135 | 67 | 1 |
| 0110 | ac_naturegrounds | 135 | 67 | 1 |
| 0111 | ac_whirlpool | 135 | 67 | 1 |
| 0112 | ac_disconnectedalley | 135 | 67 | 1 |
| 0114 | ac_roseport | 135 | 67 | 1 |
| 0115 | ac_petalsquare | 135 | 67 | 1 |
| 0116 | ac_returnjuncture | 135 | 67 | 1 |
| 0118 | alphaq | 59 | 59 | 3 |
| 0119 | whitetile | 136 | -1 | 1 |
| 0121 | hiddentile | 142 | 23 | 5 |
| 0123 | ac_abyssalpathway | 135 | 67 | 1 |
| 0124 | ac_divinesquare | 135 | 67 | 1 |
| 0223 | chatob | 61 | -1 | 26 |
| 0305 | bncentral_mb | 353 | 354 | 5 |
| 0306 | scrollwood_mb | 353 | 354 | 5 |
| 0307 | divine_mb | 353 | 354 | 5 |
| 0308 | galewind_mb | 353 | 354 | 5 |
| 0310 | redtile | 1010 | -1 | 1 |
| 0311 | mtile2l | 360 | -1 | 1 |
| 0312 | mtile2r | 361 | -1 | 1 |
| 0313 | mtile2u | 362 | -1 | 1 |
| 0314 | mtile2d | 363 | -1 | 1 |
| 0318 | toprairieflats | 356 | 364 | 6 |
| 0325 | bkmbq | 59 | 59 | 3 |
| 0340 | todatabase | 356 | 364 | 6 |
| 0344 | ac_bugisle | 135 | 67 | 1 |
| 0345 | bug_mb | 353 | 354 | 5 |
| 0347 | hexporter_d | 386 | 23 | 3 |
| 0351 | hexporter_u | 386 | 23 | 3 |
| 0354 | hexport_rc | 386 | 23 | 2 |
| 0355 | hexporter_r | 386 | 23 | 3 |
| 0356 | hexporter_l | 386 | 23 | 3 |
| 0357 | ac_bugmaze | 135 | 67 | 1 |
| 0358 | ac_zensandbar | 135 | 67 | 1 |
| 0359 | cpassword | 59 | 59 | 2 |
| 0360 | mtile3l | 401 | 23 | 1 |
| 0361 | mtile3r | 402 | 23 | 1 |
| 0362 | mtile3u | 404 | 23 | 1 |
| 0363 | mtile3d | 406 | 23 | 1 |
| 0376 | npc_greenprog | 419 | 420 | 8 |
| 0384 | serveraddress | 59 | 59 | 2 |
| 0387 | todigitalabyss | 356 | 364 | 6 |
| 0389 | tile1ur | 437 | -1 | 2 |
| 0394 | help | 59 | 59 | 2 |
| 0395 | totraversecore | 356 | 364 | 6 |
| 0396 | ac_windlysweave | 135 | 67 | 1 |
| 0398 | ac_windlysplateau | 135 | 67 | 1 |
| 0402 | mtile5l | 478 | 23 | 1 |
| 0403 | mtile5r | 479 | 23 | 1 |
| 0404 | mtile5u | 480 | 23 | 1 |
| 0405 | mtile5d | 481 | 23 | 1 |
| 0406 | navicolordisplay | 0 | -1 | 6 |
| 0407 | swaptile | 771 | -1 | 3 |
| 0414 | hexporter_any | 386 | 23 | 3 |
| 0415 | hexsling_any | 780 | 23 | 3 |
| 0418 | d_spectrum | 605 | 783 | 6 |
| 0420 | door3vertical | 797 | 383 | 4 |
| 0421 | door3horizontal | 796 | 63 | 4 |
| 0423 | notifq | 59 | 59 | 3 |
| 0427 | linkpoint | 809 | 364 | 7 |
| 0428 | dirtrtile | 824 | -1 | 1 |
| 0430 | grasstile | 829 | 23 | 2 |
| 0432 | bluetile | 378 | -1 | 1 |
| 0438 | mplatw_lr_r | 136 | -1 | 9 |
| 0440 | mplatd_ud_u | 46 | -1 | 9 |
| 0441 | mplatd_lr_r | 46 | -1 | 9 |
| 0456 | toschweisstar | 356 | 364 | 6 |
| 0464 | tcprecq | 59 | 59 | 3 |
| 0480 | tcpsendq | 59 | 59 | 3 |
| 0523 | tobahoo | 356 | 364 | 6 |
| 0529 | ac_stormdrain | 135 | 67 | 1 |
<!-- AUTOGEN:objects:end -->

## Engine functions used

<!-- AUTOGEN:gml-functions:start -->
| GML function | Call sites | Sample script | Wiki link |
|--------------|------------|---------------|-----------|
| `draw_text` | 10 | 0002-script_drawlines | [wiki/07-gml-core-functions](../../decomp/wiki/07-gml-core-functions.md) |
| `draw_set_font` | 0 | — | [wiki/07-gml-core-functions](../../decomp/wiki/07-gml-core-functions.md) |
| `keyboard_string` | 10 | 0000-script_initlines | [wiki/07-gml-core-functions](../../decomp/wiki/07-gml-core-functions.md) |
<!-- AUTOGEN:gml-functions:end -->

## Rebuild guidance

For the Phase 6 client:

- **Use a real UI framework on top of the canvas.** Phaser 3 has `Phaser.GameObjects.DOMElement` for HTML overlays; PixiJS has `@pixi/ui` for canvas-native widgets, plus DOM overlay support. Both are fine.
- **Chat first.** Get the chat HUD working before any menu. CLI-08 is the gate.
- **Settings → modal page**, not a separate Phaser scene. Faster to build, easier to dismiss.
- **Drop the queue debug surfaces** (`alphaq`, `bkmbq`, `tcprecq`, `tcpsendq`) from the Settings screen. They were dev surfaces. If you need them, gate behind a `?dev=1` URL parameter.
- **Admin command screen is anti-port territory.** See [admin-anti-port.md](admin-anti-port.md). Phase 7 PAR-07 is the legitimate slot for an admin UI; that's a separate authenticated web app, not part of the game client.

## See also

- [admin-anti-port.md](admin-anti-port.md) — Online_Command_Screen (Room 0007) replacement strategy
- [rendering.md](rendering.md) — drawing primitives that every UI element uses
- [input.md](input.md) — keyboard / mouse event routing
- [scene-room-model.md](scene-room-model.md) — the rooms that host these UI surfaces
