Concept: The Chord System
A chord requires modifier keys (Shift / Ctrl / Alt) on an action. Enforcement lives in UInputTriggerCrimsonDynamicChord, an UInputTrigger that sits in an IMC mapping's Triggers array. Because it is per mapping, the same action can carry different chords in different IMCs, and 1 / Shift+1 / Ctrl+1 can be three distinct bindings on one key.
Exact-match semantics
The trigger passes only when the held modifiers exactly equal the requirement. A chord requiring Shift fails if Ctrl is also down; a chord requiring no modifiers fails while any modifier is held. This exactness is what prevents a bare 1 from firing underneath Shift+1.
UpdateState returns Triggered immediately for gamepad input - chords are a keyboard/mouse concern. A chorded keyboard mapping therefore never suppresses the same action on a controller.Reading modifier state
The trigger prefers the modifier UInputAction assets you configure in UCrimsonInputSettings (Shift/Ctrl/Alt) - reading them via GetActionValue respects your own key mappings. If none are configured it falls back to raw key state (LeftShift/RightShift, etc.). Configure the actions so a player who remaps their modifier keys still chords correctly.
Inline vs rebindable
| Inline | Rebindable | |
|---|---|---|
MappingName | empty (None) | set to a UCrimsonPlayerMappableKeySettings.Name |
| Source of the chord | DefaultChord on the trigger | player override in UCrimsonInputUserSettings, else DefaultChord |
| Player can change it | no | yes (persisted, SaveGame) |
| Who adds the trigger | you, on the IMC mapping | auto-injected by AddMappingContextWithChordTriggers (or you, for a default) |
Overrides always win over the inline DefaultChord for a rebindable mapping, so a designer default is just the starting value a player can change.
How injection works (originals stay untouched)
UCrimsonInputHelpers::CreateIMCWithChordTriggers returns a transient copy of your IMC. It walks the mappings; for any whose action uses UCrimsonPlayerMappableKeySettings and lacks a chord trigger, it adds one (with MappingName set and an empty DefaultChord). Your asset is never modified, and results are cached per source IMC. AddMappingContextWithChordTriggers is the convenience wrapper that patches then applies. This is why you must add the patched copy in place of the original - never both at once.
bRequiresShift / bRequiresCtrl / bRequiresAlt struct is defined in CrimsonCommon so both CrimsonInput and CrimsonSettings can share it with no cross-plugin dependency. It also formats its own label via GetDisplayName(FKey).