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

# AI behaviors

> Give an NPC a steering goal (idle / patrol / chase) over HTTP, step the world, and read back the velocity it produced — plus what the behavior-tree library offers. Classical and deterministic, not an LLM.

Euca's in-engine AI drives **NPCs**, and it is **classical and deterministic — not a language model**. There
are two layers: a simple steering goal you set over HTTP (`/ai/set` attaches an `AiGoal`; the `ai_system`
turns it into a desired `Velocity` every tick), and a behavior-tree library (`euca-ai`) you author in Rust.
This page sets a chase goal and reads the velocity the tick produces; every command is real and runnable
against a [local server](/quickstart), with output captured from one.

<Note>
  **"AI" here is never an LLM.** The *agents* that drive Euca are external clients (your code or model, over
  the [API](/api-reference/introduction)). This page is about the *NPC* AI that lives inside the world. Fully
  headless.
</Note>

## Set a chase goal and step it

Spawn a chaser and a target **at a distance** (chasing a target at your own position gives a degenerate
zero/NaN direction — keep them apart):

```bash theme={null}
curl -s -X POST http://localhost:3917/spawn -H 'Content-Type: application/json' -d '{"position":[0,0,0]}'
# {"entity_id":2,"entity_generation":0}
curl -s -X POST http://localhost:3917/spawn -H 'Content-Type: application/json' -d '{"position":[10,0,0]}'
# {"entity_id":3,"entity_generation":0}
```

Attach a `chase` goal to the chaser, pointing at the target, with a speed. Read it back **before stepping** —
the goal is set, but no velocity has been computed yet:

```bash theme={null}
curl -s -X POST http://localhost:3917/ai/set \
  -H 'Content-Type: application/json' \
  -d '{"entity_id":2,"behavior":"chase","target":3,"speed":3.0}'
# {"ok":true,"message":"Set entity 2 AI to chase"}

curl -s http://localhost:3917/entities/2
# {"id":2,...,"velocity":{"linear":[0.0,0.0,0.0],...},"ai":"chase (target: 3)"}
```

The desired velocity is computed by the `ai_system`, which runs **on a tick**. Step once, then read again:

```bash theme={null}
curl -s -X POST http://localhost:3917/step -H 'Content-Type: application/json' -d '{"ticks":1}'
curl -s http://localhost:3917/entities/2
# {"id":2,...,"velocity":{"linear":[3.0,0.0,0.0],...},"ai":"chase (target: 3)"}
```

The chaser now has a velocity of `[3.0, 0.0, 0.0]` — speed `3.0`, aimed straight at the target on the +x axis.
`patrol` reads back as `"patrol (3 waypoints)"`; `idle` as `"idle"`.

## Behavior and gotchas

The things you only find out by running it:

* **The goal sets immediately; the velocity computes on a tick.** `/ai/set` attaches the `AiGoal` at once
  (`ai` reflects it right away), but `velocity` stays `[0,0,0]` until the next `/step` runs the `ai_system`.
  Set, then step, then read the velocity.

* **`/ai/set` accepts only `idle`, `chase`, and `patrol` over HTTP — `flee` (and any other string) falls
  through to `idle`.** The response message echoes whatever you sent, so it *looks* accepted, but the stored
  goal is idle:

  ```bash theme={null}
  curl -s -X POST http://localhost:3917/ai/set \
    -H 'Content-Type: application/json' -d '{"entity_id":2,"behavior":"flee","speed":3.0}'
  # {"ok":true,"message":"Set entity 2 AI to flee"}   ← message echoes your input

  curl -s -X POST http://localhost:3917/step -H 'Content-Type: application/json' -d '{"ticks":1}'
  curl -s http://localhost:3917/entities/2
  # {"id":2,...,"ai":"idle"}   ← actually stored as idle
  ```

  `AiBehavior::Flee` exists in the engine; it just has no HTTP path. Trust the `ai` field on read-back, not the
  message.

* **`ai_system` computes velocity, not position.** It writes a desired `Velocity` toward the goal. Turning that
  velocity into movement needs a physics/character-controller step on the entity; a bare spawned unit holds the
  velocity but won't necessarily advance its `transform`. Spawn with a `physics_body` (or run the relevant
  movement system) if you want the unit to actually travel.

* **Setting AI on a missing entity returns `ok:false`.** `{"entity_id":999,...}` →
  `{"ok":false,"message":"Entity 999 not found"}`. Nothing is changed.

## Behavior trees (`euca-ai`)

For richer NPC logic, the `euca-ai` crate is a full behavior-tree library, authored in Rust:

* **Composites** — `Sequence`, `Selector`, `Parallel { RequireAll | RequireOne }`
* **Decorators** — `Inverter`, `RepeatN`, `RepeatUntilFail`, `Cooldown`, `Guard`
* **Leaves** — `Action` (`MoveTo`, `Wait`, `SetBlackboard`, `Log`, `Custom`) and `Condition` (`HasKey`,
  `Compare`, `InRange`, `IsAlive`, `Custom`)
* **Blackboard** — a per-entity typed key-value store; `BtBuilder` is a fluent construction DSL.

`behavior_tree_system` ticks every `BehaviorTreeExecutor`. **Behavior trees have no HTTP surface** — they're
authored in Rust today; the only AI you can drive over HTTP is the `/ai/set` steering goal above.

## Endpoints

| Method | Path      | Description                                                                                 |
| ------ | --------- | ------------------------------------------------------------------------------------------- |
| `POST` | `/ai/set` | Set an NPC steering goal (`idle` / `chase` / `patrol`; other values fall through to `idle`) |

## Status

* **Shipped** — Steering goals (`idle` / `chase` / `patrol` over HTTP; `flee` exists in-engine but has no HTTP
  path), and the behavior-tree library (composites, decorators, leaves, blackboard, builder). No stubs.

<Card title="AI endpoint" icon="robot" href="/api-reference/introduction" horizontal>
  The `/ai/set` endpoint with its request and response schema.
</Card>
