Input is shared with chat, menus, battle controls, camera modes, and accessibility features. A mod should react only in the contexts it owns and release all held state when that context changes.

Primary ownershipActive GUI application, focused view, and camera/input controller
Readiness anchorView focus plus battle or lobby context readiness
Version sensitivityVerify private names per client build

Input layers

Layer Responsibility
Raw key/mouse events Physical press, release, wheel and pointer movement
Action mappings User-configurable game actions and modifier handling
View focus Text fields, menus, modal dialogs and browser focus
Cursor ownership Visibility, confinement and capture
Camera controller Current mode, aim state and view transforms

Engine and Scaleform key numbers are different namespaces

The key conversion helper uses a specific Scaleform contract. Passing an engine key number directly to a UI control is not equivalent to converting it.

Engine keyNormal Scaleform valueConversion limit
KEY_NONE777Scaleform zero is not unbound.
KEY_MOUSE00Zero is a valid mouse-button value.
Left or right Shift, Control, Alt16, 17, 18The inverse selects the left-hand engine key.

The separate override tables use another sentinel/mouse convention; the normal conversion functions do not select it automatically. Preserve the original engine identity when left/right distinctions matter. For unsupported input, the forward helper also returns zero, colliding with valid mouse-button output; check table membership when unsupported and mapped values must be distinguished. An unsupported reverse input returns engine KEY_NONE, while valid Scaleform zero maps to KEY_MOUSE0.

These are version-sensitive Python conversion rules, not native command matching or measured keyboard-layout behavior. See keyboard option value contracts before sending a default or unbound value to a settings control.

Keyboard and mouse dispatch

The current client separates non-repeat key notifications, repeat-key handlers, mouse handlers, and lower-level game input paths. Verify the producer before choosing where to subscribe.

  • Input-handler key events are notification-style callbacks for non-repeat key down/up events; their return values are not the normal consumption mechanism.
  • Global key, mouse, and repeat-key handler sets can consume events with a truthy return value. Use that only when deliberately blocking later game handling.
  • Mouse events do not necessarily pass through the same input handler used for key events.
  • Handler exceptions are not isolated uniformly across all input layers. Keep handlers small, context-gated, and teardown-safe.

Held-key state

Key-up can be lost when focus, application, or arena changes. Clear held keys on every context exit and do not keep movement or drag state across a view rebuild.

_pressed = set()

def _on_key(event):
    if not _input_context_is_active():
        return
    key = _normalize_key(event)
    if event.isKeyDown():
        _pressed.add(key)
    else:
        _pressed.discard(key)

def _reset_input_state():
    _pressed.clear()
    _finish_drag_if_needed()

Text entry and chat

Do not trigger hotkeys while chat, search, settings fields, or another editable control has focus. Prefer action mappings when a user-configurable game action already exists.

Cursor and overlays

A draggable panel should capture input only over its visible bounds and only while interactive. Release capture on hide, destroy, application transition, and unexpected error.

Camera context

Arcade, sniper, strategic, spectator, replay, and mode-specific cameras expose different controllers. Read the active mode at use time and make unsupported modes a clean no-op.

Aiming and marker readiness

Aiming-related UI often depends on several owners becoming valid independently: control mode, active camera, controlled vehicle, entity appearance, collision data, marker provider, battle page, and UI component. Treat this as layered readiness instead of assuming that avatar entry means every marker-related object is ready.

  • Read the active control or camera mode at use time.
  • Make unsupported modes a no-op instead of forcing a fallback path.
  • Clear cached marker and camera references on vehicle change, death/spectator transition, replay seek, and arena teardown.
  • Prefer observing existing state over replacing camera or reticle ownership.
  • When rendering positional UI, keep the coordinate conversion rules in Spatial Coordinates & Projection separate from input handling.

Aiming data flow and invalidation

Control modes own their cameras and marker controllers; native marker providers publish through the aiming global bindings; CrosshairDataProxy translates those bindings into battle-controller events; the Scaleform crosshair plugin creates or invalidates concrete marker components. Read from the narrowest existing owner instead of replacing the whole chain.

  • Marker-set changes publish a structured marker-set object, and current marker-state handlers receive marker type, state, and support-marker information.
  • Preserve client, server, replay, and mechanic-specific marker separation. Unknown future marker components must survive sorting or overrides.
  • Keep mouse-event, gun-rotator, and data-provider hooks tiny; view creation and arena scans do not belong in hot input paths.

Vehicle-selection camera readiness

A roster entry, entity, in-world vehicle, appearance, collision object, and usable matrix are separate gates. Defer from the camera owner when one is missing and cancel the work when the mode, arena, player, or selected vehicle changes.

Common mistakes

  • Consuming a key event needed by normal game controls.
  • Triggering shortcuts while the user types in chat.
  • Leaving the cursor captured after an overlay closes.
  • Applying a camera transform from the previous camera mode.
  • Polling input every frame when an event or action mapping exists.

Verification trail

Trace the input producer, focus checks, action mapping, cursor owner, and active camera provider. Test focus loss, Alt-Tab, chat, modal dialogs, death/spectator transitions, and replay seeking.

Camera-mode input contracts

The shared mouse helper applies sensitivity and independent inversion signs but has no FOV, elapsed-time or screen-size input. Sniper then applies current-zoom scaling to angular sensitivity, while scroll sensitivity has another path; arcade can use a zoom-state override and negates pitch at its handoff. A settings slider alone does not define final rotation per input count across modes.

Rotation and zoom can also occur in a defined order within one update, and keyboard deltas can be retained for automatic updates rather than follow the immediate mouse path. Inspect the concrete input mode and producer instead of transplanting one mode's signs or scheduling.

Dynamic camera impulses are not health, energy or dispersion measurements. Native wrappers combine reason-specific settings, distance and different origins; a camera position can differ from the vehicle matrix origin. Disabling an oscillator does not establish every aiming/replay effect is cosmetic. These source/controlled-vector checks do not establish native device DPI, oscillators or server accuracy.

Battle messenger focus has page and target owners

The current Scaleform BattlePage request handler routes the messenger component through setBattleMessengerFocus. The base page remembers that object, asks the container to focus the page when necessary, then submits focus for the target. Its modal-focus fallback tests a null or different target against the remembered object, rather than deciding from visibility alone.

The method still delegates to AbstractView, whose last-focus handling and deferred uninitialized-component path can also affect the result. The base clears its remembered target on disposal; remembering it is not by itself an active-chat signal. Preserve page ownership, target identity and superclass/deferred handling when integrating with this route. This is the readable Scaleform contract, not a guarantee about final focus or Gameface DOM focus. ActionScript compilation, live input and window ordering were not tested.