using System;
namespace AcDream.UI.Abstractions.Input;
///
/// Phase K.2 — state machine for MMB-hold "instant mouse-look" mode
/// (retail's CameraInstantMouseLook). While active, mouse-X
/// delta drives the character's heading AND the chase camera yaw
/// together (combined drive — the camera "instantly" follows the
/// character because mouse-X moves the character, and the chase
/// camera always tracks the character). Mouse-Y is left to the
/// caller — typically pitches the chase camera only.
///
///
/// The class owns three transitions:
///
/// - — MMB pressed AND ImGui isn't hovering a
/// panel; activate, capture initial cursor position for restore.
/// - — MMB released; deactivate, signal
/// cursor restore.
/// - — ImGui took mouse
/// focus while we were active (e.g. a panel pop-up); deactivate AS IF
/// the user released the button so the cursor is restored.
///
///
///
///
/// is the per-frame mouse-move hook: when
/// active, scales by sensitivity and feeds the
/// caller-supplied yaw mutator. The mutator is the only side-channel —
/// the class doesn't know about PlayerMovementController or
/// ChaseCamera.
///
///
public sealed class MouseLookState
{
private readonly Action _applyYawDelta;
/// True while MMB is held AND ImGui isn't capturing the
/// mouse. Mouse-X deltas drive yaw only when this is true.
public bool Active { get; private set; }
/// Cursor X at the moment activated.
/// Restore on release.
public float CapturedCursorX { get; private set; }
/// Cursor Y at the moment activated.
public float CapturedCursorY { get; private set; }
///
/// Per-radian yaw multiplier applied to mouse-X delta. The same
/// chase-camera sensitivity factor used elsewhere in GameWindow is
/// folded in by the caller; this class only owns the constant
/// scale that converts pixels to radians. Default 0.004 matches
/// the K.1b RMB-orbit factor.
///
public float SensitivityRadiansPerPixel { get; set; } = 0.004f;
public MouseLookState(Action applyYawDelta)
{
_applyYawDelta = applyYawDelta ?? throw new ArgumentNullException(nameof(applyYawDelta));
}
///
/// MMB press transition. Activates only when ImGui isn't capturing
/// the mouse — the dispatcher should already gate this, but the
/// guard adds defense in depth in case a binding fires through.
///
/// Current cursor X (captured for restore on release).
/// Current cursor Y.
/// Mirror of
/// ImGui.GetIO().WantCaptureMouse. When true, the press is
/// ignored so a hover-over-panel MMB doesn't grab the cursor.
public void Press(float cursorX, float cursorY, bool wantCaptureMouse)
{
if (wantCaptureMouse) return;
if (Active) return;
Active = true;
CapturedCursorX = cursorX;
CapturedCursorY = cursorY;
}
/// MMB release transition. Always deactivates if active.
public void Release()
{
if (!Active) return;
Active = false;
}
///
/// Reactive deactivation when ImGui takes mouse focus mid-hold.
/// E.g. a tooltip pops open over the cursor while MMB is held —
/// we yield the cursor to the panel instead of staying captured.
///
public void OnWantCaptureMouseChanged(bool wantCaptureMouse)
{
if (Active && wantCaptureMouse) Active = false;
}
///
/// Apply a per-frame mouse-X delta. When active, scales by
/// times the
/// caller-supplied (typically
/// the chase-camera sens) and feeds it to the yaw mutator. Sign
/// matches retail: dragging the mouse RIGHT yaws the character to
/// the right (positive yaw delta).
///
/// Pixels of horizontal mouse motion since the
/// last frame.
/// Multiplier (e.g. chase-camera
/// sensitivity) applied on top of the radians-per-pixel scale.
public void ApplyDelta(float dx, float extraSensitivity)
{
if (!Active) return;
// Sign: dragging the mouse RIGHT (dx > 0) should yaw the
// character to the right. With the acdream Yaw convention
// (where Yaw 0 = +X, increasing to +Y), positive yaw is
// counter-clockwise viewed top-down — so dragging right means
// yaw goes DOWN (more clockwise). The dispatcher convention
// for chase YawOffset is `YawOffset -= dx * factor`; we keep
// the same sign here so character + camera rotate identically.
_applyYawDelta(-dx * SensitivityRadiansPerPixel * extraSensitivity);
}
}