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;
}