# Teams

Every agent carries one **team id** - a plain integer you assign whatever meaning you like.
Agents spawn on team `0`. Teams do two things: they **filter queries**, so you can ask for
"the enemy units in this rectangle" in one call, and they **override avoidance** between
specific pairs of teams.

Nothing about teams is required. Leave every agent on team `0` and the simulation behaves
exactly as it did before teams existed.

## Setting and reading

```cpp
Crowd->SetAgentsTeam(Squad, 1);        // whole selection
Crowd->SetAgentTeam(Index, 2);         // one agent
TArray<int32> Teams = Crowd->GetAgentTeams({});   // parallel to GetAgentPositions
```

Team ids can be **any integer**, including negatives. They are yours - the simulation never
assigns meaning to a particular number, and changing an agent's team at runtime takes effect
on the next query or tick.

## Filtering queries

Every selection query takes a **`Teams`** array. Pass an **empty array** to get every agent,
or a list of team ids to keep only agents on those teams:

```cpp
TArray<int32> Everyone = Crowd->GetAgents({});           // all agents
TArray<int32> Enemies  = Crowd->GetAgents({2, 3});       // teams 2 and 3
TArray<int32> InBox    = Crowd->GetAgentsInRect(Rect, {2});
TArray<int32> Marquee  = Crowd->GetAgentsInScreenRect(PC, Min, Max, {1});
```

Because empty means "everything", adding the filter didn't change what any existing call
returns.

Two queries exist specifically for this:

| Function | What it does |
|---|---|
| `FilterAgents(Indices, Teams)` | Narrows an index array you already have. Preserves order and drops out-of-range entries. |
| `GetNearestAgent(Center, Radius, Teams, OutDistance)` | Closest agent to a point, or `-1`. `Radius <= 0` searches everywhere. |

`GetNearestAgent` is the "find me a target" call - closest enemy to this unit, closest
friendly to this healer.

::: tip
Filtering happens inside the simulation, over its own packed arrays. Pulling every agent into
Blueprint and filtering there with a loop costs far more at high counts.
:::

## Team avoidance rules

By default every agent pushes every other agent using the global **Strength** and
**Settle Push** values (see [Avoidance](./03-avoidance.md)). A team rule overrides both for
one **pair** of teams:

```cpp
Crowd->SetTeamAvoidance(1, 2, Strength, SettlePushStrength);
```

Rules are **symmetric** - `(1, 2)` and `(2, 1)` are the same rule, and setting one overwrites
the other. Passing the **same team twice** sets the rule *within* that team:

```cpp
Crowd->SetTeamAvoidance(1, 1, 60.0f, -1.0f);   // team 1 packs tightly among itself
```

Each of the two values is read the same way:

| Value | Meaning |
|---|---|
| `< 0` | **Follow the global slider.** Not a snapshot - if you later change **Strength** in the details panel, this pair follows it. |
| `0` | **Disable that push.** The pair stops pushing each other in that situation. |
| `> 0` | Use this value instead of the global one. |

Setting **both** to `0` makes the two teams ignore each other completely and walk straight
through one another - useful for ghosts, spectators, or units on separate logical layers that
happen to share the map.

```cpp
Crowd->SetTeamAvoidance(1, 2,  0.0f,  0.0f);   // teams 1 and 2 pass through each other
Crowd->SetTeamAvoidance(1, 2,  0.0f, -1.0f);   // don't jostle in transit, still step aside when idle
Crowd->SetTeamAvoidance(1, 2, -1.0f,  0.0f);   // normal jostling; idle units hold their ground
```

Managing rules:

| Function | What it does |
|---|---|
| `ClearTeamAvoidance(A, B)` | Drops that pair's rule; it falls back to the global values. |
| `ClearAllTeamAvoidance()` | Drops every rule. |
| `GetTeamAvoidance(A, B, OutStrength, OutSettlePushStrength)` | Returns `false` if no rule is set, in which case both outputs read `-1`. |

### Cost

With **no rules set**, the avoidance loop does no team work at all - it checks once that the
rule table is empty and skips every lookup. Measured against the same build without teams,
the difference is inside run-to-run noise.

With rules active the lookup happens only for pairs that are **actually touching**, not every
pair the spatial grid visits, which costs a few percent of tick time in dense crowds.

## Seeing teams

Tick **Show Teams** in the Debug section to draw a coloured dot above every agent, one colour
per team id. The agents nearest the camera also get their team id as text, out to
**Team Label Distance**; the text is capped to keep the overlay readable in big crowds, while
the dots are drawn for everyone.
