//===========================================================================// // File: btl4pb.cpp // // Project: BattleTech Brick: BattleTech LBE Application // // Contents: BTL4PlaybackApplication -- the mission-review application, and // // BTSpoolerTask, the pump that replays recorded NetworkPackets // //---------------------------------------------------------------------------// // Date Who Modification // // -------- --- ---------------------------------------------------------- // // // //---------------------------------------------------------------------------// // Copyright (C) 1995, Virtual World Entertainment, Inc. // // All Rights reserved worldwide // // This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL // //===========================================================================// // // RECONSTRUCTED against the AUTHENTIC surviving header (BTL4PB.HPP) from the // shipped binary's 0x4d3b74-0x4d40d8 cluster, cross-checked against the // surviving RP411 descendant RPL4PB.cpp (a line-for-line structural twin). // The binary's own base-ctor call (FUN_004d34c4(this, resource, 0x3eb, // DefaultData)) confirms the DOS ctor shape this file uses -- no windowing // parameters ever existed in 4.10. // #include #pragma hdrstop #if !defined(BTL4PB_HPP) # include #endif #if !defined(MISSION_HPP) # include #endif #if !defined(L4NET_HPP) # include #endif #if !defined(UPDATE_HPP) # include #endif #if !defined(RENDERER_HPP) # include #endif #if !defined(ICOM_HPP) # include #endif #if !defined(DROPZONE_HPP) # include #endif #if !defined(DIRECTOR_HPP) # include #endif #if !defined(NTTMGR_HPP) # include #endif #if !defined(MECH_HPP) # include #endif #if !defined(L4VIDEO_HPP) # include #endif #if !defined(L4CTRL_HPP) # include #endif //############################################################################# //########################## BTSpoolerTask ############################## //############################################################################# // // @004d3b74 / @004d3bb8 -- chain-only construction; the task adds no data. // BTSpoolerTask::BTSpoolerTask() { } BTSpoolerTask::~BTSpoolerTask() { Check_Fpu(); } // //############################################################################# // @004d3b90 / @004d3ba8 -- the pump's two callbacks forward to the running // playback application. //############################################################################# // void BTSpoolerTask::DispatchPacket(NetworkPacket *packet) { BTL4PlaybackApplication *playback_app = Cast_Object(BTL4PlaybackApplication *, application); playback_app->DispatchPacket(packet); Check_Fpu(); } long BTSpoolerTask::GetTimeBias() { BTL4PlaybackApplication *playback_app = Cast_Object(BTL4PlaybackApplication *, application); return playback_app->GetTimeBias(); } //############################################################################# //####################### BTL4PlaybackApplication ####################### //############################################################################# const Receiver::HandlerEntry BTL4PlaybackApplication::MessageHandlerEntries[] = { MESSAGE_ENTRY(BTL4PlaybackApplication, RunMission), // @004d3ef8 MESSAGE_ENTRY(BTL4PlaybackApplication, StopMission) // @004d3f44 }; BTL4PlaybackApplication::MessageHandlerSet BTL4PlaybackApplication::MessageHandlers( ELEMENTS(BTL4PlaybackApplication::MessageHandlerEntries), BTL4PlaybackApplication::MessageHandlerEntries, BTL4Application::MessageHandlers ); Derivation BTL4PlaybackApplication::ClassDerivations( BTL4Application::ClassDerivations, "BTL4PlaybackApplication" // @0051f182 ); BTL4PlaybackApplication::SharedData BTL4PlaybackApplication::DefaultData( // @0051ee5c BTL4PlaybackApplication::ClassDerivations, BTL4PlaybackApplication::MessageHandlers ); // //############################################################################# // @004d3be4 -- the binary's base call is (resource, 0x3eb, DefaultData): the // L4ApplicationClassID, no windowing. The spool lands in the inherited // spoolFile slot. //############################################################################# // BTL4PlaybackApplication::BTL4PlaybackApplication( ResourceFile *resource_file, SpoolFile *spool_file ): BTL4Application(resource_file, L4ApplicationClassID, DefaultData) { spoolFile = spool_file; timeBias = 0; Check_Fpu(); } BTL4PlaybackApplication::~BTL4PlaybackApplication() { Check_Fpu(); } Logical BTL4PlaybackApplication::TestInstance() const { return IsDerivedFrom(ClassDerivations); } // //############################################################################# // @004d3c14 -- playback drives the spool-fed network manager, not a live one. //############################################################################# // NetworkManager * BTL4PlaybackApplication::MakeNetworkManager() { Check_Fpu(); return new L4PlaybackNetworkManager; } // //############################################################################# // @004d3c34 //############################################################################# // void BTL4PlaybackApplication::Initialize() { Check(this); L4Application::InitializeTillConsole(); } // //############################################################################# // @004d3c44 -- the playback task pipeline: our own spool pump first, then the // stock event / gauge / cycle / audio tasks. //############################################################################# // void BTL4PlaybackApplication::LoadBackgroundTasks() { Check(this); ApplicationTask *application_task; application_task = new BTSpoolerTask; Register_Object(application_task); backgroundTasks->AddTask(application_task); application_task = new ProcessEventTask; Register_Object(application_task); backgroundTasks->AddTask(application_task); application_task = new GaugeRendererTask; Register_Object(application_task); backgroundTasks->AddTask(application_task); application_task = new CompleteCyclesTask; Register_Object(application_task); backgroundTasks->AddTask(application_task); application_task = new AudioRendererTask; Register_Object(application_task); backgroundTasks->AddTask(application_task); Check_Fpu(); } // //############################################################################# // @004d3d40 -- the mission-review frame loop. Idle until frames execute; // controls only in INTERACTIVE review (mode 2); then the update, interest, // renderer and intercom managers; and while running, keep the game clock // current from the recorded start. //############################################################################# // Logical BTL4PlaybackApplication::ExecuteForeground( Time start_of_frame, Scalar frame_duration ) { SET_FOREGROUND_PROCESSING(); Check(this); if (!executeFrames || GetApplicationState() == WaitingForEgg) { // // [T3] The binary services a renderer idle slot here (the RP411 // descendant names it ExecuteIdle); no such entry exists in the // 1995 renderer headers, and pre-egg there is nothing to draw -- // the idle frame simply spins. Revisit if the review screen shows // stale video before the egg arrives. // CLEAR_FOREGROUND_PROCESSING(); return executeFrames && !Exit_Code; } Time frame_ticks; frame_ticks = frame_duration; if (GetMissionReviewMode() == 2) { Check(controlsManager); ((LBE4ControlsManager *)controlsManager)->Execute(); } SET_UPDATE_MANAGER(); Check(updateManager); updateManager->Execute(start_of_frame); CLEAR_UPDATE_MANAGER(); Check(rendererManager); rendererManager->UpdateInterestOrigins(start_of_frame); Check(interestManager); interestManager->Execute(); SET_RENDERER_MANAGER(); Check(rendererManager); rendererManager->Execute(start_of_frame, frame_ticks, frame_ticks); CLEAR_RENDERER_MANAGER(); Check(intercomManager); intercomManager->Execute(); if (GetApplicationState() == RunningMission) { secondsRemainingInGame = currentMission->GetGameLength() - (Now() - gameStarted); } CLEAR_FOREGROUND_PROCESSING(); Check_Fpu(); return executeFrames && !Exit_Code; } // //############################################################################# // Inherited-shape background task (the OPT build emitted no BT override; the // body matches the L4 base, kept for the header's declaration). //############################################################################# // void BTL4PlaybackApplication::ExecuteBackgroundTask() { Check(this); if (ProcessOneEvent(HighEventPriority)) { return; } Check(backgroundTasks); backgroundTasks->Execute(); } // //############################################################################# // @004d3ecc -- route a recorded packet to its client. //############################################################################# // void BTL4PlaybackApplication::DispatchPacket(NetworkPacket *packet) { Check(this); NetworkManager *net_mgr = GetNetworkManager(); Check(net_mgr); NetworkClient *client = net_mgr->GetNetworkClientPointer(packet->clientID); Check(client); client->ReceiveNetworkPacket(packet, &packet->messageData); Check_Fpu(); } // //############################################################################# // @004d3ef8 -- first launch synchronises the playback clock: the bias is the // difference between NOW and the recorded RunMission packet's timestamp, and // every replayed packet is re-timed by it. //############################################################################# // void BTL4PlaybackApplication::RunMissionMessageHandler(RunMissionMessage *message) { Check(this); if (GetApplicationState() == WaitingForLaunch) { SpoolFile *spool = GetSpoolFile(); Check(spool); NetworkPacket *packet = (NetworkPacket *)spool->GetPointer(); timeBias = Now().ticks - packet->timeStamp.ticks; spool->NextPacket(); } BTL4Application::RunMissionMessageHandler(message); Check_Fpu(); } // //############################################################################# // @004d3f44 -- the WINNERS CIRCLE. When the recorded mission ends, release // the spool, park every player's mech on the dropzone matching its finishing // rank ("win1".."winN"), freeze them, refresh the name bitmaps and tilt the // camera up 45 degrees for the final tableau. //############################################################################# // static char winners_spot[] = "win?"; // @0051ee63 void BTL4PlaybackApplication::StopMissionMessageHandler(StopMissionMessage *message) { MissionReviewApplicationManager *app_mgr = GetApplicationManager(); Check(app_mgr); SpoolFile *spool = GetSpoolFile(); if (spool) { app_mgr->ReleaseSpoolFile(spool); spoolFile = NULL; EntityManager *entity_manager = application->GetEntityManager(); EntityGroup *dropzones = entity_manager->FindGroup("DropZones"); char *place = winners_spot + 3; Player *player; int rank; for ( rank = 0; (player = CameraDirector::FindPlayerByRank(rank)) != NULL; ++rank ) { *place = (char)('1' + rank); ChainIteratorOf iterator(dropzones->groupMembers); DropZone *dropzone; while ((dropzone = (DropZone *)iterator.ReadAndNext()) != NULL) { if (!strcmp(dropzone->GetDropZoneName(), winners_spot)) { break; } } if (!dropzone) { continue; } Mech *mech = (Mech *)player->GetPlayerVehicle(); if (mech->GetClassID() == RegisteredClass::MechClassID) { mech->Reset(dropzone->localOrigin, False); mech->SetPerformance( (Mech::Performance)&Simulation::DoNothing); mech->FlushEvents(); } } DPLRenderer *dpl_renderer = (DPLRenderer *)GetVideoRenderer(); Check(dpl_renderer); dpl_renderer->SortAndReloadNameBitmaps(); dpl_renderer->SetViewAngle(Degree(45.0f)); } BTL4Application::StopMissionMessageHandler(message); Check_Fpu(); }