# Input and UI

# Input and UI

## Actions

A game declares **actions**, not keys. An action is a `button` or a two-axis `axis2`. Bindings map each input *kind* to the keys, pad buttons or touch controls that drive the action:

```ts
const actions = {
  move:  { kind: 'axis2' },
  look:  { kind: 'axis2' },
  jump:  { kind: 'button' },
  use:   { kind: 'button' },
  menu:  { kind: 'button' },
};
const bindings = {
  keyboard: { move: [{ axisKeys: ['KeyA', 'KeyD', 'KeyS', 'KeyW'] }], jump: [{ key: 'Space' }], use: [{ key: 'KeyE', label: 'E' }] },
  console:  { move: [{ axisKeys: ['ArrowLeft', 'ArrowRight', 'ArrowDown', 'ArrowUp'] }], use: [{ key: 'Enter', label: 'A' }], menu: [{ key: 'Escape', label: 'B' }] },
};
```

`InputService` samples them every fixed step: `down(a)`, `pressed(a)`, `released(a)`, `axis(a)`, `lookDelta()`. The active `InputKind` (`console`, `keyboard`, `gamepad`, `touch`) follows whatever the player used last, and `prompt(a)` returns the label of the current binding ("A", "E", "Tap") for UI text.

On OS Beroun the cursor, A and B reach the page as keys, so the `console` kind is an ordinary key map. Pointer Lock is only for a mouse; it is never required.

For tests and scripted scenes, `inject(action, value)` forces an action, and `enabled = false` makes every action read as released while a menu is open or a cutscene plays.

## Touch

`TouchControls` generates the on-screen controls from the game's touch bindings: a stick on the left, look on the right, buttons on the edge. `createGame` adds it on phones and tablets (`touchControls: false` to opt out).

## The UI overlay

`UIService` is an HTML/CSS layer above the canvas. Everything it shows is **data first**, so the logic runs and is tested without a DOM.

| Piece | Use |
|---|---|
| HUD | `setHud('hp', 'HP 80')` / `setHud('hp', null)`: named lines |
| Prompt | `setPrompt('[E] Open door')`: the use-prompt for the focused `Interactable` |
| Toasts | `toast('Picked up: brass key')`: `ToastQueue`, at most three at once |
| Loading | the loading screen, driven by the level service's events |
| Menu | `openMenu(def)`: a `MenuModel` steered by up / down / accept / back, so the console's D-pad, A and B, a mouse and touch all work; the game pauses while it is open |

`UIOptions.scale` returns the UI scale (from `uiScaleFor(screen)`), and the CSS is written in `em` so the overlay stays readable from 640×480 up to 4K. `pauseMenu` builds the menu that the menu action opens.
