//===========================================================================// // btl4console.cpp -- BT412 in-process LocalConsole marshal (solo path). // // See btl4console.hpp. Models RP412's RPL4CONSOLE ConsoleTick, reduced to the // single-player case: no remote pods, no wire -- just own the mission clock and // dispatch StopMissionMessage at expiry. The tick runs on the game thread // (gPerFrameHook), so the engine call (Dispatch) is safe. //===========================================================================// #include // Application, application, DEBUG_STREAM #pragma hdrstop #include "appmsg.hpp" // full Application::StopMissionMessage definition #include "appmgr.hpp" // gPerFrameHook #include "btl4console.hpp" namespace { enum Phase { PhaseIdle, PhaseWaiting, PhaseRunning, PhaseStopped }; Phase gPhase = PhaseIdle; int gMissionSeconds = 0; unsigned long gStartTick = 0; Application *gWatched = 0; // // The game-thread tick (called every frame while an application is live). // void ConsoleTick() { if (application == 0) return; int state = application->GetApplicationState(); switch (gPhase) { case PhaseWaiting: // The mission has staged and started running -> start the clock. if (state == Application::RunningMission) { gPhase = PhaseRunning; gWatched = application; gStartTick = GetTickCount(); DEBUG_STREAM << "[marshal] mission running -- length " << gMissionSeconds << "s\n" << std::flush; } break; case PhaseRunning: if (application != gWatched) return; // only marshal our own mission if (state != Application::RunningMission) { // ended some other way (pilot abort, teardown) gPhase = PhaseStopped; DEBUG_STREAM << "[marshal] mission ended (state change)\n" << std::flush; } else if (gMissionSeconds > 0 && (GetTickCount() - gStartTick) >= (unsigned long)(gMissionSeconds * 1000)) { // The console clock expired: stop the race exactly as the // arcade console did (StopMissionMessage on our own pod). DEBUG_STREAM << "[marshal] time expired -- stopping mission\n" << std::flush; Application::StopMissionMessage message(0); application->Dispatch(&message); gPhase = PhaseStopped; } break; default: break; } } } void BTLocalConsole_Install(int mission_seconds) { gMissionSeconds = mission_seconds; gPhase = PhaseWaiting; gStartTick = 0; gWatched = 0; // Marshaled: the in-process console owns the clock, so a console-loss // (there is none locally) would end the race -- harmless for solo. gConsoleLossEndsMission = True; gPerFrameHook = &ConsoleTick; DEBUG_STREAM << "[marshal] installed (length " << mission_seconds << "s)\n" << std::flush; } int BTLocalConsole_MissionCompleted() { return (gPhase == PhaseStopped) ? 1 : 0; } void BTLocalConsole_Uninstall() { gPerFrameHook = 0; gPhase = PhaseIdle; }