Docs/Gameplay Moveset System/Modes & Runtime Swap
// docs/gameplay-moveset-system/modes.md
READ

Modes & Runtime Swap

Multi-grip weapons in one Definition + runtime moveset swap with combo continuity

A MovesetDefinition is one asset per weapon TYPE — Sword, Dagger, Spear, Unarmed — and every grip / stance of that weapon lives inside as separate Modes. This page covers when to use Modes, how runtime swaps preserve combo state, and how shared ComboGraph assets enable cross-weapon combo continuity.

Single-mode weapon

The simplest case — a weapon that's always held one way. A one-handed dagger, an unarmed combat moveset, a fixed-stance gun.

In the Definition:

  • Fill DefaultMode with { ComboGraph, AnimationChooser, Hitbox, Config }.
  • Leave AlternateModes empty.

At runtime:

  • Component's ActiveSelection.Mode stays empty.
  • Runtime always resolves to DefaultMode.

Multi-mode weapon

A sword that works one-handed, two-handed, and as a paired dual-wield. One Definition covers all three grips:

  • DefaultMode is the "primary" / fallback variant (typically one-handed).
  • AlternateModes maps the other Mode tags to their own FMovesetModeData:
    • Moveset.Mode.TwoHanded → two-handed grip's combo graph + chooser + hitbox.
    • Moveset.Mode.DualWield → paired-stance variant.

Equipment system picks the active Mode based on what's equipped (one weapon → DefaultMode, two of the same → DualWield, two-handed grip key held → TwoHanded), then calls:

MovesetComponent->SetActiveMode(FGameplayTag::RequestGameplayTag("Moveset.Mode.TwoHanded"));

Native Mode tags shipped with the plugin: Moveset.Mode.OneHanded, Moveset.Mode.TwoHanded, Moveset.Mode.DualWield. You're free to add your own (Moveset.Mode.Reverse, Moveset.Mode.Shielded, etc.) — they're just gameplay tags.

If the active Mode tag isn't in AlternateModes, the runtime silently falls back to DefaultMode. This is intentional — an unfamiliar Mode shouldn't crash the moveset.

Cross-weapon combo continuity

If two Definitions share the same UComboGraphAsset in their relevant Mode's data, the component's swap API preserves the combo cursor across the swap. This is how "LMB → LMB → RMB" carries as a single chain when the player swaps weapons mid-combo.

MovesetDefinition: MD_Sword
  DefaultMode = { CG_MeleeCombo, CT_Sword, Hitbox_Sword, Config }

MovesetDefinition: MD_Axe
  DefaultMode = { CG_MeleeCombo, CT_Axe,   Hitbox_Axe,   Config }
                  ^^^^^^^^^^^^^
                  Same graph asset

Equip sword → SetActiveMoveset(MD_Sword) → cursor is at Root. Press LMB → cursor advances to Attack.A on the sword's chooser. Mid-attack, equip axe → SetActiveMoveset(MD_Axe) → cursor preserved (graph matches) → next press advances to Attack.B on the axe's chooser. Same combo timing, different weapon visuals.

Runtime swap API

After Apply Moveset initialised the component, change the active selection mid-game with:

Method Behaviour
SetActiveMoveset(NewDefinition, bPreserveComboState) Swap Definition. Mode preserved.
SetActiveMode(NewMode, bPreserveComboState) Swap Mode tag, same Definition. Useful for grip flip on the same weapon.
SetActiveSelection({Definition, Mode}, bPreserveComboState) Atomic swap of both — single replicated transition.
RequestSelectionSwap(NewSelection, InitialInput) Defers swap until the current attack's Combo Window resolves, then applies. Keeps mid-attack swaps from cancelling the playing animation.

bPreserveComboState=true keeps the combo cursor + Enhanced Input bindings + ComboInstance only when the resolved ComboGraph asset is the same in old and new selection. If the graph differs, combo state is reset regardless of the flag (a different graph means different chains; the old cursor would refer to nodes that don't exist).

When to use which

  • SetActiveMoveset — equipment system swaps one weapon for another. Both weapons live in the same Mode tag.
  • SetActiveMode — the weapon stays the same but the player flipped grips (Y-button to two-hand a greatsword). Combo state usually resets (different graph), Enhanced Input bindings re-setup automatically.
  • SetActiveSelection — both Definition and Mode change at once and you need an atomic, single-replicated transition (e.g. weapon stowed → fists → fists Mode in one operation).
  • RequestSelectionSwap — same as the above but you don't want the swap to cancel the in-flight attack. The runtime queues the swap until Combo Window resolves, then applies.

Authoring patterns by genre

Souls-like — different weapons share combo structure

Combo Graph: CG_MeleeCombo (shared across weapons)
  Root → [Light] → Attack1 → [Light] → Attack2 → [Light] → Attack3
                          → [Heavy] → HeavyFinisher

Chooser Table: CT_Sword
  Moveset.Attack.Light1 → AM_Sword_Slash1
  Moveset.Attack.Light2 → AM_Sword_Slash2
  …

Chooser Table: CT_Axe
  Moveset.Attack.Light1 → AM_Axe_Chop1
  Moveset.Attack.Light2 → AM_Axe_Chop2
  …

MovesetDefinition: MD_Sword.DefaultMode = { CG_MeleeCombo, CT_Sword, Hitbox_Sword, Config }
MovesetDefinition: MD_Axe.DefaultMode   = { CG_MeleeCombo, CT_Axe,   Hitbox_Axe,   Config }

Equip sword → SetActiveMoveset(MD_Sword). Equip axe → SetActiveMoveset(MD_Axe). Same combo graph, different montages, different hitbox geometry, combo cursor survives the swap.

Greatsword grip flip

MovesetDefinition: MD_Greatsword
  DefaultMode    = { CG_OneHandedCombo, CT_Greatsword_OneHanded, Hitbox_OneHanded, Config }
  AlternateModes:
    Moveset.Mode.TwoHanded = { CG_TwoHandedCombo, CT_Greatsword_TwoHanded, Hitbox_TwoHanded, Config }

Player presses Y → SetActiveMode(Moveset.Mode.TwoHanded). Same Definition, different combo graph, combo state resets (different graph asset) but Enhanced Input bindings re-setup automatically.

Dual-wield with paired stance

MovesetDefinition: MD_DualDaggers
  DefaultMode    = { CG_SingleDaggerCombo, CT_LeftDagger,  Hitbox_Single,  Config }
  AlternateModes:
    Moveset.Mode.DualWield = { CG_DualCombo, CT_BothDaggers, Hitbox_Both, Config }

Player picks up second dagger → SetActiveMode(Moveset.Mode.DualWield) → graph swaps to dual-wield variant with paired-attack chains.

What gets reset on swap

Swap kind Cursor Bindings Active hitboxes In-flight montage
bPreserveComboState=true, same graph preserved preserved preserved preserved
bPreserveComboState=true, graph changed reset re-setup cancelled cancelled
bPreserveComboState=false reset re-setup cancelled cancelled
RequestSelectionSwap during Combo Window deferred deferred preserved preserved (until window closes)

When the in-flight montage is cancelled, the new Mode's first eligible transition fires immediately if InitialInput was passed to Apply Moveset / RequestSelectionSwap.