Input & Controls
Input in the engine is fixed for now: there is no rebinding layer and no per-device configuration. What is tunable are the look-scaling preferences, and those are ordinary client.* preferences — set one from the console and it applies on the very next frame.
Two devices are live at all times: keyboard and mouse, and a gamepad if one is plugged in. Neither has to be selected or enabled, and there is no “gamepad mode” to enter.
Keyboard and mouse
Section titled “Keyboard and mouse”| Action | Binding |
|---|---|
| Move | W / A / S / D |
| Look | Mouse |
| Jump | Space |
| Crouch | Left Ctrl |
| Sprint | Left Shift |
| Use / interact | E |
| Noclip (cheat) | V |
| Chat | Enter (or keypad Enter); Escape cancels, Enter sends, clicking away dismisses |
| Pause menu | Escape |
| Console | ` (backquote); Escape also closes it |
| DigitalHeaven overlay | F1 |
The console does not pause, and Escape never leaves a window behind. The backquote opens the console straight from gameplay — the world keeps running behind it, you just get a cursor and the keyboard goes to the prompt. Escape there closes the console and hands the game back rather than opening the pause menu; Escape over a paused game puts the menu and the console away together, so one press always ends with a clean screen. What survives either route is the scrollback and whatever was half-typed at the prompt.
The cursor is a property of the state, not something a transition does. One expression decides whether the pointer is freed and shown, and the frame loop asks it once and applies the answer — nothing anywhere locks or unlocks the mouse on its way into or out of a screen. It is freed while a menu is up, while the console or the settings screen is over the scene, while a chat line is being typed, while the window is being dragged or resized, and while a join is still loading — see the loading box. The last one is why ESC over a connecting screen and then Resume leaves the pointer where it was: resuming ends the pause, not the load.
Chat does not pause and does not fight the console. Enter opens the prompt only while gameplay actually has the keyboard, so it does nothing behind the pause menu, the settings screen or a front console — and while the prompt is open it owns the keyboard outright, so typing “was” never walks you forward. The one key it does not swallow is Escape, which always closes it — and clicking away dismisses it rather than leaving a prompt open that nothing can reach. Either way what you had typed is still there when you come back, with the caret at the end of it. See Chat for the feed itself.
Mouse look uses Source-style scaling — degrees turned equals raw mouse counts times client.mYaw / client.mPitch times client.sensitivity — and is applied every frame, client-side, so the view never waits on the server.
Gamepad
Section titled “Gamepad”Gamepad support is deliberately passive: a fixed default mapping, always on, no UI. The first connected gamepad is used; connecting or disconnecting one mid-session is handled live and logged at INF, and a session with no pad behaves exactly as it always did.
A Steam Controller works out of the box. Steam Input presents it to the game as a standard XInput/Xbox gamepad, which is what the windowing layer sees, so no special handling is involved — the same is true of any other controller Steam Input remaps.
The mapping
Section titled “The mapping”| Action | Gamepad |
|---|---|
| Move | Left stick (analog — the stick’s magnitude is the walk speed) |
| Look | Right stick |
| Jump | A (south) |
| Crouch | B (east) |
| Use / interact | X (west) |
| Sprint | LB or left stick click |
| Pause menu | Start |
Everything else — Y, RB, both triggers, the D-pad, Back and the guide button — is unmapped. So is noclip: it is a cheat toggle with no obvious pad counterpart, and a face button that flips flight on by accident is worse than one that does nothing.
Menus, the console and the settings screen remain mouse-driven. There is no controller menu navigation, so Start opening the pause menu is where the pad’s involvement in UI ends.
How the two devices coexist
Section titled “How the two devices coexist”Held actions are OR’d. Jump is down when either Space or A is down. Nothing arbitrates.
Movement gives the keyboard priority. Whenever any movement key is held, the keyboard’s wish direction is what gets sent and the stick is ignored for that frame; the stick fills in only while the keys are idle. Summing the two would let a stick push a keyboard walk past full speed, and it would mean a drifting stick permanently skews a keyboard-only player’s heading.
Look is summed. An idle device contributes exactly zero, so mouse and stick add with no mode switch — you can nudge the view with the mouse mid-stick-turn and it does the obvious thing.
The UI gate is shared. Gamepad move and look flow through the same gameplay gate as the keyboard and mouse: while the pause menu, the console or the overlay holds input, the sticks drive nothing. Start goes through the same routing rule as Escape, so one press can never close the console and open the pause menu behind it.
Stick shaping
Section titled “Stick shaping”A stick is not a mouse. A mouse delta is a displacement that already happened; a stick reports a held position, so look is a rate — scaled by degrees per second and integrated over the frame’s elapsed time. That is why the pad look preferences are in degrees/second while the mouse ones are per mouse count.
The deadzone is radial: the dead region is a disc around center, not a cross of per-axis bands, so a stick held diagonally is not clipped toward a cardinal direction. Magnitude surviving the disc is rescaled back over the full 0–1 range, which is what keeps the response continuous — without the rescale the stick would jump to the deadzone value the instant it left the dead disc.
The look stick gets a response curve on top: its magnitude is raised to client.padLookExponent, compressing the low end so small deflections aim finely while full deflection still turns at the full rate. Movement is left linear — walking speed should track the stick.
Preferences
Section titled “Preferences”| Preference | Default | Meaning |
|---|---|---|
client.padSensitivity | 1 | Look sensitivity multiplier; scales both axes at once |
client.padYaw | 220 | Yaw degrees per second at full stick deflection, before sensitivity |
client.padPitch | 160 | Pitch degrees per second at full deflection, before sensitivity |
client.padDeadzone | 0.15 | Radial deadzone, in fractions of full deflection; shared by both sticks |
client.padLookExponent | 2 | Look response curve exponent; 1 is a linear stick |
Pitch is slower than yaw by default: the vertical range is only a quarter turn between the pitch clamps, so matching the yaw rate makes the view feel twitchy.
These are console-settable and live — client.padDeadzone 0.08 takes effect on the next frame. They are not on the settings screen yet; that arrives with the rebinding layer.
What is not here yet
Section titled “What is not here yet”Rebinding, multi-controller support, a per-device enable/disable toggle, rumble, controller glyphs and controller menu navigation are all future work. The mapping above is a fixed default, not a binding system with defaults filled in.