fix #414: cursor disappears at character select after the in-world logoff

Session teardown (PlayerModeController.Exit/ResetSession ->
CameraController.ExitChaseMode) fell back to the dev free-fly camera, and
CameraPointerInputController.ApplyCursorForCameraMode faithfully applies
CursorMode.Raw (GLFW disabled cursor: hidden + captured) for fly mode —
so the character-select screen after an in-world logoff had no mouse.
Fresh boot starts in Orbit and never fires a mode change, which is why
only the post-logout path was affected.

Teardown now lands on Mode.Orbit — the exact state a fresh boot presents
at character select — and always notifies, so the pointer controller
restores CursorMode.Normal even when torn down from the dev fly camera.
The dev fly<->chase flow is untouched (it rides ToggleFly, never
ExitChaseMode).

Proven live both directions with a driven logout (UI probe 0x100000FA ->
dialog accept 0x17) under Win32 GetCursorInfo sampling: before, flags
flipped 1->0 exactly at the roster re-push that re-shows character select
and stayed hidden; after, zero hidden samples across the full timeline.
Files #415: the UI-probe 'wait world-visible' verb reads the reset
transit snapshot and is dead after reveal completion (test apparatus
only).

App tests 5564/3 skips (+3), Runtime 1756/0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-17 15:17:50 +02:00
parent 70f7f72d62
commit 7aa08045d8
3 changed files with 102 additions and 5 deletions

View file

@ -87,16 +87,24 @@ public sealed class CameraController
ModeChanged?.Invoke(IsChaseMode);
}
/// <summary>
/// Player-mode teardown (logout, session reset). Lands on the neutral
/// orbit camera — the same state a fresh boot presents at character
/// select — NOT the dev free-fly camera: fly mode raw-captures the OS
/// cursor (<c>CameraPointerInputController.ApplyCursorForCameraMode</c>
/// sets <c>CursorMode.Raw</c>), and the old <c>Mode.Fly</c> fallback
/// left the character-select screen after an in-world logoff with a
/// hidden, captured mouse. The dev fly↔chase flow is unaffected — it
/// rides <see cref="ToggleFly"/>, never this teardown path.
/// </summary>
public void ExitChaseMode()
{
bool wasChaseMode = IsChaseMode;
Chase = null;
RetailChase = null;
if (_mode == Mode.Orbit)
return;
_mode = Mode.Fly;
if (wasChaseMode)
ModeChanged?.Invoke(IsFlyMode);
_mode = Mode.Orbit;
ModeChanged?.Invoke(false);
}
public void SetAspect(float aspect)