# Gates, Costs and Cooldowns

> Gates, costs and cooldowns on Action Graph moves: a gate decides whether a move may start, a cost is what it spends, and a cooldown makes it wait before it runs again, banking uses as charges.

Markdown version of https://rigbak.com/docs/universal-player-combat/gates-costs-cooldowns, a page of the Universal Player Combat documentation. Link that page when citing it.

## A dodge with two charges and a stamina cost (5 minutes)

1. **Add a dodge**

   Open `AG_MyFirstGraph`, the graph from the [Action Graph](https://rigbak.com/docs/universal-player-combat/action-graph) quick start, still on the demo's fists (`Item_UPC_Unarmed`). Add a node `Dodge` that plays `AM_UPC_DodgeBack`, and wire **ROOT** to it on `UPC.Input.Dash`, which the fists send from Space (left shoulder on a gamepad).
2. **Give it two charges**

   On **Dodge**, add a **Gates** entry and pick **Cooldown**: **Cooldown Tag** `UPC.Cooldown.Ability.Dodge`, **Seconds** `3`, **Charges** `2`.

   **Details panel: Dodge** (instanced object, in AG_MyFirstGraph)

   **Category: Action Node Data**

   | Property | Value |
   | --- | --- |
   | Gates | 1 Array element |
   | Gates > Index [0] | Cooldown |
   | Gates > Index [0] > Cooldown Tag | UPC.Cooldown.Ability.Dodge |
   | Gates > Index [0] > Seconds | 3.0 |
   | Gates > Index [0] > Charges | 2 |
   | Effect Class | UPCNodeCooldownEffect (advanced) |

   *Two dodges, then a wait of 3 seconds for the first charge.*
3. **Make it cost stamina**

   Add a **Costs** entry and pick **Vital**: **Vital Tag** `UPC.Vital.Stamina`, **Amount** `25`. The demo player has that vital; a character of your own needs its row on the [Vitals](https://rigbak.com/docs/universal-player-combat/vitals) component.

   **Details panel: Dodge** (instanced object, in AG_MyFirstGraph)

   **Category: Action Node Data**

   | Property | Value |
   | --- | --- |
   | Costs | 1 Array element |
   | Costs > Index [0] | Vital |
   | Costs > Index [0] > Vital Tag | UPC.Vital.Stamina |
   | Costs > Index [0] > Amount | 25 |

   *25 stamina per dodge. The dodge runs only while 25 is there to spend.*
4. **Dodge three times**

   Save, play `LVL_UPC_Demo` and press Space three times within 3 seconds, each press after the last dodge has ended. The first two dodges play; the third drops. Press again 3 seconds after the first dodge and it plays. When you finish with `AG_MyFirstGraph`, set the fists' **Action Graph** back to `AG_UPC_Unarmed`.

---

## Gates: whether a move may start

A node starts only while every gate in its **Gates** list is open; a closed gate stops the move, and the press drops unless something else is wired ([what can play instead](https://rigbak.com/docs/universal-player-combat/gates-costs-cooldowns#try-another-move-instead)). Root ignores gates.

| Gate | Opens while |
| --- | --- |
| **Cooldown** | A charge is ready. Each use spends one, and a timer brings it back. |
| **Tags** | The character carries the tags it asks for: a skill unlock. |
| **Vital** | A vital such as stamina meets a level. It spends nothing. |

### Cooldown

| Setting | Does |
| --- | --- |
| **Cooldown Tag** | Names the timer, matched exactly; empty turns the gate off. Moves with the same tag share it, in any graph; give them the same **Seconds** and **Charges**, since each gate judges the shared timer by its own values. |
| **Seconds** | Until a used charge returns (1). At 0 the gate stays open, with a compile warning. |
| **Charges** | Uses the move banks; 1 is an ordinary cooldown. |

Each use starts its own 3-second timer: dodges at 0 s and 1 s bring charges back at 3 s and 4 s.

### Tags: a skill unlock

**Details panel: Heavy Finisher** (instanced object, in AG_MyFirstGraph)

**Category: Action Node Data**

| Property | Value |
| --- | --- |
| Gates | 1 Array element |
| Gates > Index [0] | Tags |
| Gates > Index [0] > Required Tags | no tags |
| Gates > Index [0] > Blocked Tags | no tags |
| Gates > Index [0] > When Closed | Skip This Action |

*Add the tag your game grants when the skill is learned to Required Tags. In Skip This Action a press passes over the locked move to the next transition on the same input.*

Opens while the character carries every **Required Tag** and none of the **Blocked Tags**, which win; your game grants the tag as the skill is learned. **When Closed**: **Skip This Action** (default) passes the press to the next transition on the same input, so a locked finisher falls back to the plain heavy; **Refuse The Press** refuses it. For the skip, give the gated move's transition the higher **Priority**.

Graph nodes:

- **Light 2** (montage AM_Attack_Light)
- **Heavy Finisher** (montage AM_Heavy_Finisher)
- **Heavy** (montage AM_Heavy)

Connections:

- Light 2 to Heavy Finisher, through UPC.Input.HeavyAttack (pressed)
- Light 2 to Heavy, through UPC.Input.HeavyAttack (pressed)

*Two heavy attack transitions out of Light 2. The one into Heavy Finisher has Priority 1, and Heavy Finisher carries a Tags gate on Skip This Action: until the character carries the skill's tag, the press takes the plain Heavy.*

### Vital: a level without spending it

**Details panel: Desperation Strike** (instanced object, in AG_MyFirstGraph)

**Category: Action Node Data**

| Property | Value |
| --- | --- |
| Gates | 1 Array element |
| Gates > Index [0] | Vital |
| Gates > Index [0] > Vital Tag | UPC.Vital.Health |
| Gates > Index [0] > Comparison | Less Or Equal (<=) |
| Gates > Index [0] > Threshold | 0.25 |
| Gates > Index [0] > Use Fraction | true |

*Opens only while health sits at or below a quarter of its max. It spends nothing.*

**Comparison** checks **Vital Tag** against **Threshold**: below, at or above, or within **Tolerance**. **Use Fraction** reads the threshold as a 0 to 1 share of the max. With no Vitals row for the vital, it stays shut.

---

## Costs: what a move spends

A node runs only when every cost in its **Costs** list can be paid, and pays as it starts; a refused move pays nothing.

| Cost | Spends |
| --- | --- |
| **Vital** | A vital on the character's Vitals component. |
| **Attribute** | An attribute your project already defines in a Gameplay Ability System attribute set, in place of a Vitals row. Pick it in **Attribute** and set **Amount**; the character's ability system must carry that set. |

**Details panel: Spell** (instanced object, in AG_MyFirstGraph)

**Category: Action Node Data**

| Property | Value |
| --- | --- |
| Costs | 1 Array element |
| Costs > Index [0] | Attribute |
| Costs > Index [0] > Attribute | MyAttributeSet.Mana |
| Costs > Index [0] > Amount | 20 |

*MyAttributeSet stands for your own attribute set class.*

A positive **Amount** must be affordable and is spent; 0 costs nothing; a negative one always passes and adds to the pool, for a move that builds a meter. Two costs on one vital each check alone and can overspend together, so author one cost with the total (the compile warns). To drain a vital while a move lasts, use the [Drain Vital](https://rigbak.com/docs/universal-player-combat/action-graph#add-logic-to-a-node-hooks) hook.

---

## Try another move instead

With the quick start's Dodge out of charges, a dash press plays what you wire:

| Wired | The press plays |
| --- | --- |
| Nothing more | Nothing: it drops ([Action Graph](https://rigbak.com/docs/universal-player-combat/action-graph#when-a-move-ends-or-fails)). |
| An **Action Failed** transition out of Dodge | Its target, such as a stagger, after Dodge is tried and refused. |
| **Cooldown Ready** on the dash transition into Dodge, and a second dash transition into Hop Back | Hop Back: the condition skips Dodge, so nothing is refused. |

Graph nodes:

- **ROOT**
- **Dodge** (montage AM_UPC_DodgeBack)
- **Hop Back** (montage AM_UPC_Hop_B)

Connections:

- ROOT to Dodge, through UPC.Input.Dash (pressed; Cooldown Ready)
- ROOT to Hop Back, through UPC.Input.Dash (pressed)

*The quick start's Dodge with a fallback: Cooldown Ready on the dash transition into Dodge, and a second dash transition out of Root into Hop Back.*

Other refusals fall through the same way:

| To fall through on | Put on the transition |
| --- | --- |
| A cooldown with no charge left | **Cooldown Ready**, naming the gate's Cooldown Tag; it reads the gate's own count. |
| Too little stamina, or any vital | **Vital Threshold**, with the cost's vital and amount. |
| A skill not yet learned | Nothing: a Tags gate in **Skip This Action** does it. |

**Details panel: Transition Router** (instanced object, in AG_MyFirstGraph)

**Category: Entries [0]**

| Property | Value |
| --- | --- |
| Fires On | Input Pressed |
| Input Tag | UPC.Input.Dash |
| Conditions | 1 Array element |
| Conditions > Index [0] | Cooldown Ready |
| Conditions > Index [0] > Cooldown Tag | UPC.Cooldown.Ability.Dodge |

*Name the same tag the Dodge node's Cooldown gate uses.*

---

## Show a cooldown on the HUD

**Get Cooldown State** takes a character and a cooldown tag, a gate's or another ability's (one charge), and returns:

| Field | Reads |
| --- | --- |
| **Valid** | True where this machine sees the timers, as in single player; the Multiplayer drawer covers clients. |
| **Configured** | True when a move in the current graph uses the tag. False means another ability keeps the cooldown, and Max Charges reads 1. |
| **Max Charges**, **Charges Ready** | Charges banked, and those ready now. |
| **Seconds Until Next Charge** | 0 with no timer; -1 while every spent charge waits for the server's timer. |
| **Next Charge Duration** | That timer's length, for a progress bar; 0 and -1 as above. |

---

## Debug it

With the graph open in Play In Editor, each gate on a card shows a live row: a Cooldown reads `2/2`, `1/2, 0.8 s`, or `1/2, waiting for server` on a player's machine; a Tags gate reads `needs` or `blocked by` and the tag; a Vital gate reads the value against the level. `Log LogUPCActionGraph Verbose` names each refusal. `AbilitySystem.IgnoreCooldowns 1` opens every Cooldown gate, while Get Cooldown State still reads the spent charges.

<details>
<summary>A gate or a cost of your own (Advanced)</summary>

- On the server, **Remove Active Effects with Granted Tags** with a gate's Cooldown Tag resets its charges.
- A Cooldown gate's **Effect Class**, under its advanced arrow, is the gameplay effect each use applies as its timer (**UPCNodeCooldownEffect** by default). Point it at a subclass with a custom duration calculation to shorten cooldowns from a stat.
- A Blueprint child of `UPCActionGate`: **Can Enter** answers whether the node may start, on the player's machine and the server, so it reads state the player's machine sees. **Enter** runs as it starts, after every gate said yes, and changes state only through **Apply Entry Effect**, which predicts an effect and takes it back on a refusal; the ability system's own apply nodes cannot predict there. **Get Status** returns the card's live row: open or shut, a label such as `1/2, 3.2 s`, a 0 to 1 progress; by default it reports Can Enter.
- A Blueprint child of `UPCActionCost`: **Can Afford** answers whether the character can pay, **Commit** pays.
- Characters running the graph share one gate or cost object, so it stores nothing about one character. A magnifier beside a Blueprint gate or cost selects it in the Content Browser.

</details>

<details>
<summary>What runs where (Multiplayer)</summary>

The player's machine and the server both check gates and costs; the server's answer counts, and it alone pays.

- **Cooldowns:** a use takes its charge on the player's machine at once and holds it, with no countdown, until the server's timer replaces it. The charge returns about half a round trip after the server's timer ends. The player's ability system needs **Mixed** or **Full** replication to see the timers; under **Minimal** the server warns once and every charge reads ready.
- **Costs and vitals:** the player's machine reads its own view of the vital, up to a round trip behind the server's, so its bar moves when the spend arrives; a move refused for its cost rolls back like any refusal.
- **Cooldown Ready** passes where the timers cannot be seen, leaving the press to the server's gate.
- Under packet loss, a quick chain into a cooldown move can free a charge for a moment, ending at worst in a refusal.

</details>

<details>
<summary>C++</summary>

- Gates subclass `UUPCActionGate` and override `CanEnter_Implementation`, `Enter_Implementation` and `GetStatus_Implementation`; C++ alone overrides `AppendEdgeTags` (tags added to every transition into the node at compile, as Skip This Action does), `GetRefusalTag` and `ValidateInGraph`. The context is `FUPCActionGateContext` (owner, ability system, graph, node index), the status `FUPCActionGateStatus`.
- Costs subclass `UUPCActionCost` and override `CanAfford_Implementation` and `Commit_Implementation` on an `FUPCActionCostContext`.
- `UUPCActionGate_Cooldown::GetCooldownState` and `CountActiveUses` read a cooldown. The timer is a `UUPCNodeCooldownEffect`, its seconds on `UPC.SetByCaller.Cooldown` and the gate's tag in its granted tags. A replacement Effect Class needs a duration, no period and no stacking, or the default runs with a warning.
- A graph built in code calls `UUPCActionGraphAsset::ApplyGateEdgeTags()` once its gates and transitions are in place.

</details>

<details>
<summary>Troubleshooting (Advanced)</summary>

**Compile warnings and refusals**

- Compiling or saving warns about a Cooldown gate with no tag or no seconds, two Cooldown gates for one tag on one node (each use spends from both), moves sharing a cooldown tag with different Seconds or Charges, a Tags gate naming no tags, a Vital gate naming no vital, two costs on one pool, and any gate on Root.
- The compile compares Seconds and Charges within one graph and its sub graphs only; keep two weapons' graphs in step yourself.
- A refusing Cooldown gate reports the Gameplay Abilities **Activate Fail Cooldown Tag**, as an ability's cooldown does. **On Action Failed**, on the Action Graph component, fires with the node the press came from, the node that refused and the input.
- A positive cost cannot be paid without its Vitals row, or for an attribute, an ability system carrying the picked attribute's set. One affordable when checked but not when paid lets the move run and pays nothing.

</details>

---

Previous page: [Action Graph Editor](https://rigbak.com/docs/universal-player-combat/action-graph-editor). Next page: [Input](https://rigbak.com/docs/universal-player-combat/input)
