---
mvp: yes
subsystem: input
---

# Input

The BNO 5-8 client receives input through GameMaker 5.3a's per-key event system. Object **0042-player** is the canonical input consumer: it carries Keyboard-N events for each movement key (37 left, 38 up, 39 right, 40 down — Windows virtual-key codes), plus Keyboard-32 (space) and a handful of Other-N events for context-menu and chat-mode entry.

Three event flavours exist and BNO uses all of them:

- **Keyboard-N** (held) fires every step that the key is down. Used for continuous motion.
- **Keyboard-Press-N** fires once on the down transition. Used for menu confirms, dropping items, sending a chat line.
- **Keyboard-Release-N** fires once on the up transition. Used for stopping animations cleanly when the player releases a movement key.

The Step event of `0042-player` (see [collision.md](collision.md)) consumes the result of the Keyboard events — it is NOT a polling consumer. The Keyboard events directly mutate `hspeed` / `vspeed` / `direction`; Step then resolves collision before the engine commits the move.

A small but important subtlety: GM 5.x Keyboard events do NOT fire while a text-input field has focus (chat input, server-address dialog, password prompt). This implicit "input ownership" is GM-internal; in the rebuild we will need an explicit input mode flag (`game.inputMode === 'walking' | 'chatting' | 'menu'`) to route key events correctly. See `extracted/client-5-8/objects/0042-player/events/Create.gml` for the player initialisation:

```gml
// auto-transcompiled — see *.dnd.json for canonical truth
image_speed = 0.8;

//global.p_created[pid] = 1;
```

`image_speed = 0.8` slows the per-frame sprite animation step (8 frames per "second" at 10 fps); this is unrelated to input but lives here because it's the player's only Create-event work.

## Key idioms

- **Per-key event = handler.** GM 5.x has no `onKeyDown(e: KeyboardEvent)` aggregator. Each VK code has its own event slot. The compiled `events/<EventName>-<VK>.gml` files mirror this 1:1.
- **WASD vs arrow keys.** BNO uses arrow keys exclusively for movement; there is no WASD remap. Phase 6 must add one (modern web users expect it).
- **Modifier-aware actions.** `keyboard_check(vk_alt)` + `keyboard_check(ord('A'))` is the original admin "Ctrl+A stores location" idiom (see [admin-anti-port.md](admin-anti-port.md)). Modifier checks happen inside the regular Keyboard event, NOT through a separate event slot.
- **`keyboard_string`** is a built-in GM accumulator string updated by every text-producing key. The chat input reads it. Backspace clears one character. Phase 6 needs a controlled equivalent (probably a managed `<input>` element) since `keyboard_string` is a global.

## Scripts referenced in this subsystem

<!-- AUTOGEN:scripts:start -->
| Script ID | Name | Lines | Used in objects |
|-----------|------|-------|------------------|
<!-- AUTOGEN:scripts:end -->

## Objects referenced in this subsystem

<!-- AUTOGEN:objects:start -->
| Object ID | Name | Sprite | Mask | Events |
|-----------|------|--------|------|--------|
| 0042 | player | 65 | 34 | 6 |
<!-- AUTOGEN:objects:end -->

## Engine functions used

<!-- AUTOGEN:gml-functions:start -->
| GML function | Call sites | Sample script | Wiki link |
|--------------|------------|---------------|-----------|
| `keyboard_check` | 0 | — | [wiki/07-gml-core-functions](../../decomp/wiki/07-gml-core-functions.md) |
| `keyboard_check_pressed` | 0 | — | [wiki/07-gml-core-functions](../../decomp/wiki/07-gml-core-functions.md) |
| `keyboard_check_released` | 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 explicit input modes**. Maintain `inputMode: 'walking' | 'chatting' | 'menu' | 'modal'`; route `KeyboardEvent`s based on mode. GM's implicit text-field-takes-focus model does not survive in browser DOM.
- **Map both arrow keys AND WASD.** Modern players expect both. Internally collapse to a `Direction = none | left | right | up | down` enum.
- **Debounce on the protocol boundary, NOT on the input boundary.** The server is authoritative for movement (CLAUDE.md hard rule #1). Send intent (`{type: 'walk', dir: 'left', held: true}`) at server tick, NOT every browser keydown.
- **Disable repeat for one-shot actions** (chat send, menu confirm). The browser fires `keydown` repeatedly while held; the original GM Keyboard-Press-N event fires once.

## See also

- [decomp/wiki/07-gml-core-functions.md](../../decomp/wiki/07-gml-core-functions.md) — `keyboard_check*` reference
- [collision.md](collision.md) — what Step does with the input that Keyboard events have queued
- [animation.md](animation.md) — `image_speed` / `sprite_index` use after movement
- [ui-and-menus.md](ui-and-menus.md) — chat / menu input handling
