using AcDream.UI.Abstractions.Input; namespace AcDream.App.Input; internal interface IMovementInputSource { MovementInput Capture(); } /// /// Owns held movement sampling and the retail autorun latch. The input /// dispatcher remains the only keyboard/mouse-button state source. /// internal sealed class DispatcherMovementInputSource : IMovementInputSource { private readonly IInputCaptureSource? _capture; private InputDispatcher? _dispatcher; public DispatcherMovementInputSource(IInputCaptureSource? capture = null) => _capture = capture; public bool AutoRunActive { get; private set; } public void Bind(InputDispatcher dispatcher) { ArgumentNullException.ThrowIfNull(dispatcher); if (_dispatcher is not null && !ReferenceEquals(_dispatcher, dispatcher)) throw new InvalidOperationException( "The movement input source is already bound to another dispatcher."); _dispatcher = dispatcher; } public MovementInput Capture() { // Devtools owns the whole gameplay keyboard while active, including // a latched autorun. Retained chat owns physical key state only; // retail's autorun latch continues until an explicit cancel action. if (_capture?.DevToolsWantCaptureKeyboard == true) return default; if (_dispatcher is not { } dispatcher) return default; bool walking = dispatcher.IsActionHeld(InputAction.MovementWalkMode); bool forward = dispatcher.IsActionHeld(InputAction.MovementForward); return new MovementInput( Forward: forward || AutoRunActive, Backward: dispatcher.IsActionHeld(InputAction.MovementBackup), StrafeLeft: dispatcher.IsActionHeld(InputAction.MovementStrafeLeft), StrafeRight: dispatcher.IsActionHeld(InputAction.MovementStrafeRight), TurnLeft: dispatcher.IsActionHeld(InputAction.MovementTurnLeft), TurnRight: dispatcher.IsActionHeld(InputAction.MovementTurnRight), Run: !walking, Jump: dispatcher.IsActionHeld(InputAction.MovementJump)); } /// /// Applies the press-only autorun policy at the same point in the semantic /// action pipeline as the former GameWindow body. /// /// True when the action is fully consumed. public bool HandlePressedAction(InputAction action) { if (action == InputAction.MovementRunLock) { AutoRunActive = !AutoRunActive; return true; } if (AutoRunActive && action is ( InputAction.MovementBackup or InputAction.MovementStop or InputAction.MovementStrafeLeft or InputAction.MovementStrafeRight)) { AutoRunActive = false; } return false; } public void ResetSession() => AutoRunActive = false; }