83 lines
2.9 KiB
C#
83 lines
2.9 KiB
C#
using AcDream.UI.Abstractions.Input;
|
|
|
|
namespace AcDream.App.Input;
|
|
|
|
internal interface IMovementInputSource
|
|
{
|
|
MovementInput Capture();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Owns held movement sampling and the retail autorun latch. The input
|
|
/// dispatcher remains the only keyboard/mouse-button state source.
|
|
/// </summary>
|
|
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));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Applies the press-only autorun policy at the same point in the semantic
|
|
/// action pipeline as the former GameWindow body.
|
|
/// </summary>
|
|
/// <returns>True when the action is fully consumed.</returns>
|
|
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;
|
|
}
|