> ## 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.

# Trace

> Cast rays, boxes, spheres, and capsules through the game world - hit detection, visibility checks, overlap queries, and surface info.

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

The `Trace` module casts rays and sweeps shapes through the game world using
the same collision data the engine uses - static geometry, dynamic entities,
and tagged collision layers.

<Note>
  All trace functions are **synchronous** and return immediately. Create
  a `TraceFilter` once and reuse it across frames - this avoids per-call
  allocation and lets you build up an ignore list over time.
</Note>

## Overview

<CardGroup cols={2}>
  <Card title="Filters" icon="filter" href="#filters">
    Create reusable trace filters from presets or custom configs.
  </Card>

  <Card title="Raycasts" icon="arrow-right-long" href="#raycasts">
    Single and multi-hit rays, box / sphere / capsule sweeps.
  </Card>

  <Card title="Spatial queries" icon="magnifying-glass" href="#spatial-queries">
    Visibility, solid checks, overlap spheres, closest point.
  </Card>

  <Card title="Results" icon="bullseye" href="#results">
    TraceResult, OverlapResult, ClosestPointResult shapes.
  </Card>
</CardGroup>

***

## Filters

Every trace function accepts an optional **filter** that controls what
the trace collides with. Filters can be passed in three forms - pick
whichever fits your situation:

| Form | Example |
| :- | :- |
| Preset string | `"bullet"` |
| `TraceFilter` object | `Trace.filter("bullet")` |
| Inline config object | `{ type: "bullet", backFaces: true }` |
| Omitted | Defaults to `"bullet"` |

### Trace.filter

<br />

```ts theme={null}
Trace.filter(preset?: string): TraceFilter
Trace.filter(options: FilterOptions): TraceFilter
```

Creates a reusable `TraceFilter`. Pass a preset string for common
scenarios, or a configuration object for fine-grained control.

<ParamField path="preset" type="string" default="bullet">
  One of the built-in filter presets (see table below).
</ParamField>

**Preset strings**

| Preset | Description |
| :- | :- |
| `"bullet"` | Standard bullet collision (default) |
| `"visibility"` | Line-of-sight checks |
| `"everything"` | Collide with all surfaces |
| `"grenade"` | Grenade collision rules |
| `"player_movement"` | Player movement sweep |
| `"occlusion"` | Occlusion culling traces |

<CodeGroup>
  ```ts Preset string theme={null}
  const filter = Trace.filter("bullet");
  const visFilter = Trace.filter("visibility");

  // No argument - defaults to "bullet"
  const defaultFilter = Trace.filter();
  ```

  ```ts Custom config theme={null}
  const filter = Trace.filter({
      type: "bullet",
      static: true,
      dynamic: true,
      resolveNormals: true,
      resolveMaterials: true,
      resolveEntity: true,
      backFaces: false,
      includeTags: ["pass_bullets", "window", "sky", "ladder", "player"],
      excludeTags: ["player_clip", "npc_clip", "grenade_clip"],
  });
  ```
</CodeGroup>

**Configuration object fields**

| Option | Type | Default | Description |
| :- | :- | :- | :- |
| `type` | `string` | `"bullet"` | Base preset to start from |
| `static` | `boolean` | `true` | Trace against static world geometry |
| `dynamic` | `boolean` | `true` | Trace against dynamic entities |
| `resolveNormals` | `boolean` | `true` | Compute surface normals on hit |
| `resolveMaterials` | `boolean` | `true` | Resolve surface material info on hit |
| `resolveEntity` | `boolean` | `true` | Populate entity info on hit |
| `backFaces` | `boolean` | `false` | Collide with back-facing surfaces |
| `includeTags` | `string[]` | - | [Collision tags](#collision-tags) to include |
| `excludeTags` | `string[]` | - | [Collision tags](#collision-tags) to exclude |

<Tip>
  Disable `resolveNormals`, `resolveMaterials`, and `resolveEntity` if
  you only care about **whether** something was hit - the trace runs
  faster when it doesn't need to fill in extra data.
</Tip>

### TraceFilter methods

Once you have a filter, you can tell it to skip specific entities.

<AccordionGroup>
  <Accordion title="filter.ignore" icon="eye-slash">
    Adds an entity to the filter's ignore list. Accepts a raw entity index
    or any object with an `.index` property (e.g. an `Entity`).

    ```ts theme={null}
    filter.ignore(entityIndex: number): void
    filter.ignore(entity: { index: number }): void
    ```

    ```ts theme={null}
    const filter = Trace.filter("bullet");
    const local = Entities.getLocalPlayer();

    // Ignore by entity object
    filter.ignore(local);

    // Ignore by raw index
    filter.ignore(42);
    ```
  </Accordion>

  <Accordion title="filter.clearIgnore" icon="rotate">
    Clears the ignore list so the filter collides with all entities again.

    ```ts theme={null}
    filter.clearIgnore(): void
    ```

    ```ts theme={null}
    filter.clearIgnore();
    ```
  </Accordion>
</AccordionGroup>

***

## Raycasts

### Trace.ray

<br />

```ts theme={null}
Trace.ray(start: Vector, end: Vector, filter?): TraceResult
```

Casts a single ray from `start` to `end`. The workhorse of the trace API -
use this for hit detection, aim checks, and penetration tests.

<ParamField path="start" type="Vector" required>
  Ray origin, typically the player's eye position.
</ParamField>

<ParamField path="end" type="Vector" required>
  Ray endpoint - the target position.
</ParamField>

<ParamField path="filter" type="string | TraceFilter | FilterOptions">
  Collision filter. Defaults to `"bullet"` if omitted.
</ParamField>

<Tabs>
  <Tab title="Default filter">
    ```ts theme={null}
    const result = Trace.ray(eye, target);
    ```
  </Tab>

  <Tab title="Preset string">
    ```ts theme={null}
    const result = Trace.ray(eye, target, "bullet");
    ```
  </Tab>

  <Tab title="Reusable filter">
    ```ts theme={null}
    const filter = Trace.filter("bullet");
    filter.ignore(local);
    const result = Trace.ray(eye, target, filter);
    ```
  </Tab>

  <Tab title="Inline config">
    ```ts theme={null}
    const result = Trace.ray(eye, target, { type: "bullet", backFaces: true });
    ```
  </Tab>
</Tabs>

### Trace.box

<br />

```ts theme={null}
Trace.box(start: Vector, end: Vector, mins: Vector, maxs: Vector, filter?): TraceResult
```

Sweeps an axis-aligned box from `start` to `end`. `mins` and `maxs` define
the box extents relative to the sweep origin.

<ParamField path="start" type="Vector" required>Sweep origin.</ParamField>
<ParamField path="end" type="Vector" required>Sweep destination.</ParamField>

<ParamField path="mins" type="Vector" required>
  Minimum corner of the box (negative offsets).
</ParamField>

<ParamField path="maxs" type="Vector" required>
  Maximum corner of the box (positive offsets).
</ParamField>

<ParamField path="filter" type="string | TraceFilter | FilterOptions">
  Collision filter. Defaults to `"bullet"`.
</ParamField>

```ts theme={null}
const mins = new Vector(-16, -16, 0);
const maxs = new Vector(16, 16, 72);

const result = Trace.box(start, end, mins, maxs, "player_movement");
if (result.hit) {
    console.log("Blocked at", result.position);
}
```

### Trace.sphere

<br />

```ts theme={null}
Trace.sphere(start: Vector, end: Vector, radius: number, filter?): TraceResult
```

Sweeps a sphere from `start` to `end`.

<ParamField path="start" type="Vector" required>Sweep origin.</ParamField>
<ParamField path="end" type="Vector" required>Sweep destination.</ParamField>
<ParamField path="radius" type="number" required>Sphere radius in game units.</ParamField>

<ParamField path="filter" type="string | TraceFilter | FilterOptions">
  Collision filter. Defaults to `"bullet"`.
</ParamField>

```ts theme={null}
const result = Trace.sphere(start, end, 8.0, "grenade");
```

### Trace.capsule

<br />

```ts theme={null}
Trace.capsule(start: Vector, end: Vector, radius: number, halfHeight: number, filter?): TraceResult
```

Sweeps a capsule (cylinder with hemispherical caps) from `start` to `end`.
Useful for simulating player-sized collision hulls.

<ParamField path="start" type="Vector" required>Sweep origin.</ParamField>
<ParamField path="end" type="Vector" required>Sweep destination.</ParamField>
<ParamField path="radius" type="number" required>Capsule radius.</ParamField>
<ParamField path="halfHeight" type="number" required>Half the height of the cylindrical section.</ParamField>

<ParamField path="filter" type="string | TraceFilter | FilterOptions">
  Collision filter. Defaults to `"bullet"`.
</ParamField>

```ts theme={null}
const result = Trace.capsule(start, end, 16.0, 36.0, "player_movement");
```

### Trace.rayAll

<br />

```ts theme={null}
Trace.rayAll(start: Vector, end: Vector, filter?, maxHits?: number): TraceResult[]
```

Casts a ray and returns **all** hits along the path, not just the first.
Useful for penetration simulation - you get every surface the ray passes
through.

<ParamField path="start" type="Vector" required>Ray origin.</ParamField>
<ParamField path="end" type="Vector" required>Ray endpoint.</ParamField>

<ParamField path="filter" type="string | TraceFilter | FilterOptions">
  Collision filter. Defaults to `"bullet"`.
</ParamField>

<ParamField path="maxHits" type="number" default="32">
  Maximum number of hits to return. Clamped to `1..128`.
</ParamField>

```ts theme={null}
const hits = Trace.rayAll(eye, target, "bullet", 8);

for (const hit of hits) {
    console.log(`Hit at fraction ${hit.fraction.toFixed(3)}`);
    if (hit.surface) {
        console.log(`  Material: ${hit.surface.name}`);
    }
}
```

***

## Spatial queries

Fast utility functions that answer spatial questions without giving you
a full `TraceResult`.

### Trace.isVisible

<br />

```ts theme={null}
Trace.isVisible(from: Vector, to: Vector): boolean
```

The **fastest** way to check line of sight. Returns `true` if nothing
blocks the path between `from` and `to`. Skips normal, material,
and entity resolution entirely - just a yes/no answer.

<ParamField path="from" type="Vector" required>Start position (e.g. eye position).</ParamField>
<ParamField path="to" type="Vector" required>Target position (e.g. enemy head).</ParamField>

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

const local = Entities.getLocalPlayer();
const enemy = Entities.getPlayers({ skipLocal: true })[0];

const localPos = local.m_pGameSceneNode.m_vecAbsOrigin;
const enemyPos = enemy.m_pGameSceneNode.m_vecAbsOrigin;

if (Trace.isVisible(localPos, enemyPos)) {
    console.log("Target is visible");
}
```

<Tip>
  Use `isVisible` over `Trace.ray` when you only need a boolean - it's
  significantly faster because it doesn't resolve any hit data.
</Tip>

### Trace.isSolid

<br />

```ts theme={null}
Trace.isSolid(point: Vector): boolean
```

Returns `true` if the given point is inside solid geometry. Useful for
checking whether a calculated position is valid or stuck in a wall.

<ParamField path="point" type="Vector" required>World position to test.</ParamField>

```ts theme={null}
if (Trace.isSolid(position)) {
    console.log("Point is inside a wall");
}
```

### Trace.overlapSphere

<br />

```ts theme={null}
Trace.overlapSphere(origin: Vector, radius: number, filter?): OverlapResult[]
```

Returns all colliders overlapping a sphere centered at `origin`.

<ParamField path="origin" type="Vector" required>Center of the sphere.</ParamField>
<ParamField path="radius" type="number" required>Sphere radius in game units.</ParamField>

<ParamField path="filter" type="string | TraceFilter | FilterOptions">
  Collision filter. Defaults to `"bullet"`.
</ParamField>

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

const local = Entities.getLocalPlayer();
const localPos = local.m_pGameSceneNode.m_vecAbsOrigin;

const nearby = Trace.overlapSphere(localPos, 256.0);

for (const overlap of nearby) {
    if (overlap.entity) {
        console.log(`${overlap.entity.className} - depth: ${overlap.depth}`);
    }
}
```

### Trace.closestPoint

<br />

```ts theme={null}
Trace.closestPoint(origin: Vector, maxDistance: number, filter?): ClosestPointResult
```

Finds the closest point on any surface within `maxDistance` of `origin`.

<ParamField path="origin" type="Vector" required>Search center.</ParamField>
<ParamField path="maxDistance" type="number" required>Maximum search radius in game units.</ParamField>

<ParamField path="filter" type="string | TraceFilter | FilterOptions">
  Collision filter. Defaults to `"bullet"`.
</ParamField>

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

const local = Entities.getLocalPlayer();
const localPos = local.m_pGameSceneNode.m_vecAbsOrigin;

const closest = Trace.closestPoint(localPos, 128.0);
if (closest.hit) {
    console.log(`Nearest wall at distance ${closest.distance.toFixed(1)}`);
}
```

***

## Results

### TraceResult

Returned by `Trace.ray`, `Trace.box`, `Trace.sphere`, `Trace.capsule`,
and each element of `Trace.rayAll`.

| Property | Type | Description |
| :- | :- | :- |
| `hit` | `boolean` | Whether the trace hit anything |
| `fraction` | `number` | `0..1` - how far along the path the hit occurred |
| `position` | [`Vector`](/api/types/vector) | Impact point in world space |
| `normal` | [`Vector`](/api/types/vector) | Surface normal at the impact point |
| `startSolid` | `boolean` | `true` if the trace started inside solid geometry |
| `allSolid` | `boolean` | `true` if the entire trace was inside solid |
| `hitSky` | `boolean` | Hit the skybox |
| `hitWindow` | `boolean` | Hit a window surface |
| `hitLadder` | `boolean` | Hit a ladder surface |
| `hitPassBullets` | `boolean` | Hit a pass-bullets surface |
| `entity` | `object \| null` | Entity info (see below) |
| `surface` | `object \| null` | Surface material info (see below) |

**`entity`** - populated when `resolveEntity` is `true` and the trace hit a dynamic entity:

| Property | Type | |
| :- | :- | :- |
| `className` | `string` | Entity class name, e.g. `"C_CSPlayerPawn"` |
| `handle` | `number` | Entity handle |
| `origin` | [`Vector`](/api/types/vector) | Entity origin |

**`surface`** - populated when `resolveMaterials` is `true`:

| Property | Type | |
| :- | :- | :- |
| `name` | `string` | Material name |
| `penetration` | `number` | Penetration factor (higher = easier to shoot through) |
| `damage` | `number` | Damage modifier for this surface |

```ts theme={null}
const result = Trace.ray(eye, target);

if (result.hit) {
    console.log(`Hit at ${result.position}, fraction: ${result.fraction}`);

    if (result.entity) {
        console.log(`Entity: ${result.entity.className}`);
    }
    if (result.surface) {
        console.log(`Material: ${result.surface.name}`);
        console.log(`Penetration: ${result.surface.penetration}`);
    }
}
```

***

### OverlapResult

Returned by each element of `Trace.overlapSphere`.

| Property | Type | Description |
| :- | :- | :- |
| `position` | [`Vector`](/api/types/vector) | Contact point |
| `normal` | [`Vector`](/api/types/vector) | Contact normal |
| `depth` | `number` | Penetration depth |
| `entity` | `{ className: string } \| null` | Entity class if the overlap is with an entity |

***

### ClosestPointResult

Returned by `Trace.closestPoint`.

| Property | Type | Description |
| :- | :- | :- |
| `hit` | `boolean` | Whether a surface was found within range |
| `position` | [`Vector`](/api/types/vector) | Closest point on the surface |
| `normal` | [`Vector`](/api/types/vector) | Surface normal at that point |
| `distance` | `number` | Distance from origin to the closest point |

***

## Collision tags

Tags control which collision layers are included or excluded in a
[filter's](#tracefilter) `includeTags` / `excludeTags` arrays.

| Tag | Description |
| :- | :- |
| `"pass_bullets"` | Surfaces that bullets pass through |
| `"player_clip"` | Player clip brushes - block players, not bullets |
| `"npc_clip"` | NPC clip brushes |
| `"grenade_clip"` | Grenade clip brushes |
| `"ladder"` | Ladder surfaces |
| `"window"` | Breakable windows |
| `"sky"` | Skybox surfaces |
| `"player"` | Player collision hulls |

***

## Recipes

### Fast visibility check

`Trace.isVisible` is the fastest way to check line of sight - it skips
normal / material / entity resolution and just returns a boolean.

```ts theme={null}
const local = Entities.getLocalPlayer();
if (!local) return;

const myEyes = local.m_pGameSceneNode.m_vecAbsOrigin;

for (const enemy of Entities.getPlayers({ skipLocal: true })) {
    if (enemy.m_iHealth <= 0) continue;

    const head = enemy.m_pGameSceneNode.m_vecAbsOrigin;
    if (Trace.isVisible(myEyes, head)) {
        console.log(`${enemy.controller?.m_iszPlayerName} is visible`);
    }
}
```

### Ground trace

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

const local = Entities.getLocalPlayer();
if (!local) return;

const feet = local.m_pGameSceneNode.m_vecAbsOrigin;
const below = new Vector(feet.x, feet.y, feet.z - 64);

const ground = Trace.ray(feet, below, "player_movement");
if (ground.hit) {
    console.log(`Ground at Z=${ground.position.z.toFixed(1)}`);
    console.log(`Normal: ${ground.normal}`);
}
```
