# Threat Detection

> Turn the player's camera attention into a 0 to 1 reading, and make an enemy swap sides or give ground under a stare.

Markdown version of https://rigbak.com/docs/soulslike-combat/threat-detection, a page of the Soulslike Enemy Combat documentation. Link that page when citing it.

> An enemy that feels your camera on it. Hold a stare and it flips the side it circles on, or backs off to buy itself room.

*Interactive diagram on the page: Enemy strafes around the player. Aim toward the AI to raise threat (even outside the 15° cone); hold inside the cone for 3s to fire On Threat Duration Exceeded and swap strafe side when the profile allows it.*

## Set it up (30 seconds)

1. **Check the controller carries the component**

   An AI controller made from `EnemyControllerBase` ships a **Threat Detection** component and needs nothing added. On your own controller, add **Threat Detection** to the AI controller, not the pawn.
2. **Turn on a response**

   Open the enemy's **Movement Behavior Profile** and under **Threat Response** tick **Swap Strafe on High Threat** and **Adjust Distance by Threat**. Both ship off, so this is the step that makes a stare visible.

   In **MovementProfile_SEC_Attacker**, under Threat Response, set:

   | Property | Value |
   | --- | --- |
   | Swap Strafe on High Threat | true (replaces false) |
   | Adjust Distance by Threat | true (replaces false) |

   *Both switches ship off. Tick them and a stare reaches movement. Threat Distance Scale under them stays grayed until Adjust Distance by Threat is on.*
3. **Play, and stare at it**

   Press Play, point the camera at the enemy and hold it there. Three seconds of looking flips the side it circles on. With **Adjust Distance by Threat** ticked as well, it widens the range it holds for as long as the camera stays on it.
4. **See the numbers behind it**

   Run `SEC.Debug.Threat.Draw 1`. A cone draws forward from the player, and a ring above the enemy closes white through orange to red as the stare completes.

> **Warning**
>
> Threat reads the controller's focus actor, and nothing sets that until a combat target is registered. An enemy with no target sits at 0 however hard you stare at it. See [Getting Started](https://rigbak.com/docs/soulslike-combat/getting-started) and [Targeting](https://rigbak.com/docs/soulslike-combat/targeting-system).

---

## Two readings, two questions

**Threat level** is how directly the focus target aims at this enemy, from 0 to 1. Any pawn contributes its control rotation, which for a player means the camera direction rather than the character's facing, so a player who swings the camera off an enemy reads 0 even while running straight at it. A focus actor that is not a pawn contributes its forward vector instead. The value tracks the aim frame by frame and carries no history.

**The stare timer** runs only while that aim sits inside **Player Look Angle Threshold** of the enemy. Hold it for **Player Look Time Threshold** and the duration event fires. Look away and the timer clears.

The two disagree on purpose. Threat level can read 0.7 while the timer has not started, because the camera points broadly toward the enemy without landing inside the cone.

---

## Tune the stare

**Details panel: Threat Detection Component** (component, in Enemy Controller Base)

**Category: Threat Detection**

| Property | Value |
| --- | --- |
| Player Look Angle Threshold | 15 |
| Player Look Time Threshold | 3 |

**Category: Debug**

| Property | Value |
| --- | --- |
| Debug Log Threat | false |
| Debug Draw Threat | false |

*Sits on the AI controller. Evaluation starts off on a controller made from Enemy Controller Base, and the SEC Combat Controller turns it on at BeginPlay when Enable Threat Detection is ticked.*

| Field | Default | What to do with it |
| --- | --- | --- |
| **Player Look Angle Threshold** | 15 degrees | Half-angle of the cone that counts as looking. Widen it toward 30 for an enemy that answers a glance, tighten it toward 5 for one that answers a dead-center stare only. Accepts 5 to 90. |
| **Player Look Time Threshold** | 3 seconds | How long the stare holds before the duration event fires. Accepts 0.1 to 10. |

Each time the duration fires, the timer resets and the next threshold rolls a random value between the base and double it, so a pack caught by one stare does not swap sides in lockstep. Looking away puts the next threshold back at the base value.

---

## Make movement answer it

Both responses live on the [movement profile](https://rigbak.com/docs/soulslike-combat/movement-system), so each [combat role](https://rigbak.com/docs/soulslike-combat/combat-roles) can answer a stare its own way.

**Details panel: Movement Behavior Profile** (data asset, in Movement profile asset)

**Category: Threat Response**

| Property | Value |
| --- | --- |
| Swap Strafe on High Threat | false |
| Adjust Distance by Threat | false |
| Threat Distance Scale | 1 (greyed out) |

*Role-swapped fields on the active movement profile. Both switches ship off on a new profile.*

| Field | Default | Does |
| --- | --- | --- |
| **Swap Strafe on High Threat** | off | Each completed stare flips the side the enemy circles on. |
| **Adjust Distance by Threat** | off | Threat widens the range the enemy holds, continuously rather than in steps. |
| **Threat Distance Scale** | 1.0 | How far that widening goes. The held range is multiplied by `1 + threat x scale`, so scale 1 under a dead-center stare doubles it. Accepts 0 to 5. |

The **SEC Combat Controller** component is what carries a stare into movement. It binds the two threat events at BeginPlay and drives the **Movement Evaluator** on the same controller, so both components have to sit on that controller for either switch to do anything.

> **Note**
>
> The profile that answers is the one the enemy's current role resolved to. Editing **Default Movement Profile** changes nothing for a role that has its own entry in **Role Movement Profiles**.

---

## Read it yourself

Movement is the one response SEC wires on its own. Everything else, a bark, an animation flag, a boss phase, binds to these.

| Node | Gives you |
| --- | --- |
| **Get Current Threat Level** | The 0 to 1 reading, this frame. |
| **Get Player Look Duration** | Seconds the current stare has held. Clears when the player looks away. |
| **Is Player Looking at Me** | Whether the aim sits inside the angle cone. |
| **Is Threat Detection Enabled** | Whether evaluation is running on this component. |
| **On Threat Level Changed** | Fires when the reading moves by more than 0.01. |
| **On Threat Duration Exceeded** | Fires when a stare completes, carrying the seconds it took. |

---

## Debug it

```
SEC.Debug.Threat.Draw 1   // Look cone from the player, filling ring above each enemy
SEC.Debug.Decision 1      // Threat, distance, angle, sight and speed above each enemy
```

The cone turns red the moment the aim crosses inside the angle threshold, and the ring drawn about a meter above the enemy closes white through orange to red as the timer runs toward its current threshold.

The decision line is an overlay section, so `SEC.Debug.All 1` brings it up with the rest and `SEC.Debug.WatchPawn <name>` narrows the overlays to one enemy. The threat drawing has its own switch and answers to neither.

The **Threat Detection** component carries the same two switches per enemy. **Debug Draw Threat** draws the cone and ring for that one enemy with the console variable off. **Debug Log Threat** writes a record instead: the look angle, the running timer against the threshold it is chasing, and the length rolled for the next one. Both stay blank while evaluation is off, as it is once the enemy dies.

<details>
<summary>Nothing answers the stare (Advanced)</summary>

In the order worth checking:

- **No combat target.** No focus actor means no threat, and this logs nothing at all. It is the common one.
- **Both profile switches are off.** A new **Movement Behavior Profile** ships with **Swap Strafe on High Threat** and **Adjust Distance by Threat** unticked.
- **The role uses a different profile.** Check **Role Movement Profiles** on the AI Config before assuming **Default Movement Profile** is the one in play.
- **No Movement Evaluator, or no SEC Combat Controller, on the controller.** The switches have nothing to drive, or nothing to carry the events to them.
- **The component sits on the pawn.** It belongs on the AI controller. Anywhere else it warns once at BeginPlay and stops.
- **Enable Threat Detection is unticked** on the **SEC Combat Controller** component, so a controller made from `EnemyControllerBase` never starts evaluating.
- **The enemy died.** Shutdown turns threat evaluation off, and a revive turns it back on.

</details>

<details>
<summary>Switching evaluation off, and throttling it (Advanced)</summary>

**Enable Threat Detection** on the **SEC Combat Controller** component turns evaluation on at BeginPlay. It ships ticked, and a controller made from `EnemyControllerBase` starts with evaluation off, so this box is what starts it.

**Details panel: SEC Combat Controller Component** (component, in Enemy Controller Base)

**Category: Threat Response**

| Property | Value |
| --- | --- |
| Enable Threat Detection | true |

*Binds the threat events at BeginPlay whenever a Threat Detection component sits on the same controller. Enable Threat Detection turns evaluation on there and never turns it off.*

Unticking it leaves that switch-on undone and never turns a running component off. A hand-built controller carrying the component without the base class starts evaluating on its own, so call **Set Threat Detection Enabled** with false to stop it.

**Set Threat Detection Enabled** flips it at runtime. Switching it off clears the reading, the timer and the looking flag, and stops the component ticking, so the debug drawing and logging stay blank until evaluation is back on.

**Set Tick Interval** sets the seconds between updates. The default of 0.1, ten updates a second, costs a three second stare nothing; 0 evaluates every frame. **Get Current Threat Level** and **On Threat Level Changed** move at that rate.

The delegates bind whether or not evaluation is enabled, so a handler stays connected across an off and on cycle.

</details>

<details>
<summary>Letting a stare bias which move an enemy picks (Advanced)</summary>

No shipped scorer reads this component, so an action favors nothing on its own while the player stares. Write one of your own:

1. Make a Blueprint child of **SEC Scorer**.
2. Override **Score Multiplier**. Take **Controller** off the context, get the **Threat Detection** component from it, and read **Get Current Threat Level**.
3. Turn that 0 to 1 reading into the multiplier you want. 1 leaves the action where it was, above 1 favors it, below 1 pushes it down.
4. Add the scorer to the action's **Scoring > Scorers** list, the same as a shipped one. See [Action System](https://rigbak.com/docs/soulslike-combat/action-system).

The figure the overlay prints alongside distance and angle is a different one: it lives on the decision context that feeds the brain and never reaches a scorer. Read the component for camera attention.

The same scorer in C++ is in the [API Reference](https://rigbak.com/docs/soulslike-combat/api-reference#usecscorer-and-usecgate).

</details>

<details>
<summary>Wiring it without the SEC Combat Controller (Alternative)</summary>

The component senses and broadcasts; it drives nothing and holds no reference to movement. On a controller without the SEC pieces:

1. Add **Threat Detection** to the AI controller.
2. Call **Set Threat Detection Enabled** with true.
3. Bind **On Threat Duration Exceeded**, or **On Threat Level Changed**, to whatever the stare should cause.

Focus still has to come from somewhere, either **Set Focus** on the controller or SEC [targeting](https://rigbak.com/docs/soulslike-combat/targeting-system).

</details>

<details>
<summary>Threat next to awareness (Advanced)</summary>

[Awareness](https://rigbak.com/docs/soulslike-combat/awareness-system) answers what this enemy has sensed: whether it has spotted a target, how sure it is, where it last saw it. Threat answers the opposite direction, whether the target is paying attention to this enemy.

They share no state and neither gates the other. An enemy can sit at full threat while its awareness has lost the player behind a pillar.

</details>

---

Previous page: [Targeting System](https://rigbak.com/docs/soulslike-combat/targeting-system). Next page: [Awareness System](https://rigbak.com/docs/soulslike-combat/awareness-system)
