> ## Documentation Index
> Fetch the complete documentation index at: https://docs.spurdoverse.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Input

> Key and mouse state, input injection, view angles and movement.

Reads keys and mouse buttons, sends input to the game, and exposes view angles and movement.

```ts theme={null}
import input from "@native/input";
```

<Note>
  Keys are [Win32 virtual-key codes](https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes)
  (`0x41` is A). Mouse buttons use DOM numbering: `0` left, `1` middle, `2` right, `3` back,
  `4` forward.
</Note>

## Overview

<CardGroup cols={2}>
  <Card title="Reading" icon="eye" href="#reading">
    `isKeyDown`, `isMouseDown`.
  </Card>

  <Card title="Injection" icon="computer-mouse" href="#injection">
    `keyDown`, `keyPress`, `mouseClick`, `moveMouse`, `moveToAngle`.
  </Card>

  <Card title="State" icon="gauge" href="#state">
    `viewAngles`, movement, mouse delta.
  </Card>
</CardGroup>

***

## Reading

Reading always works, focused or not.

### isKeyDown

<br />

```ts theme={null}
input.isKeyDown(key: number): boolean
```

Whether a key is held right now. Mouse-button virtual keys (`0x01`, `0x02`, …) work too.

```ts theme={null}
if (input.isKeyDown(0x12)) console.log("Alt held"); // VK_MENU
```

### isMouseDown

<br />

```ts theme={null}
input.isMouseDown(button?: 0 | 1 | 2 | 3 | 4): boolean
```

Whether a mouse button is held. Default `0` (left).

```ts theme={null}
if (input.isMouseDown(3)) console.log("back button held");
```

For presses as events, listen to `keydown` and `mousedown` on the [global target](/api/globals)
instead of polling.

***

## Injection

Input is only sent while the game window has focus, the menu is closed and no text input (chat,
IME) is active. Every call returns `true` when the input was sent and `false` when it was refused.

| Call | |
| :- | :- |
| `keyDown(key)` | Presses a key and keeps it down. |
| `keyUp(key)` | Releases it. |
| `keyPress(key)` | Down, then up. |
| `mouseDown(button?)` | Default left. |
| `mouseUp(button?)` | Default left. |
| `mouseClick(button?)` | Down, then up. Default left. |

```ts theme={null}
if (!input.keyPress(0x20)) console.log("jump refused"); // VK_SPACE
input.mouseClick(0);
```

<Warning>
  A `keyDown` that was sent stays down until a `keyUp` gets through. If the user opens the menu
  in between, the `keyUp` returns `false`, so retry it on the next tick.
</Warning>

### moveMouse

<br />

```ts theme={null}
input.moveMouse(dx: number, dy: number): boolean
```

A raw relative mouse move, in whole counts.

```ts theme={null}
input.moveMouse(0, 4); // pull down a little
```

### moveToAngle

<br />

```ts theme={null}
input.moveToAngle(angle: AngleLike): boolean
```

Moves the mouse by what it takes to turn the view to `angle` (degrees), using your `sensitivity`,
`m_yaw` and `m_pitch`.

```ts theme={null}
const view = input.state.viewAngles;
if (view) input.moveToAngle(view.lerp(target, 0.2)); // a fifth of the way per call
```

***

## State

`input.state` is the game's input as of the current tick. Every field is `null` while it can't be
read, e.g. while the game is loading.

| Field | Type |
| :- | :- |
| `viewAngles` | [QAngle](/api/types/qangle) |
| `thirdPersonAngles` | `QAngle` |
| `forwardMove`, `leftMove`, `upMove` | `number` |
| `mouseDeltaX`, `mouseDeltaY` | `number` |
| `inThirdPerson` | `boolean` |

```ts theme={null}
const { viewAngles, forwardMove } = input.state;
if (viewAngles && forwardMove !== null) console.log(viewAngles.yaw, forwardMove);
```

***

## Example

Hold the back mouse button to pull toward the nearest enemy head in a 5° cone.

```ts theme={null}
import entities from "@native/entities";
import input from "@native/input";

on("tick", () => {
    const me = entities.getLocalPlayer();
    const view = input.state.viewAngles;
    const eye = me?.getEyePosition();
    if (!me || !view || !eye || !input.isMouseDown(3)) return;

    let best: QAngle | null = null;
    for (const pawn of entities.getPlayers({ skipLocal: true })) {
        if (pawn.m_iHealth <= 0 || !pawn.isEnemy(me)) continue;
        const head = pawn.getEyePosition();
        const aim = head && eye.angleTo(head);
        if (aim && view.fovTo(aim) < 5 && (!best || view.fovTo(aim) < view.fovTo(best))) best = aim;
    }

    if (best) input.moveToAngle(view.lerp(best, 0.15));
});
```
