# Indices & Stable IDs

Every agent in a crowd has **two handles**: an **index** and a **stable id**. Knowing which
to use, and when, is the single most important thing to get right when scripting against the
crowd.

## Index - fast, but not stable

An **index** is the agent's slot in the crowd's arrays (`0 … GetAgentCount()-1`). All the
per-agent getters and order functions take indices, and the query functions
(`GetAgentsInRect`, `GetAgentsInRadius`, `GetAgentsInScreenRect`) return them.

Indices are **not stable across removals.** When an agent is removed, the simulation fills
its slot with the **last** agent (swap-and-pop), so some other agent's index changes. An
index is only guaranteed valid **within the same tick** you obtained it.

::: tip
For the common loop - *select this frame → order this frame → render this frame* - indices
are exactly right and you never need ids. Reach for ids only when you **hold a reference to a
specific agent across ticks**.
:::

## Stable id - durable, for tracking over time

A **stable id** is unique per crowd actor, **never reused** (not even across
`ResetSimulation`), and **fixed for the agent's whole lifetime**. Use ids whenever you need
to remember a particular agent between frames - a unit's health bar, a selection that
persists, a "go here when you arrive" follow-up.

### Translating between the two

```cpp
// Single:
int32 Id    = Crowd->GetAgentId(Index);   // -1 if Index out of range
int32 Index = Crowd->GetAgentIndex(Id);   // -1 if no such agent (e.g. removed)

// Batch (one call for a whole selection):
TArray<int32> Ids     = Crowd->GetAgentsIds(Indices);     // -1 per out-of-range entry
TArray<int32> Indices = Crowd->GetAgentsIndices(Ids);     // -1 per unknown id
```

A returned **-1 means the agent is gone** - the standard way to detect that a tracked unit
was removed.

## The pattern

```
Selection time:   indices ──GetAgentsIds──► store ids        (durable handle)
                                              │
Each later use:   stored ids ──GetAgentsIndices──► indices    (re-resolve)
                                              │
                                              ▼
                            drop the -1s (removed), use the rest
```

So: **convert to ids when you store, convert back to indices when you act.**

## Events speak ids, broadcast as indices

The [movement events](./05-events.md) (`OnAgentsReachedGoal`, `OnAgentsStuck`,
`OnAgentsOrdersCanceled`) are buffered internally as **stable ids** - because an event can
outlive the tick that produced it while indices reshuffle - but the actor resolves them to
**current indices** before broadcasting, so your handler receives ready-to-use indices.
Agents removed between the event and the broadcast are simply dropped from the batch.

The spawn/remove events (`OnAgentsSpawned`, `OnAgentsRemoved`) also deliver **indices**, and
fire **before** the tick that applies the change, so those indices are still valid in the
handler (see [Events](./05-events.md) for the exact timing).

## Custom per-agent data

The crowd doesn't store arbitrary gameplay data per agent. The intended pattern is to key
**your own** `Map<int32, ...>` by the agent's **stable id** - ids survive the swap-and-pop
shuffles that would scramble an index-keyed map.
