Controls
How keyboard input reaches a game that only understands a PS2 pad: Enter in menus, keys whose meaning depends on the screen, and the Numpad 1 caveman fix.
1Everything becomes a PS2 pad
The engine reads DUALSHOCK 2 style raw data: two bytes of active-low buttons, four stick bytes, twelve pressure
bytes. Keyboard and controller both fill one sPadState, which is packed into that format.

2Enter, Space, Escape: the other path
Menus also read a typed-character buffer (SIO::KeyboardRead), which is how Enter/Space choose and Escape
backs out or pauses. SDL events feed it; mapping those keys to pad buttons as well would fire twice.
3Sample the context when a key goes down
Enter toggles the camera while skating but means "place" in the park editor; Numpad 7 is nollie when skating and "lower" in the editor. The context is captured on key-down and kept until release, so a key held across a menu opening doesn't change meaning halfway.
4Fix the caveman
Numpad 1/3 pressed L2/R2, which is not how you get off the board on an Xbox build. They're the Xbox Black/White buttons, which live in a byte the PS2 format doesn't have, and only reach the game when the device is in digital mode.

switch_control.q: Xbox_Trigger = {{ Press , Black , 10 }}. Four words of script, one afternoon of reading pad bytes.
5The default layout
| Key | Skating | Menus | Create-A-Park |
|---|---|---|---|
| W A S D | move | move | move selection |
| Numpad 2 / 4 / 6 / 8 | ollie / flip / grab, talk / grind | accept / – / – / back | place / rotate / rotate / delete |
| Numpad 7 / 9 | nollie, spin left / switch, spin right | – | lower / raise |
| Numpad 1 / 3 | get off / back on board | – | zoom in / out |
| Z X C V | camera | – | camera |
| Enter / Escape | change view / pause | accept / back | place / pause |
All of these can be changed now: Keyboard settings.