Burnsies HideoutDocsBurnsie.Common

Keybinds — Keybind

Burnsie.Common.Input.Keybind represents a keyboard chord (main key + optional modifier) with an optional controller equivalent. It fixes the two things plugins habitually get wrong: modifier semantics and input suppression.

Quick start

// Keyboard-only chord — Ctrl+B (either Ctrl key):
var bind = new Keybind(Keys.B, Keys.Control);

// With a controller chord (hold RB, press A):
var bind2 = new Keybind(Keys.B, Keys.Control,
    ControllerButtons.A, ControllerButtons.RightShoulder);

// In your tick loop:
if (bind.IsJustPressed())
{
    // fired once per press; automatically suppressed when the console is open,
    // the game is paused, or the user is typing into an on-screen keyboard
}

// For hold-to-confirm mechanics:
if (bind.IsHeld()) { /* level semantics — true every tick while held */ }

// For help text / config docs:
string label = bind.FriendlyName();   // "Ctrl+B", or "Ctrl+B (pad: RightShoulder+A)"

Bind values usually come from config — ConfigSchema.Enum parses Keys and ControllerButtons by name (see Configuration):

var keyEntry = schema.Enum("Keybindings", "Key",      Keys.B,       "Activation key.");
var modEntry = schema.Enum("Keybindings", "Modifier", Keys.Control, "Hold with the key. None = no modifier.");
// after schema.Load():
var bind = new Keybind(keyEntry.Value, modEntry.Value);

Modifier semantics (the part v1 got wrong)

  • Composite valuesKeys.Control, Keys.Shift, Keys.Alt — match either physical key (left or right). This is what users mean when they write Control in an INI.
  • Explicit L/R keysKeys.LControlKey, Keys.RShiftKey, etc. — match exactly that key.
  • Keys.None as the modifier means no modifier required; Keys.None as the main key disables the keyboard chord entirely (controller-only binds).
  • Any other key can be a modifier too (e.g. a two-key chord like Keys.E + Keys.B).

Edge semantics: IsJustPressed() is edge-triggered on the main key (fires once per press) with level checks on the modifier. IsHeld() is level-triggered on both.

Input suppression

All queries return false while:

  • the RPH console is open,
  • the game is paused,
  • an on-screen keyboard (text input) is active — detected via SafeNatives.IsOnScreenKeyboardActive, so hotkeys don't fire while the user types.

The static check is public if you need it for your own input handling:

if (Keybind.IsInputSuppressed()) return;

The talk key

The LSPDFR ecosystem convention for "talk / interact" is Y, exposed as a constant and a suppression-aware helper for dialogue systems:

if (Keybind.TalkPressed())   // edge-fired Y, respecting suppression
{
    AdvanceDialogue();
}

Friendly names

FriendlyName() produces human-readable labels for menus, help text and notifications: digits render as 09 (not D0), OEM keys as their symbols (,, ., -, +), and any Control/Shift/Alt variant renders as Ctrl+/Shift+/Alt+. Show it wherever you tell the user what to press:

notifier.Show("Press " + Palette.Key(bind.FriendlyName()) + " to activate.");