Combat Tokens

Documentation Unreal Engine AI Combat Tokens

Cap how many enemies swing at once: add one gate to the attack, set the pool size, and the rest keep circling.


One enemy swings while the rest circle. Each target hands out a fixed number of permission slots, and a gated attack takes one to run.

Set it up

30 seconds
  1. 1

    Put a Combat Token Gate on the attack

    Open the Action Set, select the attack card, and add a Combat Token Gate to its Scoring > Gates list. Set Token Tag to SEC.Token.Attack.
    DA_ActionSet_Grunt
    Combat Token Gate
    Token Tag 
    Cost1
    Token Tag names the pool this attack draws from.
  2. 2

    Do the same on every attack that shares the cap

    Give each melee attack the same tag and they draw from one pool. An action with no gate stays uncapped, which is what a dodge, a reposition or a taunt wants.
  3. 3

    Set how many may swing at once

    Project Settings > Plugins > Soulslike Enemy Combat > Combat Tokens. SEC.Token.Attack ships with a Budget of 2. Drop it to 1 for the classic one-attacker-at-a-time fight.
    Project Settings
    Combat Tokens > SEC.Token.Attack
    Budget2
    Reissue Delay0.0
    Budget is the slot count. Reissue Delay holds a freed slot back before anyone else takes it.
  4. 4

    Play, and watch the slots

    Press Play and run SEC.Debug.Tokens 1. A line above the target reads Token.Attack 1/1 while the pool is open and 0/1 while an enemy holds the slot, green when a slot is free and red when the pool is full.
A tag with no row in Project Settings hands out unlimited slots, so a gate carrying a tag you invented caps nothing. The overlay gives it away: that pool prints 1 held, unlimited in place of a free-against-allowed count. Add the row before you use the tag.
Slots belong to the target, so six enemies split across two players never spend each other's. An enemy claims against the target it is fighting, which is the focus actor its controller holds. An enemy with no focus target claims nothing and its attack runs uncapped. See Getting Started.
The AI takes its slots the moment the action starts and hands them back when it ends, whether it completed, was cancelled, was interrupted, or timed out. A dying enemy releases what it holds as part of its death shutdown. An enemy that finds the pool full drops that action from its choices and picks something else, so it keeps strafing, closing or backing off while it waits. Nothing queues and nothing takes turns in order: the next enemy to reach for the slot gets it.

Set the pool size

One row per tag under Combat Tokens in Project Settings. Two ship, counted separately, so an archer never spends a melee slot.
Combat Tokens
Token Budgets
2 Array elements
SEC.Token.Attack
Budget
2
Reissue Delay
0
SEC.Token.Ranged
Budget
2
Reissue Delay
0
FieldDefaultWhat to do with it
Token TagSEC.Token.Attack, SEC.Token.RangedNames the pool. Any tag of your own works once it has a row here.
Budget2 on both shipped rows, 1 on a row you addHow many enemies run an action from this pool against one target at a time. 1 for the strictest read. 0 shuts the pool and stops everything gated on the tag.
Reissue Delay0 sSeconds a freed slot stays out of circulation before another enemy can take it.

Pace how fast the next attack lands

Reissue Delay holds a freed slot back for that many seconds. At 0 the next enemy steps in the same frame the last one finishes. Raise it to somewhere around the length of an attack recovery and the fight breathes: one enemy swings, backs off, and a beat passes before the next comes in.
AdvancedReissue delay against cooldowns and recovery
Three separate clocks shape the same rhythm:
ClockScopeHolds back
CooldownOne action on one enemyThat action, for that enemy
Recovery TimeOne enemyEvery offensive action on that enemy
Reissue DelayOne pool on one targetThe freed slot, for everyone
Cooldown and recovery pace a single enemy. Reissue Delay paces the group, which is the one that reads as a fight taking turns rather than a crowd taking swings.
Reissue Delay runs on world time, so it honors pause and time dilation the way cooldowns do.

Give one target its own numbers

Add an SEC Token Budget Component to the actor being fought and fill Token Budgets with a row per tag. The rows apply when play begins and lift when the component leaves, so a boss with SEC.Token.Attack at 3 takes three attackers at once while every other enemy in the level stays on the Project Settings value.
Combat Tokens
Token Budgets
1 element
SEC.Token.Attack
3
Set Token Budget For This Actor changes one row mid-fight, for a boss phase that opens the arena up to more attackers. After editing the map itself at runtime, call Apply Budgets to push it.
The component sets counts. Reissue Delay stays project-wide and is edited in Project Settings.
AlternativeSetting a budget from a Blueprint graph instead
Get the SEC Combat Token Subsystem from the world and call Set Token Budget, passing the target actor, the tag and the count. Use it where no component sits on the target.
Clear Token Budget drops one row back to the Project Settings value. Clear Target forgets that actor completely, which is what an actor coming out of a pool wants so it does not inherit the last occupant's numbers.
Lowering a budget takes no slot back from an enemy already swinging. It starves new claims until that enemy finishes.

An attack that holds more than one slot

Cost on the gate sets how many slots the action holds while it runs. A pool of 2 and a heavy attack at Cost 2 means that attack runs alone, while two light attackers at Cost 1 pair up.
Combat Token Gate
Token Tag
SEC.Token.Attack
Cost
1
Gate
Invert
AdvancedTwo gates naming the same tag
An action carrying two Combat Token Gates on one tag keeps the larger cost rather than adding the two together. Each gate checks only its own cost while the action is scored, so adding them up would let the action win selection and then be refused every time it tried to start. To hold two slots, set Cost to 2 on one gate.
AlternativeActing only while everyone else is busy
Tick Invert on the gate and it reads the pool the other way: the action is blocked while a slot is free, and allowed once the pool is full. A flanker that should reposition while the attackers are committed is the case this covers.
An inverted gate claims nothing. It reads the pool and steps aside.

Reactions

A Combat Token Gate on a reaction passes and takes nothing. A parry or a dodge answers something the player did, so holding it back until an attack slot frees up would read as the enemy failing to defend itself.
Cap the offense with tokens and shape the defense with reaction priorities and cooldowns.

Combat Tokens against Combat Roles

Both limit how many enemies press the player, from different ends.
Combat RolesCombat Tokens
LimitsHow many enemies hold a roleHow many enemies run an action at once
GranularityThe enemy, for as long as it holds the roleOne action, for as long as it runs
ChangesOn a reassignment cycle, with hysteresisThe moment an action starts or ends
ReachSwaps the Action Set, Reaction Set and Movement ProfileAllows or blocks one action
Roles decide who is in the fight and how they behave. Tokens decide who swings right now. They stack: give three enemies the Attacker role so they close in and press, and set the attack pool to 1 so one of them swings at a time.
Tokens work on their own too. An enemy that holds no role still takes and returns slots.

Debug it

SEC.Debug.Tokens 1     // Each target's pools above its head, free against allowed
SEC.Debug.All 1        // Every SEC overlay at once
One line per token tag, written above the head of the target the slots belong to: how many are free against how many it allows, and how many are waiting out a reissue delay with the seconds left on the first of them. A tag with no budget anywhere reads 1 held, unlimited instead, counting the holders against a pool that caps nothing. Green has a slot to give, red is full, grey is waiting on a delay. The ledger lives on the server, so the overlay draws there.
Tick Debug Overlay Tokens on an actor's SEC Token Budget Component to light that one target up without the console.
For a record you can scroll back through, raise the token log:
Log LogSECCombatToken Verbose
It prints each refused claim with the tag, the cost and the holder, each budget change, and each pool whose free count moved.
AdvancedNothing is capped
Six causes, in the order worth checking:
  • The attack carries no gate. The cap covers the actions that ask for it.
  • The Token Tag has no row anywhere. No row in Project Settings and no budget on the target means unlimited, and the overlay prints that pool as 1 held, unlimited.
  • The enemies are fighting different targets. Pools are per target, so two enemies on two players spend two separate pools.
  • The enemy holds no focus target. Slots are claimed against the focus actor, so an enemy without one attacks uncapped.
  • The gate is inverted. An inverted gate reads the pool and claims nothing.
  • The attack starts outside the action loop. Tokens ride the action execution path.
AdvancedAbilities started outside the action loop
A Blueprint that activates an ability straight on the ability system component, or a Behavior Tree that runs one outside the action loop, swings without taking a slot and the pool never sees it. Route attacks through actions to keep the cap.
AdvancedWhy an action can pass the gate and still not start
The gate reads the pool while the action is scored. The slot is taken when the action starts. Between those two moments another enemy can take the last slot, and the start is refused.
A refused start costs nothing: no cooldown is stamped, no recovery window opens, no delegate fires, and the AI picks again on its next tick. The score breakdown in the Action Set editor can show the gate passing on the frame the claim lost the race, which is worth knowing when a debug view and the fight seem to disagree.
AdvancedReading and driving a pool from Blueprint
Get SEC Combat Token Subsystem from the world. Actions carrying a gate claim and release on their own, so reach for the claim nodes only for something the action loop does not drive.
NodeReturnsWhat it does
Set Token BudgetSets how many slots one target hands out for a tag.
Clear Token BudgetDrops the override so the tag falls back to Project Settings.
Clear TargetForgets a target's overrides, held slots and cooling slots.
Get Token BudgetIntegerThe resolved count, a very large number when the tag is unlimited.
Get Available TokensIntegerFree slots right now, counting out held and cooling ones.
Can Claim TokenTrue or falseWhether an AI could take the slots. True while it already holds one.
Holds TokenTrue or falseWhether an AI is holding a slot of that tag against the target.
Get Token HoldersControllersWho has permission right now. Read it for a debug overlay, or to pick someone to interrupt.
Get Token Pool StatusOne row per tagBudget, free, held, cooling, seconds to the next reissue, and the holders. Sorted by tag.
Is Token TargetTrue or falseWhether this actor has been in a fight the tokens govern. Stays true for the rest of the level, so a readout holds steady between one attack and the next.
Try Claim TokensTrue or false, plus a receiptTakes slots directly. Pair each granted claim with Release Tokens.
Release TokensHands back the slots a receipt covers.
Release All Tokens For HolderIntegerHands back everything one AI holds, across every target, and reports how many claims were freed.
The receipt a granted claim hands back remembers the target it was taken against, so a release finds the right pool after the AI has moved on to someone else. Releasing the same receipt twice, or one whose target is gone, does nothing. An enemy destroyed while holding a slot frees it the next time the pool is read, so a pool cannot be held shut by an AI that has left the level.
On Token Availability Changed fires the moment a pool's free count moves, carrying the target, the tag, the free slots and the budget. Bind it for a HUD readout instead of asking for the count each frame. Two things send nothing: a slot handed back with a reissue delay, which fires once the delay ends and the count rises, and an unlimited tag, whose count never moves.
MultiplayerMultiplayer
The ledger lives on the server and nothing about it replicates. Every function that changes it does nothing on a client, and the queries there report no block, so a client-side debug widget cannot show a veto the server would apply.
Clients see the result through the ability and animation replication that already happens: the enemy that got the slot plays its attack, the ones that did not keep circling.

PageWhy
Action SystemWhere the gate sits, and the rest of the scorers and gates
Combat RolesThe other half of crowd control, deciding who is in the fight
Reaction SystemDefense, which tokens leave alone
ConfigurationEvery Project Settings value in one place
API ReferenceThe claim and release surface in C++