Move attack build/repeat state, combat-mode policy, authoritative auto-target transitions, and spell-cast intent beneath RuntimeActionState. Keep App as the input, world-query, DAT-policy, transport, and presentation adapter while preserving retail request and busy ordering. Add direct/graphical parity, reset, failure, and instance-isolation coverage. Co-authored-by: Codex <noreply@openai.com>
127 lines
4.3 KiB
C#
127 lines
4.3 KiB
C#
using AcDream.App.Combat;
|
|
using AcDream.App.Update;
|
|
using AcDream.Runtime.Gameplay;
|
|
using AcDream.UI.Abstractions.Input;
|
|
|
|
namespace AcDream.App.Input;
|
|
|
|
internal interface ICombatInputFrameController
|
|
{
|
|
void Tick();
|
|
void HandleMovementInput(InputAction action, ActivationType activation);
|
|
bool HandleInputAction(InputAction action, ActivationType activation);
|
|
}
|
|
|
|
internal sealed class CombatAttackInputFrameAdapter : ICombatInputFrameController
|
|
{
|
|
private readonly RuntimeCombatAttackState _owner;
|
|
|
|
public CombatAttackInputFrameAdapter(RuntimeCombatAttackState owner) =>
|
|
_owner = owner ?? throw new ArgumentNullException(nameof(owner));
|
|
|
|
public void Tick() => _owner.Tick();
|
|
|
|
public void HandleMovementInput(InputAction action, ActivationType activation)
|
|
{
|
|
if (activation != ActivationType.Press
|
|
|| action is not (
|
|
InputAction.MovementForward
|
|
or InputAction.MovementBackup
|
|
or InputAction.MovementRunLock
|
|
or InputAction.MovementJump))
|
|
{
|
|
return;
|
|
}
|
|
|
|
_owner.HandleCommand(new RuntimeCombatAttackInput(
|
|
RuntimeCombatAttackCommand.AbortForMovement,
|
|
RuntimeInputActivation.Press));
|
|
}
|
|
|
|
public bool HandleInputAction(InputAction action, ActivationType activation)
|
|
{
|
|
RuntimeCombatAttackCommand? command = action switch
|
|
{
|
|
InputAction.CombatLowAttack =>
|
|
RuntimeCombatAttackCommand.LowAttack,
|
|
InputAction.CombatMediumAttack =>
|
|
RuntimeCombatAttackCommand.MediumAttack,
|
|
InputAction.CombatHighAttack =>
|
|
RuntimeCombatAttackCommand.HighAttack,
|
|
InputAction.CombatDecreaseAttackPower =>
|
|
RuntimeCombatAttackCommand.DecreasePower,
|
|
InputAction.CombatIncreaseAttackPower =>
|
|
RuntimeCombatAttackCommand.IncreasePower,
|
|
_ => null,
|
|
};
|
|
if (command is null)
|
|
return false;
|
|
|
|
return _owner.HandleCommand(new RuntimeCombatAttackInput(
|
|
command.Value,
|
|
activation == ActivationType.Press
|
|
? RuntimeInputActivation.Press
|
|
: RuntimeInputActivation.Release));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Owns the complete pre-object input frame: held semantic dispatch, one raw
|
|
/// mouse-look sample, and combat attack intent.
|
|
/// </summary>
|
|
internal sealed class GameplayInputFrameController
|
|
: IGameplayInputFramePhase,
|
|
AcDream.App.Streaming.ILocalPlayerTeleportInputLifetime
|
|
{
|
|
private readonly InputDispatcher? _dispatcher;
|
|
private readonly DispatcherMovementInputSource _movement;
|
|
private readonly IMouseLookInputFrameController? _mouseLook;
|
|
private readonly ICombatInputFrameController _combat;
|
|
|
|
public GameplayInputFrameController(
|
|
InputDispatcher? dispatcher,
|
|
DispatcherMovementInputSource movement,
|
|
IMouseLookInputFrameController? mouseLook,
|
|
ICombatInputFrameController combat)
|
|
{
|
|
_dispatcher = dispatcher;
|
|
_movement = movement ?? throw new ArgumentNullException(nameof(movement));
|
|
_mouseLook = mouseLook;
|
|
_combat = combat ?? throw new ArgumentNullException(nameof(combat));
|
|
}
|
|
|
|
public bool MouseLookActive => _mouseLook?.Active == true;
|
|
|
|
public void Tick(UpdateFrameTiming timing)
|
|
{
|
|
_ = timing;
|
|
_dispatcher?.Tick();
|
|
_mouseLook?.Tick();
|
|
_combat.Tick();
|
|
}
|
|
|
|
public bool HandlePointerAction(InputAction action, ActivationType activation) =>
|
|
_mouseLook?.HandlePointerAction(action, activation) == true;
|
|
|
|
public bool HandleCombatAction(InputAction action, ActivationType activation)
|
|
{
|
|
// ACCmdInterp::HandleNewForwardMovement @ 0x0058B1F0 aborts
|
|
// automatic combat before the movement command enters the interpreter.
|
|
_combat.HandleMovementInput(action, activation);
|
|
return _combat.HandleInputAction(action, activation);
|
|
}
|
|
|
|
public bool HandlePressedMovementAction(InputAction action) =>
|
|
_movement.HandlePressedAction(action);
|
|
|
|
public void QueueRawMouseDelta(float dx, float dy) =>
|
|
_mouseLook?.QueueRawDelta(dx, dy);
|
|
|
|
public void EndMouseLook() => _mouseLook?.EndForLifecycle();
|
|
|
|
public void ResetSession()
|
|
{
|
|
_mouseLook?.ResetSession();
|
|
_movement.ResetSession();
|
|
}
|
|
}
|