# Targeting System

> Give every enemy something to fight: register the player once, pick how each AI chooses, and the pack engages.

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

> Nothing in SEC runs until an enemy has something to fight. Register the player once, and each enemy takes a target from the pool by whatever rule you gave it.

## Set it up (45 seconds)

1. **Register the player as a combat target**

   On the player pawn's **Event BeginPlay**, add **Get AICombat Role Subsystem**, then call **Register Combat Target** with **Target Actor** set to **Self** and **Auto Assign Unassigned** ticked.

   Blueprint graph, nodes in reading order:

   - **Comment box: Player pawn · Event BeginPlay**

     - **AICombatRoleSubsystem** (subsystem getter)
     - **Register Combat Target** (function; Target is AICombat Role Subsystem); inputs: Target, Target Actor = Self, Auto Assign Unassigned = true, Reevaluate Existing = false

   Search the context menu for `Register Combat Target`, under **AI | Combat Role | Targeting**. AI decisions run on the server, so branch on **Has Authority** before the call.
2. **Tell each enemy how to choose**

   Open the enemy's **Enemy AI Config** and set **Target Selector** to **Closest Target Selector**.

   In **EnemyConfig_Grunt**, under Combat Role, set:

   | Property | Value |
   | --- | --- |
   | Target Selector | Closest Target Selector |
   | Target Filter | None |
   | Ignore Target Redistribution | false |

   *All three have working defaults. Target Selector is the one worth filling in once a fight holds more than one enemy.*
3. **Play, and watch it engage**

   Press Play and run `SEC.Debug.Role 1`. Every enemy draws its name and combat role above its head, colored by role. One that never took a target reads None.

> **Warning**
>
> **Target Selector** ships empty on the AI Config, and **Default Target Selector** ships empty in project settings. With neither filled in, an enemy takes the first target it may fight, whether that target was already in the pool or registered long after the enemy started running, as long as **Register Combat Target** is called with **Auto Assign Unassigned** ticked. That carries a single enemy and a single player. Fill one of the two in once several enemies share a fight or several players are registered, because then which target each enemy takes is a decision worth making.

---

## Pick how each enemy chooses

*Interactive diagram on the page: Click targets to eliminate. System periodically re-evaluates assignments based on strategy.*

| Selector | Picks | Use it for |
| --- | --- | --- |
| **First Target Selector** | The first registered target | Single player, one player pawn |
| **Closest Target Selector** | The nearest target | Melee chasers |
| **Balanced Target Selector** | The target with the fewest other enemies on it, staying put while tied for least loaded | Co-op fairness |
| **Random Target Selector** | One target at random | Chaotic encounters |
| **Fixed Target Selector** | The one actor carrying a tag you name | A training dummy, livestock, a siege gate |
| **Awareness Filtered Target Selector** | Wraps another selector and offers it only targets this enemy has perceived | Stealth |

### Where the selector comes from

**Category: Combat Role**

| Property | Value |
| --- | --- |
| Target Selector | None |
| Target Filter | None |
| Ignore Target Redistribution | false |

**Target Selector** on the AI Config answers first. Leave it empty and the project setting answers instead, under **Project Settings > Plugins > Soulslike Enemy Combat > Combat Roles > Target Selection**.

**Category: Target Selection**

| Property | Value |
| --- | --- |
| Default Target Selector | None |
| Default Target Filter | None |

Set the project default once for a whole game, then override it on the configs that want something else.

### Fight only what it has seen

Set **Target Selector** to **Awareness Filtered Target Selector**, and fill in **Awareness Config** on the same AI Config. See [Awareness System](https://rigbak.com/docs/soulslike-combat/awareness-system).

**Details panel: Awareness Filtered Target Selector** (instanced object, in Enemy AI Config → Combat Role)

**Category: Awareness Filter**

| Property | Value |
| --- | --- |
| Min Required State | Lost |
| Inner Selector | None |

*Instanced selector on the Enemy AI Config. Needs a SEC Awareness component on the AI controller.*

| Field | What to do with it |
| --- | --- |
| **Min Required State** | How much the enemy has to know before a target counts. **Lost**, the default, keeps it engaged through the grace period after it loses sight, on the same target, and picks again once it forgets it. **Detected** demands current sight, and picks again as the target drops to Lost. **Suspicious** lets a heard target through. |
| **Inner Selector** | Runs on what survives the filter, and takes any selector including another of these. Leave it empty for the nearest candidate. |

> **Warning**
>
> Without an awareness component on the AI controller the filter warns once, then passes the whole list through unfiltered, so the enemy fights everything. Controllers built on the plugin's base class carry one.

### Hunt one named actor

**Fixed Target Selector** pins an enemy to a single actor and nobody else, the player included.

1. Select the target actor in the level. Open the **Actor** category in the Details panel, click the advanced arrow, and add a name under **Tags**.
2. Register that actor with **Register Combat Target**, the same node the player uses.
3. Set **Required Tag** on the selector to the same name.

**Keep Current Target if Untagged** is on, so an enemy already fighting keeps the fight if something strips the tag mid-combat. Leave **Required Tag** empty and the enemy targets nothing at all, which is deliberate rather than a fallback to whoever is nearest.

> **Warning**
>
> Nesting this inside an **Awareness Filtered Target Selector** gives an enemy that fights nothing, because that wrapper offers only targets the enemy has perceived and a training dummy is never sensed. Tick **Always Aware** on the awareness config, or use the Fixed selector on its own.

---

## Decide who an enemy will fight at all

A selector picks from what an enemy is willing to fight, and a filter decides that first. The built-in answer refuses the enemy itself and anything it counts as an ally, and lets the rest through. An actor with no team declared reads as fair game, which is the ordinary case for a player pawn that never sets one.

Add a **SEC Team** component to declare sides. Characters built on the plugin's enemy base class carry one already.

| Field | Default | What to do with it |
| --- | --- | --- |
| **Team Id** | 1 | 0 to 254. Use 0 for the player and 1 for enemies. 255 means no team. |
| **Free for All** | off | Every other actor reads hostile, this actor's own team included, and **Team Id** stops deciding anything. |
| **Ignore Player** | off | Team 0 and any player-controlled pawn read as neutral, so this actor leaves the player alone. |
| **Excluded Team Ids** | empty | Actors on these teams read as neutral to this actor. |
| **Allowed Hostile Team Ids** | empty | Fill it and only these teams can read hostile; everything else reads neutral. Leave it empty to filter with **Excluded Team Ids** instead. |
| **Ignore Neutrals** | off | Keeps a teamless actor neutral while **Free for All** is on. Outside Free for All a teamless actor already reads neutral. |
| **Fight Only** | empty | Pick one actor in the level and this pawn hunts that alone, whatever the rows above say. Its own team still reads friendly. |

Change a team during play with **Set Team Id** on the component. Every enemy's senses read the new team at once, so an enemy can see an actor from the moment it turns hostile, and lets go of one that joins its side. **Set Free for All**, **Set Ignore Player** and the **Excluded Team Ids** setters have the actor's own senses read their rules again.

> **Note**
>
> These rules change what an enemy will fight, not what its weapon will hit. A swing that catches a bystander still lands, because a melee trace decides what it damages through its own **Team Filter**.

---

## Several targets at once

Each registered target owns its own [role pool](https://rigbak.com/docs/soulslike-combat/combat-roles), so two players each cap at three Attackers independently.

For co-op, register every player pawn and set **Default Target Selector** to **Balanced Target Selector**. Enemies spread themselves across the players, and each one stays with its current target while that target is among the least loaded. An enemy leaves itself out of its own target's count, so five enemies on two players settle three and two.

To pin a boss to whoever it first engaged, tick **Ignore Target Redistribution** on its AI Config. That enemy sits out the automatic redistribution passes, both the reassignment timer and the **Reevaluate Existing** pin, and keeps its target until the target dies. The hand-driven nodes in **Assigning targets by hand** below move it anyway.

---

## When a target dies

Unregister the target, or let it be destroyed, and the sequence runs on its own:

1. Every enemy assigned to it loses its target and its focus, and its combat role resets to None.
2. Each of those controllers fires **On Combat Target Lost**, carrying the target it lost.
3. Each one runs its selector again and takes another registered target if one is available.
4. **On Combatants Orphaned** fires once, carrying the lost target and everyone freed by it.

To send an enemy back to patrol instead of straight onto the next player, bind **On Combat Target Lost** on the pawn controller's **SEC Combat Controller** component, or implement the **On Combat Target Lost** event on an Enemy Controller Base subclass.

---

## Debug it

```
SEC.Debug.Role 1                 // Name and combat role above each enemy, colored by role
SEC.Debug.Decision 1             // Distance, angle, line of sight, speed and threat it scores against
SEC.Debug.Teams 1                // Team ID, Free-For-All, and what the exclusion rules turn aside
SEC.Debug.Awareness 1            // What each enemy knows about its target
SEC.Debug.LogRoleAssignments 1   // Role assignment scoring, 1 for basic and 2 for verbose
```

`SEC.Debug.All 1` turns every overlay on at once, and `SEC.Debug.WatchPawn <name>` narrows them to one enemy. Each overlay also has a checkbox on its own component, so one enemy can be isolated without the console.

An enemy holding role None while a target is registered never took one. Check its **Target Selector** first, then its team rules with `SEC.Debug.Teams 1`.

<details>
<summary>Enemies that spawn before the player (Advanced)</summary>

An enemy registered while the target pool is empty stays idle. Every call to **Register Combat Target** gives each idle enemy another chance, running its own filter and selector over the pool, so a player who appears late is picked up without extra wiring. An enemy whose team rules turn that target aside stays idle rather than being handed it.

That idle pass needs a selector. With none configured anywhere it returns nothing, which is the case the warning at the top of this page covers.

**Auto Assign Unassigned** adds an immediate role evaluation on top of the idle pass. **Reevaluate Existing** also forces enemies that already have a target to run their selector again, for a target that registers closer than the one they picked.

To pin one enemy to a pool at the moment it registers, set **Initial Target (Runtime Only)** on the role registration parameters before registration. It skips the selector entirely.

</details>

<details>
<summary>Redistributing targets on a timer (Advanced)</summary>

The reassignment timer updates roles only, unless you tick **Reevaluate Targets on Reassignment** under **Project Settings > Plugins > Soulslike Enemy Combat > Combat Roles > Timing**:

**Category: Timing**

| Property | Value |
| --- | --- |
| Role Reassignment Interval | 8 |
| Min Time in Role | 8 |
| Reevaluate Targets on Reassignment | false |

It has an effect only while more than one target is registered. Each cycle re-runs every enemy's selector, skipping any with **Ignore Target Redistribution**, then evaluates roles. An enemy keeps what it has when its selector answers with the same actor or with nothing.

**Set Reevaluate Targets on Reassignment** toggles the same flag at runtime.

</details>

<details>
<summary>Assigning targets by hand (Alternative)</summary>

For scripted encounters, drive the pools directly instead of leaving it to selectors. Every one of these lives on the AICombat Role Subsystem. **Reevaluate All Targets** is the only one that honors **Ignore Target Redistribution**; the rest carry out the order on a pinned enemy too.

| Node | Does |
| --- | --- |
| **Assign Combatant to Target** | Puts one enemy in one pool. Pass nothing to unassign. It carries out the order as given without asking the enemy's filter, so check the pairing yourself. |
| **Reassign Combatant Target** | Re-runs the selector for one enemy. On failure it clears the assignment, resets the role to None, and fires the target-lost path. |
| **Reevaluate All Targets** | Re-runs the selector for everyone, skipping **Ignore Target Redistribution**. |
| **Balance Combatants Across Targets** | Round-robin across every registered target, ignoring selectors. Each enemy is still only handed a target its own team rules allow. |
| **Transfer Combatants to Target** | Moves a whole pool, for a player-death handoff. An enemy that may not fight the destination is left where it was. |
| **Set Primary Target and Assign All** | Puts everyone on one target, for a boss or a VIP. An enemy whose team rules refuse that target keeps what it had. |
| **Select Target For** | Runs the enemy's configured selector over a candidate list of your own, such as sphere overlap results. Returns nothing when no selector is configured. |

</details>

<details>
<summary>Writing your own selector (Advanced)</summary>

Subclass **Target Selector** in Blueprint or C++ and override **Select Target**. It receives the controller, the candidate list, and a context the subsystem builds for it:

**Category: Target Selection Context**

| Property | Value |
| --- | --- |
| Current Target | None |
| Combatant Count Per Target | 0 Map elements |
| Distance Squared Per Target | 0 Map elements |
| Is Target Loss Reselection | false |

The candidate list arrives with everything its filter refuses already removed, the enemy itself included while the built-in answer is in play, so a selector decides preference rather than permission.

**Combatant Count Per Target** counts the asking enemy on its own **Current Target**, so subtract one there when comparing loads, as **Balanced Target Selector** does.

Return nothing while **Is Target Loss Reselection** is true to take over the handoff after a target dies. The subsystem leaves the enemy unassigned and your **On Combat Target Lost** handler runs the retreat, the delay, or the re-aggro itself.

</details>

<details>
<summary>Writing your own filter (Advanced)</summary>

Subclass **SEC Target Filter** in Blueprint and override **Can Fight**, for a charmed enemy that turns on its own side. Set it on the AI Config's **Target Filter** for one enemy, or as **Default Target Filter** in project settings for all of them.

Add a parent call first to keep the built-in answer and narrow it from there. Without one, the override replaces the self check, the ally check and the exclusion rules together.

Keep any property you add on a single-segment category (`Filter`, not `Filter|Advanced`). The class expands inline wherever it is set, and a nested category makes the details panel recurse until it overflows.

One filter answers for every targeting decision: the candidate list a selector sees, an actor joining the shared pool, and the awareness pass that wakes an enemy up. Exclusion rules bite through it and nowhere else, so a hand-rolled friend-or-foe check reads an excluded actor as neutral and lets it straight through.

</details>

<details>
<summary>Team reads and damage filtering (Advanced)</summary>

Team lookups read a **SEC Team** component on the actor, on its pawn, or on its controller, then fall back to the engine's own team agent. An actor with neither is neutral.

| Node | Returns |
| --- | --- |
| **Get Team Id** | 0 to 254, or 255 for no team |
| **Set Team Id** | True when a component or the engine agent accepted the ID |
| **Has Team** | Whether the actor has anything other than 255 |
| **Get Relation** | Friendly, Hostile, or Neutral, answered by the first actor's own rules |
| **Are Friendly** / **Are Hostile** | Same question as a bool |
| **Can Damage** | Whether a damage causer may hit a target under a filter mode |

Damage filtering is separate from targeting. **Can Damage** takes one of four modes: **No Filter**, **Enemies Only**, **Friendlies Only**, **Neutral Only**. The pawn's melee trace component exposes the same choice:

**Details panel: SEC Melee Trace** (component, in Enemy Character Base)

**Category: Damage**

| Property | Value |
| --- | --- |
| Team Filter | No Filter |

*Damage category on the wielder trace component. Filters who can be hit, separate from combat target selection.*

</details>

<details>
<summary>Subsystem events (Advanced)</summary>

| Event | Fires when |
| --- | --- |
| **On Combat Target Registered** | A target joins the pool |
| **On Combat Target Unregistered** | A target leaves it |
| **On Combatants Orphaned** | A target is lost, carrying it and every controller freed by it |
| **On Combat Role Changed for Target** | A role changes, carrying which pool it happened in |

The full list is on [Combat Roles](https://rigbak.com/docs/soulslike-combat/combat-roles).

</details>

<details>
<summary>Querying the pools (Advanced)</summary>

| Node | Returns |
| --- | --- |
| **Get All Combat Targets** / **Has Combat Target** | The registered target pool |
| **Get Combatant Target** | One enemy's assigned target |
| **Get Combatants for Target** | Everyone in that target's pool |
| **Get Role Count for Target** | How many of a role that pool is using |

**Set Combat Target** and **Get Combat Target** are deprecated and warn on compile. **Set Combat Target** calls **Register Combat Target** with auto-assign on; **Get Combat Target** returns the first registered target. Use **Get Combatant Target** or **Get All Combat Targets** instead.

</details>

---

## Where targeting connects

| System | How |
| --- | --- |
| [Combat Roles](https://rigbak.com/docs/soulslike-combat/combat-roles) | Every target owns an independent role pool. |
| [Awareness System](https://rigbak.com/docs/soulslike-combat/awareness-system) | The Awareness Filtered Target Selector gates candidates on what the enemy has perceived. |
| [Movement System](https://rigbak.com/docs/soulslike-combat/movement-system) | Movement steers relative to the controller's focus actor, which is set when a target is assigned. |
| [Action System](https://rigbak.com/docs/soulslike-combat/action-system) | Actions score against the assigned target, and one action can be handed another through [contextual execution](https://rigbak.com/docs/soulslike-combat/action-system#handing-an-action-data-at-the-moment-it-runs). |
| [Multiplayer](https://rigbak.com/docs/soulslike-combat/multiplayer) | Register each player pawn on the server. |

Previous page: [Combat Tokens](https://rigbak.com/docs/soulslike-combat/combat-tokens). Next page: [Threat Detection](https://rigbak.com/docs/soulslike-combat/threat-detection)
