Combo System

Chains discrete GAS abilities into branching, designer-authored sequences (light-light-heavy, charge attacks, directional finishers). Structured the OPPOSITE way from most UE combo solutions: each combo step is its own `UCrimsonGameplayAbility`, and a combo graph asset describes how steps chain. The combo system owns none of the animation, damage, costs, or augments - those live on each step's ability where GAS already handles them.

How it differs

AspectMost UE combo solutionsCrimsonAbilitySystem combo
What's a combo?One ability + big montage with notify transitionsA graph; each node IS a step, each step IS its own ability
Per-step granularityAll steps share one ability's costs/cooldowns/augmentsEach step inherits the FULL GAS surface - own cost, cancellable, augmentable independently
Input source of truthAnim notifies parsed at runtimeDesigner-authored phase boundaries in seconds, inline montage timeline
MultiplayerAuthor manuallyBuilt-in: server-authoritative CurrentNodeID, client-predicted next step, ping-leniency cancel check
BranchingHardcoded in the montageFirst-class - every transition is an edge; steps can branch into combos rooted on their own ability

Concepts

TermWhat it is
Combo Graph Asset (UCrimsonComboGraphAsset)The asset designers create - the graph of step nodes + transitions + a starting handshake
Step Node (FCrimsonComboRuntimeNode)One step: references a UCrimsonGameplayAbility + phase boundaries + outgoing transitions
Transition (FCrimsonComboTransition)An edge between steps: input + priority + target + conditional augment grants
Starting Handshake (FCrimsonComboStartingHandshake)How a rooting ability enters a combo asset
Combo Extension (FCrimsonComboExtension)UPROPERTY on UCrimsonGameplayAbility listing DefaultCombos + the transition window for chaining INTO a combo
Combo Controller (UCrimsonComboController)Runtime driver on the pawn - input buffer, grants, state machine, prediction RPCs
Transition Condition (UCrimsonComboTransitionCondition)Blueprintable observer that watches the prior step and decides whether the transition's augment grants apply
BDO-style input model
Combo inputs ARE combat inputs - the same UInputAction set drives normal activation AND combo transitions. Designers pick combo inputs from a closed catalog (the project IMC in UCrimsonAbilitySystemSettings) so they can't reference inputs that don't exist.

Input buffer TTL - override chain

The resolved TTL walks this chain, returning the first non-sentinel value:

#SourceSentinel that defers
1Step override - FCrimsonComboRuntimeNode::InputBufferTTLOverride-1
2Asset default - UCrimsonComboGraphAsset::InputBufferTTLSeconds0
3Rooting ability override - FCrimsonComboExtension::InputBufferTTLOverride-1
4Per-character - UCrimsonComboController::DefaultInputBufferTTL0
5Project setting - UCrimsonAbilitySystemSettings::DefaultInputBufferTTLn/a (0.1 default)

Grants - runtime API

Combos are granted to the controller at runtime, not hardcoded. ComboExtension.DefaultCombos is the seed; the controller's replicated FCrimsonComboGrantContainer is the authority:

cpp
Controller->UnlockComboGrant(RootAbilityClass, ComboAsset);
Controller->LockComboGrant(RootAbilityClass, ComboAsset);
Controller->GrantDynamicCombo(RootAbilityClass, ComboAsset); // returns a handle
Controller->RevokeComboGrant(Handle);
Controller->IsComboGranted(RootAbilityClass, ComboAsset);

Save support: mirror dynamic grants via Controller->GetSaveData() / ApplySaveData(Data). Default grants regenerate on every ASC init.

Conditional augments

Every transition carries (UCrimsonComboTransitionCondition, Augments[]) pairs. Conditions are stateful observers receiving lifecycle hooks during the prior step, then pure-returning true/false at fire time:

HookWhen it fires
OnObservationBeginStep ability activates; reset accumulators
OnEffectAppliedToTargetStep applies a GE to a target
OnObservedAttributeChangedA declared observed attribute changes
OnAugmentEventAn augment event broadcasts from the observed ability
OnObservationEndStep ends (cancelled or completed)
EvaluateConditionPure evaluation at fire time - returns whether the augments are granted

When a transition fires, augments behind any true condition are equipped onto the receiving step's ability via the slot system, and auto-unequipped when that step ends.

Ship base class only
The plugin ships only UCrimsonComboTransitionCondition (Blueprintable, EditInlineNew, Abstract); its default EvaluateCondition returns true. Real conditions are your project's to subclass.

Multiplayer - per-step prediction

RoleWhat runs
Owning clientBuffers input, predicts the next step locally, sends Server_RequestTransition. Rolls back on Client_RejectTransition.
ServerValidates (current node matches, transition exists, input matches, cancel window open with bounded ping leniency <= 80 ms). Commits CurrentNodeID. Activates the next step's ability.
Simulated proxiesNo controller logic. See montages via GAS replication; CurrentNodeID read only for cosmetic cues.
Per-step prediction, never chained
Each transition is its own RPC + prediction key. Chaining predicted activations server-side is the documented GAS prediction failure mode (causes desync). The per-step approach is the established workaround.