Front end: LocalConsole marshal + single-binary loop (timed stop, relaunch)
The in-process console that replaces the operator for solo play: it owns
the mission clock and ends the race at the chosen length, then relaunches
to the setup menu -- the arcade launcher behavior in one binary.
- engine/APPMGR: gPerFrameHook -- a per-frame observer called on the game
thread in RunMissions (the marshal's engine-safe tick site).
- btl4console.{hpp,cpp}: BTLocalConsole_Install/MissionCompleted -- the
marshal watches for RunningMission, starts the clock, and dispatches
Application::StopMissionMessage at expiry (BT_MISSION_SECONDS overrides
for testing). Confirmed: StopMission cleanly ends the mission and
RunMissions returns.
- btl4fe: expose BTFrontEnd_LastMissionSeconds; Enter=LAUNCH / Esc=quit in
the menu loop; BT_FE_AUTOLAUNCH quick-launch (skips the menu).
- btl4main WinMain: front-end mode runs menu -> arms marshal -> mission;
when the marshal ends it, RELAUNCH a fresh instance (arcade launcher
model) -> lands back on the menu. This sidesteps BT's in-process
re-init fragility: a second mission in-process AV'd on stale gBT*
per-mission entity globals (gBTTerrainEntity et al., cdb-traced to
MakeEntityRenderables); a fresh process has none.
Verified: menu LAUNCH -> mission runs -> marshal stops it at the set
length -> RunMissions returns -> relaunch (3 distinct PIDs across a
multi-cycle run, no crash).
Remaining in Phase 5: the results screen (kills/deaths at stop).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,9 @@
|
||||
#include "btl4fe.hpp"
|
||||
#include "l4app.hpp" // L4Application::SetEggNotationFileName
|
||||
|
||||
// Mission length (seconds) chosen on the last LAUNCH; WinMain arms the marshal.
|
||||
static int gLastMissionSeconds = 300;
|
||||
|
||||
//---------------------------------------------------------------------------//
|
||||
// Catalogs -- the BattleTech content the console's RPConfig.xml named.
|
||||
// Maps: the 8 in BTL4.RES. Mechs: the base 8 + the known variants.
|
||||
@@ -536,6 +539,17 @@ LRESULT CALLBACK MenuWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
|
||||
//---------------------------------------------------------------------------//
|
||||
int BTFrontEnd_Run()
|
||||
{
|
||||
// BT_FE_AUTOLAUNCH: skip the interactive menu and launch the default
|
||||
// loadout straight away (quick-launch / deterministic marshal testing).
|
||||
if (getenv("BT_FE_AUTOLAUNCH") != NULL) {
|
||||
BTFeMission mission; BTFeMission_Default(&mission);
|
||||
const char *egg = "frontend.egg";
|
||||
if (!BTFeMission_WriteEgg(&mission, egg)) return 0;
|
||||
L4Application::SetEggNotationFileName(egg);
|
||||
gLastMissionSeconds = mission.lengthSeconds;
|
||||
return 1;
|
||||
}
|
||||
|
||||
HINSTANCE instance = GetModuleHandleA(NULL);
|
||||
|
||||
MenuState m; memset(&m, 0, sizeof(m));
|
||||
@@ -583,6 +597,12 @@ int BTFrontEnd_Run()
|
||||
while (!m.launched && !m.closed) {
|
||||
BOOL r = GetMessageA(&msg, NULL, 0, 0);
|
||||
if (r <= 0) { m.closed = 1; break; }
|
||||
// Enter = LAUNCH, Escape = quit -- intercepted at the loop level so
|
||||
// they fire whichever child (menu or name box) has focus.
|
||||
if (msg.message == WM_KEYDOWN) {
|
||||
if (msg.wParam == VK_RETURN) { m.launched = 1; continue; }
|
||||
if (msg.wParam == VK_ESCAPE) { m.closed = 1; continue; }
|
||||
}
|
||||
if (msg.message == WM_QUIT) m.closed = 1;
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageA(&msg);
|
||||
@@ -620,5 +640,11 @@ int BTFrontEnd_Run()
|
||||
if (!BTFeMission_WriteEgg(&mission, egg_path))
|
||||
return 0;
|
||||
L4Application::SetEggNotationFileName(egg_path);
|
||||
gLastMissionSeconds = mission.lengthSeconds;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int BTFrontEnd_LastMissionSeconds()
|
||||
{
|
||||
return gLastMissionSeconds;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user