From 6481169cb9ab357207c2ce0aae8844d4c4e87246 Mon Sep 17 00:00:00 2001 From: Erik Date: Sun, 26 Apr 2026 14:30:28 +0200 Subject: [PATCH] =?UTF-8?q?fix(input):=20Phase=20K=20live-test=20fixes=20p?= =?UTF-8?q?t2=20=E2=80=94=20visible=20cursor=20in=20chase,=20free-fly=20di?= =?UTF-8?q?scoverable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two issues from the K-fix1 launch (2026-04-26 user report): 1. Mouse pointer invisible after login. Root cause: CameraController.EnterChaseMode invokes ModeChanged?.Invoke(IsChaseMode) — passing TRUE when chase becomes active. The OnCameraModeChanged handler interpreted that bool as `isFlyMode`, so chase entry wrongly triggered the Raw cursor branch (raw = invisible pointer). The bool is unreliable: ToggleFly passes IsFlyMode, ExitChaseMode passes IsFlyMode, but EnterChaseMode passes IsChaseMode. Read the controller state directly inside the handler instead — fly mode IS the only state that needs Raw, everything else stays Normal so the user can click panels / future selectables. 2. No way to enter free-fly mode. The DebugPanel already had a "Toggle Free-Fly Mode" button wired in K.2, but the user didn't know to look there. Added two more discovery paths: - Keyboard shortcut: Ctrl+Shift+F → AcdreamToggleFlyMode in RetailDefaults() (retail leaves Ctrl+Shift+F unbound; Ctrl+F is unused too, so this is conflict-free). - View → Camera submenu in the ImGui MainMenuBar with a "Enter / Exit Free-Fly Mode" entry whose label flips with the active state. Shortcut hint shows "Ctrl+Shift+F". The keyboard handler now also cancels _playerModeAutoEntry on manual fly toggle (matches the DebugPanel button + new menu entry — user's choice wins, the chase camera doesn't snap on top of the fly camera mid-inspection). Also corrected the View → Debug menu shortcut hint (was "F1", actual binding is Ctrl+F1 since K.1c). Tests still 1220 green. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/AcDream.App/Rendering/GameWindow.cs | 45 +++++++++++++++---- .../Input/KeyBindings.cs | 20 ++++++--- 2 files changed, 50 insertions(+), 15 deletions(-) diff --git a/src/AcDream.App/Rendering/GameWindow.cs b/src/AcDream.App/Rendering/GameWindow.cs index 56f8b74..77e5524 100644 --- a/src/AcDream.App/Rendering/GameWindow.cs +++ b/src/AcDream.App/Rendering/GameWindow.cs @@ -3987,19 +3987,23 @@ public sealed class GameWindow : IDisposable return new System.Numerics.Quaternion(0f, 0f, z, w); } - private void OnCameraModeChanged(bool isFlyMode) + private void OnCameraModeChanged(bool _modeBool) { if (_input is null) return; var mouse = _input.Mice.FirstOrDefault(); if (mouse is null) return; - // K-fix1 (2026-04-26): cursor visible by default in chase / orbit - // modes — the user needs to click panels, dropdowns, the future - // selection picker, etc. Mouse-look (raw mode) only happens - // transiently while MMB is held (HideCursorForMouseLook / - // RestoreCursorAfterMouseLook). Fly mode still needs raw because - // it's a continuous look-and-fly affordance. - bool needsRawCursor = isFlyMode; + // K-fix2 (2026-04-26): the bool passed to ModeChanged is NOT + // reliably "isFlyMode" — CameraController.EnterChaseMode invokes + // it with IsChaseMode (true), CameraController.ToggleFly invokes + // it with IsFlyMode, and CameraController.ExitChaseMode invokes + // it with IsFlyMode. Reading the controller state directly is + // the only correct gate. Cursor visible by default in chase / + // orbit modes; Raw cursor only in fly mode (continuous + // look-and-fly affordance). Mouse-look (raw mode) when MMB is + // held is handled separately by HideCursorForMouseLook / + // RestoreCursorAfterMouseLook. + bool needsRawCursor = _cameraController?.IsFlyMode == true; mouse.Cursor.CursorMode = needsRawCursor ? CursorMode.Raw : CursorMode.Normal; _capturedMouse = needsRawCursor ? mouse : null; } @@ -4330,10 +4334,27 @@ public sealed class GameWindow : IDisposable && ImGuiNET.ImGui.MenuItem("Chat")) _chatPanel.IsVisible = !_chatPanel.IsVisible; if (_debugPanel is not null - && ImGuiNET.ImGui.MenuItem("Debug", "F1")) + && ImGuiNET.ImGui.MenuItem("Debug", "Ctrl+F1")) _debugPanel.IsVisible = !_debugPanel.IsVisible; ImGuiNET.ImGui.EndMenu(); } + // K-fix2 (2026-04-26): Camera submenu — discoverable + // free-fly toggle for users who don't know the + // Ctrl+Shift+F shortcut or the Debug-panel button. + if (ImGuiNET.ImGui.BeginMenu("Camera")) + { + if (_cameraController is not null) + { + string flyLabel = _cameraController.IsFlyMode + ? "Exit Free-Fly Mode" : "Enter Free-Fly Mode"; + if (ImGuiNET.ImGui.MenuItem(flyLabel, "Ctrl+Shift+F")) + { + _playerModeAutoEntry?.Cancel(); + _cameraController.ToggleFly(); + } + } + ImGuiNET.ImGui.EndMenu(); + } ImGuiNET.ImGui.EndMainMenuBar(); } @@ -5327,6 +5348,12 @@ public sealed class GameWindow : IDisposable break; case AcDream.UI.Abstractions.Input.InputAction.AcdreamToggleFlyMode: + // K-fix2 (2026-04-26): manual fly toggle pre-empts the + // auto-entry trigger so the chase camera doesn't snap on + // top of the fly camera mid-inspection. Mirrors the + // DebugPanel "Toggle Free-Fly Mode" button + Camera menu + // entry. + _playerModeAutoEntry?.Cancel(); _cameraController?.ToggleFly(); break; diff --git a/src/AcDream.UI.Abstractions/Input/KeyBindings.cs b/src/AcDream.UI.Abstractions/Input/KeyBindings.cs index d2020b0..f5c3c4f 100644 --- a/src/AcDream.UI.Abstractions/Input/KeyBindings.cs +++ b/src/AcDream.UI.Abstractions/Input/KeyBindings.cs @@ -325,12 +325,11 @@ public sealed class KeyBindings b.Add(new(new KeyChord(Key.Down, ModifierMask.Ctrl), InputAction.ScrollDown)); // ── Acdream debug actions: relocated to Ctrl+F* to avoid retail - // conflicts. AcdreamToggleFlyMode + AcdreamTogglePlayerMode have - // NO keyboard binding in retail-default; K.2 adds a DebugPanel - // button for free-fly toggle and player-mode is auto-entered at - // login. AcdreamRmbOrbitHold is intentionally absent — retail - // binds RMB to SelectRight; the chase-camera orbit lives behind - // a debug-mode flag in K.2. + // conflicts. AcdreamTogglePlayerMode has NO keyboard binding + // in retail-default (player-mode is auto-entered at login). + // AcdreamRmbOrbitHold is intentionally absent — retail binds + // RMB to SelectRight; the chase-camera orbit lives behind a + // debug-mode flag in K.2. b.Add(new(new KeyChord(Key.F1, ModifierMask.Ctrl), InputAction.AcdreamToggleDebugPanel)); b.Add(new(new KeyChord(Key.F2, ModifierMask.Ctrl), InputAction.AcdreamToggleCollisionWires)); b.Add(new(new KeyChord(Key.F3, ModifierMask.Ctrl), InputAction.AcdreamDumpNearby)); @@ -339,6 +338,15 @@ public sealed class KeyBindings b.Add(new(new KeyChord(Key.F9, ModifierMask.Ctrl), InputAction.AcdreamSensitivityUp)); b.Add(new(new KeyChord(Key.F10, ModifierMask.Ctrl), InputAction.AcdreamCycleWeather)); + // K-fix2 (2026-04-26): free-fly toggle keyboard shortcut. + // Retail leaves Ctrl+Shift+F unbound (retail F = SelectionPickUp, + // Ctrl+F = unused) so this is non-conflicting. Also discoverable + // via View → Camera in the ImGui MainMenuBar and the + // "Toggle Free-Fly Mode" button in the Debug panel. + b.Add(new( + new KeyChord(Key.F, ModifierMask.Ctrl | ModifierMask.Shift), + InputAction.AcdreamToggleFlyMode)); + // K-fix1 (2026-04-26): RMB-hold camera orbit. Coexists with the // SelectRight Press binding above — Press fires on click, // AcdreamRmbOrbitHold fires on hold/release transitions so the