SL-5: retry/quit actions on the END-2 win/loss banner

The terminal banner gains PLAY AGAIN + QUIT TO MENU buttons so a finished run has a clear action (no Esc-hunting). PLAY AGAIN restarts a fresh Single run via WorldLauncher.StartSession (the proven menu lifecycle, no save load); QUIT TO MENU reuses TeardownToMenu (autosave + menu); both self-guard on WorldLauncher.Busy. The button row picks (Position) under the Ignore banner root, matching the build-palette idiom. AimReticleSystem (the sole Cursor.visible writer) keeps the cursor visible while RunOutcome != InProgress so the buttons are clickable regardless of aim state. 342/342 EditMode green. Co-op retry-together stays a cut slice-limit (a client's Play Again starts a solo run). See DR-036.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-15 13:20:22 -07:00
parent 04ad707e3b
commit e94b2948c7
2 changed files with 17 additions and 4 deletions
@@ -144,7 +144,10 @@ namespace ProjectM.Client
// Hide the OS cursor only while aiming AND focused; restore otherwise (focus loss / pre-spawn) so an
// unfocused editor or a windowed session is never stranded with an invisible pointer.
bool wantHidden = haveTarget && Application.isFocused && !AimPresentation.ForceCursorVisible;
// END-2: while the run is over (terminal banner up) keep the cursor visible so the player can click the
// Play Again / Quit buttons, regardless of aim state. AimReticleSystem is the sole Cursor.visible writer.
bool runOver = SystemAPI.TryGetSingleton<RunOutcome>(out var ro) && ro.Value != RunOutcomeId.InProgress;
bool wantHidden = haveTarget && Application.isFocused && !AimPresentation.ForceCursorVisible && !runOver;
if (wantHidden != _cursorHidden)
{
if (wantHidden) Cursor.lockState = CursorLockMode.None;
@@ -883,9 +883,19 @@ namespace ProjectM.Client
_runBannerSub = HudUi.Text("", 22, MenuUi.SubCol, TextAnchor.MiddleCenter);
_runBannerSub.style.marginTop = 8;
col.Add(_runBannerSub);
var hint = HudUi.Text("Esc - menu", 15, MenuUi.SubCol, TextAnchor.MiddleCenter);
hint.style.marginTop = 24;
col.Add(hint);
// END-2 (SL-5): the terminal banner offers a clear action so the player isn't hunting for Esc.
// PLAY AGAIN restarts a fresh Single run via the proven menu lifecycle; QUIT TO MENU reuses the teardown.
// Both self-guard on WorldLauncher.Busy. The row picks (Position) even though the banner root Ignores.
var btnRow = new VisualElement();
btnRow.style.flexDirection = FlexDirection.Row;
btnRow.style.justifyContent = Justify.Center;
btnRow.style.marginTop = 28;
btnRow.pickingMode = PickingMode.Position;
var again = MenuUi.Button("PLAY AGAIN", () => WorldLauncher.StartSession(SessionMode.Single, null, false));
again.style.marginRight = 12;
btnRow.Add(again);
btnRow.Add(MenuUi.Button("QUIT TO MENU", WorldLauncher.TeardownToMenu));
col.Add(btnRow);
_runBanner.Add(col);
_runBanner.style.display = DisplayStyle.None;
root.Add(_runBanner);