> For the complete documentation index, see [llms.txt](https://dante-3.gitbook.io/dante-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://dante-3.gitbook.io/dante-docs/modular-input-system/core-concepts/buffered-combos.md).

# Buffered Combos

This guide builds a three-hit combo. Pressing **Attack** starts the first swing; pressing it again near the end of a swing queues the next one, which starts exactly when the combo window closes. Presses made a moment too early still count.

It uses a **buffer window** opened by the **Input Buffer Window** anim notify state, a **Call Input Receiver** processor and the mode's **Buffer Unhandled Inputs** option.

## The Timeline

```
time ─────────────────────────────────────────────────────────────────────►
       Attack 1 montage section
       [ wind-up ........ swing ........ recovery ................. ]
                                   [==== Window.Combo ====]
                              ▲         ▲        ▲              ▲
   press at 0.00 ─ allowed    │         │        │              │
   starts Attack 1            │         │        │              │
                  0.40 early press      0.55     0.70           0.90 window closes:
                  (unhandled, held)     captured captured        newest press (0.70) is
                                                                 replayed ─► Attack 2
```

* The press at **0.40** arrives while the pawn is attacking but before the window. The receiver ignores it, the mode holds it (**Buffer Unhandled Inputs**), and the window's **Lead Time** lets it count.
* Presses at **0.55** and **0.70** are **captured** by the window instead of being dispatched.
* When the window closes, **Resolution: Newest** picks one, the rest are discarded, and the winner is **replayed** through the pipeline. The receiver sees a replayed press and starts the next section.

## Build It

{% stepper %}
{% step %}

## Tags

Add `Input.Attack` for the input and `Window.Combo` for the window in **Project Settings > Gameplay Tags**.
{% endstep %}

{% step %}

## Config

In your Input Config:

* **Input Actions:** a row `Input.Attack` → `IA_Attack`, **Trigger Events:** *Started*.
* **Buffer Windows:** a window with

| Field                      | Value                                    |
| -------------------------- | ---------------------------------------- |
| Window Tag                 | `Window.Combo`                           |
| Accepted Inputs            | `Input.Attack`, Trigger Events *Started* |
| Lead Time                  | `0.15`                                   |
| Capture Accepted Inputs    | On                                       |
| Resolution                 | Newest                                   |
| Replay On Close            | On                                       |
| Discard Remaining On Close | On                                       |
| {% endstep %}              |                                          |

{% step %}

## Mode

In your on-foot Input Mode:

* Add a **Call Input Receiver** processor with **Input Tags** `Input.Attack` and **Trigger Events** *Started*.
* Tick **Buffer Unhandled Inputs**, so presses the pawn ignores mid-swing are held for the window's lead time.
  {% endstep %}

{% step %}

## Receiver

Add the **MIPS\_InputReceiver** interface to the character and implement **Handle Input Event**:

```
Event Handle Input Event (Event Tag, Context)
  ├─ Event Tag != Input.Attack ──► Return false
  ├─ Is Attacking == false
  │     └─ Play Montage (AM_Combo, section "Attack1"), ComboIndex = 1 ──► Return true
  ├─ Context.bReplayedFromBuffer == true            (a press the combo window picked)
  │     └─ ComboIndex < 3 ──► Montage Jump To Section ("Attack" + (ComboIndex + 1)),
  │                           ComboIndex++ ──► Return true
  └─ Return false                                   (mid-swing press: held by the mode)
```

Returning **false** for the early press matters: an unhandled event is what the mode holds in the buffer.
{% endstep %}

{% step %}

## Anim notify state

Open the combo montage. On each attack section, add an **Input Buffer Window** notify state over the part of the swing where the next attack may be queued, and set its **Window Tag** to `Window.Combo`. Leave **Resolve On End** on.

The end of the notify is where the next attack starts, so place it at the swing's cancel point.
{% endstep %}

{% step %}

## Interruptions

When a swing is interrupted (a hit reaction, a stun), cancel the window so nothing replays:

```
On Hit Reaction
  └─ Close Buffer Window (Actor = Self, Window Tag = Window.Combo, Cancel = true)
```

If a montage is interrupted while its notify is active, the notify's end also runs; with **Resolve On End** off it would cancel instead of resolve. Cancelling first from your hit reaction makes the outcome explicit.
{% endstep %}
{% endstepper %}

## Checking It

Play and open **Window > Developer Tools > Modular Input Monitor**:

* The window appears as an orange band on the timeline, grey when cancelled.
* Captured presses are amber markers with the reason `ModularInput.Reason.BufferWindow`.
* The replayed press appears again when the window closes, marked as a replay, with **Handled By** showing the receiver processor.

## Variations

| Want                                                 | Change                                                                                                            |
| ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| The first press in the window wins, not the last     | **Resolution:** Oldest                                                                                            |
| Heavy attack beats light attack if both were pressed | Accept `Input.Attack.Heavy` with **Priority** 1 and `Input.Attack.Light` with 0, **Resolution:** Highest Priority |
| React without a replay                               | Turn **Replay On Close** off and bind **On Buffer Window Closed**; read `bResolved` and `ResolvedEvent` there     |
| Open the window from input rather than animation     | Use the **Open Buffer Window** processor with a **Duration**                                                      |
| Allow queuing only while a certain state holds       | Add a **Tag Query** gate on `Input.Attack` in the mode                                                            |

{% hint style="info" %}
Keep the component's **Max Buffer Age** longer than your longest window plus its lead time. Events older than Max Buffer Age are dropped before a window can pick them.
{% endhint %}

## Next Steps
