# Hit Reactions

> Hit reactions make a struck character answer the blow: an event sent as the hit lands and an ability that answers it, with poise deciding which hit breaks through and hyper armor carrying a heavy swing on.

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

## Set it up (5 minutes)

1. **Create the reaction ability**

   A reaction is a gameplay ability that a hit effect starts by sending it an event. It stops the attack the character is making, holds its next action back, and moves the body only through the montage it plays. The bone flinch is [Physical Reactions](https://rigbak.com/docs/universal-player-combat/physical-reactions), a separate system that can play on the same hit. Create a Blueprint class whose parent is **Gameplay Ability**, named `GA_MyHitReact`, and set its **Class Defaults**. It is **Server Initiated** because the event goes out on the server:

   In **GA_MyHitReact**, under Class Defaults, set:

   | Property | Value |
   | --- | --- |
   | Net Execution Policy | Server Initiated (replaces Local Predicted) |
   | Activation Owned Tags | UPC.State.ActionsBlocked |
   | Ability Triggers | 1 Array element (replaces 0 Array elements) |
   | Ability Triggers > Trigger Tag | UPC.Event.Reaction (replaces None) |
   | Ability Triggers > Trigger Source | Gameplay Event |

   *Activation Owned Tags are held while the ability runs, so UPC.State.ActionsBlocked refuses every press until it ends. Leave Instancing Policy on Instanced Per Execution: each hit runs its own copy.*
2. **Cut the attack, wait, end**

   In its event graph, cut the attack the character is making with **Interrupt Action for External Ability** on its **UPC Action Graph**, which ends the running move whatever its cancel rules say and does nothing while idle. Then wait 0.4 seconds (a float variable `Hold Seconds`) and end.

   Blueprint graph, nodes in reading order:

   - **Comment box: GA_MyHitReact · Event Graph**

     - **Event ActivateAbility** (event)
     - **Get Avatar Actor from Actor Info** (pure function); outputs: Return Value
     - **Get Component by Class** (pure function); inputs: Target, Component Class = UPC Action Graph; outputs: Return Value
     - **Interrupt Action for External Ability** (function; Target is UPC Action Graph); inputs: Target
     - **Hold Seconds** (variable)
     - **Wait Delay** (function; Task); inputs: Time; outputs: On Finish
     - **End Ability** (function; Target is Gameplay Ability); inputs: Target = self

   For a flinch animation, use **Play Montage and Wait** in place of **Wait Delay**, and end the ability from its **On Completed**, **On Interrupted** and **On Cancelled** pins: the character starts nothing new until the flinch ends. An ability that ends first stops the montage (**Stop when Ability Ends**, on by default).
3. **Grant it to the enemy**

   Use an enemy of your own built on the UPC Character Base; the demo enemies already answer `UPC.Event.Reaction`. Give `BP_MyEnemy` a **Starting Ability Set** `AbilitySet_MyEnemy` (right-click in the Content Browser, **UPC**, **Actions**, **Ability Set**; rows in [Input](https://rigbak.com/docs/universal-player-combat/input#abilities-that-answer-a-key)) with a row of **Granted Gameplay Abilities** holding `GA_MyHitReact` and **Input Tag** empty, since an event starts it, not a key.
4. **Send the event when a hit lands**

   Open the Defense Rule Set the enemy's **UPC Defense** component names as its **Defense Set**, or make one (**UPC**, **Damage and Defense**, **Defense Rule Set**) and set it there ([Defense](https://rigbak.com/docs/universal-player-combat/defense#where-the-responses-come-from)). Add a **UPC Send Gameplay Event** to **Outcome Effects**: **Event Tag** `UPC.Event.Reaction`, only **Target** under **Affected Sides**, **Require Damage Applied** ticked, and `UPC.DamageResult.Blocked` and `UPC.DamageResult.Parried` in **Suppress on Result Tags**.

   **Details panel: DefenseRuleSet_MyEnemy** (data asset, in Content Browser)

   **Category: UPC > Defense**

   | Property | Value |
   | --- | --- |
   | Responses | 0 Array elements |
   | Outcome Effects | 1 Array element |
   | Outcome Effects > Index [0] | UPC Send Gameplay Event |
   | Outcome Effects > Index [0] > Event Tag | UPC.Event.Reaction |
   | Outcome Effects > Index [0] > Suppress on Result Tags | UPC.DamageResult.Blocked, UPC.DamageResult.Parried |
   | Outcome Effects > Index [0] > Require Any Result Tags | no tags |
   | Outcome Effects > Index [0] > Require Damage Applied | true |
   | Outcome Effects > Index [0] > Run on Damageable Hits | true |
   | Outcome Effects > Index [0] > Run on Inert Hits | false |
   | Outcome Effects > Index [0] > Run on Scenery Hits | false |
   | Outcome Effects > Index [0] > Affected Sides | Target |

   *A hit that drains health sends UPC.Event.Reaction to the struck character; a blocked or parried one sends nothing.*
5. **Press Play and hit the enemy mid-swing**

   Its attack stops, and for 0.4 seconds it starts nothing new; a blow it blocks or parries leaves it swinging. A second hit during the hold starts another reaction. To flinch only when poise breaks, as the demo does, add [Poise](https://rigbak.com/docs/universal-player-combat/hit-reactions#poise-flinch-only-when-it-breaks).

---

## Send the event

Author **UPC Send Gameplay Event** in one place per reaction, since each place runs:

| Authored on | Suits |
| --- | --- |
| The defender's **Outcome Effects** | A reaction of the character's, whatever struck it, such as the demo's flinch. Runs after the pools drain. |
| The attack's [damage config](https://rigbak.com/docs/universal-player-combat/damage-and-hit-effects#what-a-landed-hit-does) **Hit Effects** | A reaction of the blow's, such as a launcher. |
| **On Damage Taken** on the character | A reaction needing Blueprint logic. It runs on blocked and parried hits too, so check the Result. |

A new entry's **Affected Sides** starts on **Attacker**, so tick **Target** for the struck character. An ability triggered on a parent **Event Tag** also answers its children: one on `UPC.Event.Reaction` answers `UPC.Event.Reaction.Stagger`. The effect's outcome gates ([Damage and Hit Effects](https://rigbak.com/docs/universal-player-combat/damage-and-hit-effects)) pick which hits send it; a character with no ability system receives nothing.

---

## Poise: flinch only when it breaks

Poise is a small pool every hit drains: the character flinches on the hit that empties it and the pool refills, so light weapons need a few hits for a flinch and heavy ones one.

1. **The pool.** Add a **UPC Vitals** row with **Vital Tag** `UPC.Vital.Poise` and **Depleted Result Tag** `UPC.DamageResult.PoiseBroken`, which the emptying hit reports. **Regen Delay Seconds** is the pause; a **Regen Rate Per Second** equal to **Max Value** refills it in a second.
2. **The damage.** Add a `UPC.Vital.Poise` row to each attack's **Additional Vital Damage**: how hard it staggers.
3. **The flinch.** On the reaction's **UPC Send Gameplay Event**, put `UPC.DamageResult.PoiseBroken` in **Require Any Result Tags** and untick **Require Damage Applied**, so a break flinches even with no health damage.
4. **The refill.** Beside it, add a **UPC Refill Vital** with **Vital Tag** `UPC.Vital.Poise` and `UPC.DamageResult.PoiseBroken` in **Require Any Result Tags**. It reaches the struck character by default (**Affected Sides** on **Target**) and fills the pool on the break even when the flinch is refused.

With the demo enemy's 40 poise and a 25-poise sword hit: the first hit leaves 15; the second empties the pool, reports `UPC.DamageResult.PoiseBroken`, flinches the enemy and refills it to 40.

In **UPC Vitals**, under Vitals, Poise row, set:

| Property | Value |
| --- | --- |
| Vital Tag | UPC.Vital.Poise (replaces None) |
| Max Value | 40 (replaces 100) |
| Regen Rate Per Second | 40 (replaces 0) |
| Regen Delay Seconds | 4 (replaces 0) |
| Depleted Result Tag | UPC.DamageResult.PoiseBroken (replaces None) |

*The demo enemy's row.*

---

## Hyper armor

A swing that should carry through light hits holds `UPC.State.HyperArmor`, from a **UPC State Tag Window** over its frames ([Animation Integration](https://rigbak.com/docs/universal-player-combat/animation-integration#hold-a-tag-for-part-of-an-animation)). With poise, make it a poise bonus: add this response to the defender's rule set, below its guards.

In **DefenseRuleSet_MyEnemy**, under Responses > Index [ 0 ] (UPC Scale Damage), set:

| Property | Value |
| --- | --- |
| Required Defender Tags | UPC.State.HyperArmor |
| Damage Multiplier | 1.0 |
| Vital Damage Multipliers | UPC.Vital.Poise: 0.5 |
| Continue After Claim | true |

*Health passes at full value; poise takes half.*

Light hits drain half their poise and the swing goes on; a heavy hit, or a worn-down pool, still breaks it. A 40-poise enemy takes 12.5 from a 25-poise sword hit under hyper armor, so it breaks on the fourth hit instead of the second.

Without poise, list the tag in the reaction's **Activation Blocked Tags**: no hit stops the swing, though health drains and the physical reaction plays.

---

## Answer the attacker: a parry recoil

On the attack's damage config, or the parrying set's **Outcome Effects**, add a **UPC Send Gameplay Event** with **Affected Sides** on **Attacker**, `UPC.DamageResult.Parried` in **Require Any Result Tags**, and the tag a recoil ability triggers on. Make that ability **Server Initiated** too: a Local Predicted one never starts for a player who swung.

---

## Debug it

`ShowDebug AbilitySystem` lists a character's abilities and tags (is the reaction granted, is hyper armor or `UPC.State.ActionsBlocked` on). `LogUPCDamage` names an effect with no **Event Tag**.

<details>
<summary>What the event carries (Advanced)</summary>

- **Instigator** is the character that swung, **Target** the one struck.
- **Event Magnitude** is the health damage that landed, 0 for a hit draining stance alone.
- **Target Tags** are the defender's result tags, such as `UPC.DamageResult.Blocked`, for one ability to branch on.
- The hit rides along in the target data (**Get Hit Result from Target Data**, index 0) and the effect context (**GetHitResult**). The event carries no blow direction.
- An attack whose outcome tag sits in the [melee trace's](https://rigbak.com/docs/universal-player-combat/melee-trace#how-often-a-swing-lands) **Skip Record Tags** sends the event on each landing; list the reaction's own state tag in its Activation Blocked Tags to answer the first only.

</details>

<details>
<summary>Cutting from the ability grant instead (Alternative)</summary>

A row in a UPC ability set with **Interrupts Action Graph** ticked cuts the running move whenever its ability starts, so the reaction needs no Interrupt node. Its Activation Blocked Tags decide first.

</details>

<details>
<summary>The demo</summary>

`GA_UPC_HitReact` is the demo's reaction, granted by `AbilitySet_UPC_Enemy` and `AbilitySet_UPC_Default`. Every demo character but the two showcase hit dummies carries a Poise row and points at `DefenseRuleSet_UPC_Character`, holding the flinch event, the refill and the hyper armor response. `DefenseRuleSet_UPC_Sword` and `DefenseRuleSet_UPC_TwoHanded` send `UPC.Event.ParryRecoil` to a parried attacker, which `GA_UPC_ParryRecoil` answers.

| Hits to break, full pool / under hyper armor | Player 70 | Enemy 40 | Over Shoulder boss 100 | Soulslike boss 180 |
| --- | --- | --- | --- | --- |
| Punch, 20 | 4 / 7 | 2 / 4 | 5 / 10 | 9 / 18 |
| Sword, 25 | 3 / 6 | 2 / 4 | 4 / 8 | 8 / 15 |
| Two-handed light swing, 30 | 3 / 5 | 2 / 3 | 4 / 7 | 6 / 12 |
| Sword heavy, Counter, shield bash, 60 | 2 / 3 | 1 / 2 | 2 / 4 | 3 / 6 |
| Heavy punch, two-handed heavy, 120 | 1 / 2 | 1 / 1 | 1 / 2 | 2 / 3 |

**Timeline: AM_UPC_Unarmed_Combo, section 5** (1 s long). Tracks:

- **Notifies**: UPC State Tag Window, UPC.State.HyperArmor (0.013 s to 0.9 s)
- **Trace**: UPC Melee Trace Window (0.419 s to 0.9 s)

*The unarmed combo's sixth punch holds hyper armor through its windup and swing; the two-handed Heavy Attack holds it on its move.*

</details>

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

- The server decides the reaction: a **Server Initiated** ability starts there and on the struck player's machine about a round trip after the hit, so a press made in between is refused and rolled back.
- The break, the flinch and the refill are the server's, which reads hyper armor off its own copy of the montage.

</details>

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

- `UUPCSendGameplayEventHitEffect` (`UPCFoundation`) builds the event payload; `CanPlayAhead` returns false.
- `UUPCRefillVitalHitEffect` (`UPCFoundation`) calls the vitals component's `FillVital` on the authority, skipping a character with no row for the vital.
- A vital row's `DepletedResultTag` reaches the hit's `ResultTags` through `UUPCDefenseStatics::ApplyDamageResultToVitals` on the emptying hit, before the outcome effects run.
- `UUPCActionGraphComponent::InterruptActionForExternalAbility` cuts the running node from outside the graph.

</details>

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

| Symptom | Fix |
| --- | --- |
| Nothing happens on a hit | Grant the reaction, match its **Trigger Tag** to the **Event Tag**, and tick **Target** under Affected Sides. |
| The reaction plays but the swing goes on | Call **Interrupt Action for External Ability**, or grant it with **Interrupts Action Graph**. |

</details>

---

Previous page: [Defense](https://rigbak.com/docs/universal-player-combat/defense). Next page: [Physical Reactions](https://rigbak.com/docs/universal-player-combat/physical-reactions)
