diff --git a/game/btl4main.cpp b/game/btl4main.cpp index a83f599..19409d3 100644 --- a/game/btl4main.cpp +++ b/game/btl4main.cpp @@ -97,6 +97,11 @@ struct BTDriveInput { float throttle; float turn; int forced; int fire; int fire int keyFwd; int keyBack; int keyLeft; int keyRight; int allStop; }; BTDriveInput gBTDrive = { 0.0f, 0.0f, 0, 0, 0, 1.0f, 0, 0, 0, 0, 0 }; +// The human clicked X (WM_CLOSE). Read by BTFE_RelaunchSelfAndExit: a +// user-closed window exits for REAL instead of relaunching into the join +// wait (the pod's authentic immortality, wrong for a desktop window). +volatile int gBTUserRequestedExit = 0; + LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) @@ -157,6 +162,12 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) } return 0; case WM_CLOSE: + // The human clicked X / pressed Alt-F4. Without this stamp the + // relaunch chain treats the resulting RunMissions return like a + // round ending and RESURRECTS the client (the pod was immortal by + // design; a desktop window must not be) -- see the gate in + // BTFE_RelaunchSelfAndExit. + gBTUserRequestedExit = 1; DestroyWindow(hWnd); return 0; case WM_DESTROY: diff --git a/game/glass/btl4console.cpp b/game/glass/btl4console.cpp index 6dff2a3..9c2ed68 100644 --- a/game/glass/btl4console.cpp +++ b/game/glass/btl4console.cpp @@ -137,6 +137,28 @@ static unsigned long void BTFE_RelaunchSelfAndExit(const char *arguments) { + // + // THE X BUTTON MEANS QUIT (2026-07-27, operator report: "my local + // instance auto-relaunches whenever I stop and start a session, even if + // I close my window"). The 1995 pod was IMMORTAL by design -- an arcade + // cabinet's join loop never exits -- so every RunMissions return + // relaunches, and a human closing the window was indistinguishable from + // a round ending. On a desktop that made the client unkillable outside + // Task Manager (the orphaned-plasma zombies). btl4main's WM_CLOSE now + // stamps gBTUserRequestedExit; every relaunch path funnels through here, + // so this single gate turns a user-closed window into a REAL exit while + // round-end / console-loss / abort relaunches stay exactly as they were. + // + { + extern volatile int gBTUserRequestedExit; + if (gBTUserRequestedExit) + { + MarshalLog("window closed by the user -- exiting for real " + "(no relaunch)"); + ExitProcess(0); + } + } + // // RELAUNCH-STORM DAMPER (issue #33, night-2 capture): a client whose // generations die young (stale identity -> HELLO reject -> drop ->